diff --git a/.editorconfig b/.editorconfig index c3fb970c3b..fa43757a9f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,7 +6,7 @@ indent_style = space indent_size = 4 csharp_style_var_for_built_in_types = false:warning csharp_style_var_elsewhere = false:warning -csharp_style_var_when_type_is_apparent = false:warning +csharp_style_var_when_type_is_apparent = true:warning end_of_line = crlf dotnet_sort_system_directives_first = true dotnet_style_predefined_type_for_locals_parameters_members = true:warning diff --git a/ImageSharp.ruleset b/ImageSharp.ruleset index 3f10206fca..0bd9cd11ab 100644 --- a/ImageSharp.ruleset +++ b/ImageSharp.ruleset @@ -4,7 +4,7 @@ - + diff --git a/ImageSharp.sln b/ImageSharp.sln index 2802e7ba8a..468bbbf862 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -1,24 +1,21 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.9 +VisualStudioVersion = 15.0.26403.7 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .travis.yml = .travis.yml appveyor.yml = appveyor.yml + codecov.yml = codecov.yml CodeCoverage.runsettings = CodeCoverage.runsettings - contributing.md = contributing.md - dotnet-latest.ps1 = dotnet-latest.ps1 + .github\CONTRIBUTING.md = .github\CONTRIBUTING.md features.md = features.md - global.json = global.json ImageSharp.ruleset = ImageSharp.ruleset ImageSharp.sln.DotSettings = ImageSharp.sln.DotSettings NuGet.config = NuGet.config README.md = README.md - Rebracer.xml = Rebracer.xml - Settings.StyleCop = Settings.StyleCop EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{815C0625-CD3D-440F-9F80-2D83856AB7AE}" diff --git a/README.md b/README.md index 6ba113e922..ca24275469 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,13 @@ -# ImageSharp +# ImageSharp ImageSharp -**ImageSharp** is a new cross-platform 2D graphics API designed to allow the processing of images without the use of `System.Drawing`. +**ImageSharp** is a new, fully featured, fully managed, cross-platform, 2D graphics API designed to allow the processing of images without the use of `System.Drawing`. -> **ImageSharp is still in early stages (alpha) but progress has been pretty quick. As such, please do not use on production environments until the library reaches release candidate status. Pre-release downloads are available from the [MyGet package repository](https://www.myget.org/gallery/imagesharp).** +Built against .Net Standard 1.1 ImageSharp can be used in device, cloud, and embedded/IoT scenarios. + +> **ImageSharp** has made excellent progress and contains many great features but is still considered by us to be still in early stages (alpha). As such, we cannot support its use on production environments until the library reaches release candidate status. +> +> Pre-release downloads are available from the [MyGet package repository](https://www.myget.org/gallery/imagesharp). [![GitHub license](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://raw.githubusercontent.com/JimBobSquarePants/ImageSharp/master/APACHE-2.0-LICENSE.txt) [![GitHub issues](https://img.shields.io/github/issues/JimBobSquarePants/ImageSharp.svg)](https://github.com/JimBobSquarePants/ImageSharp/issues) @@ -35,8 +39,8 @@ The **ImageSharp** library is made up of multiple packages. Packages include: - **ImageSharp** - - Contains the Image classes, Colors, Primitives, Configuration, and other core functionality. - - The IImageFormat interface, Jpeg, Png, Bmp, and Gif formats. + - Contains the generic `Image` class, PixelFormats, Primitives, Configuration, and other core functionality. + - The `IImageFormat` interface, Jpeg, Png, Bmp, and Gif formats. - Transform methods like Resize, Crop, Skew, Rotate - Anything that alters the dimensions of the image. - Non-transform methods like Gaussian Blur, Pixelate, Edge Detection - Anything that maintains the original image dimensions. @@ -69,15 +73,20 @@ There's plenty there and more coming. Check out the [current features](features. ### API -Without the constraints of `System.Drawing` We have been able to develop something much more flexible, easier to code against, and much, much less prone to memory leaks. Gone are system-wide process-locks. Images and processors are thread safe usable in parallel processing utilizing all the availables cores. +Without the constraints of `System.Drawing` We have been able to develop something much more flexible, easier to code against, and much, much less prone to memory leaks. + +Gone are system-wide process-locks; ImageSharp images are thread-safe and fully supported in web environments. -Many `Image` methods are also fluent. +Many `Image` methods are also fluent. Here's an example of the code required to resize an image using the default Bicubic resampler then turn the colors into their grayscale equivalent using the BT709 standard matrix. +`Rgba32` is our default PixelFormat, equivalent to `System.Drawing Color`. + On platforms supporting netstandard 1.3+ ```csharp -using (Image image = Image.Load("foo.jpg")) +// Image.Load(string path) is a shortcut for our default type. Other pixel formats use Image.Load(string path)) +using (Image image = Image.Load("foo.jpg")) { image.Resize(image.Width / 2, image.Height / 2) .Grayscale() @@ -86,9 +95,10 @@ using (Image image = Image.Load("foo.jpg")) ``` on netstandard 1.1 - 1.2 ```csharp +// Image.Load(Stream stream) is a shortcut for our default type. Other pixel formats use Image.Load(Stream stream)) using (FileStream stream = File.OpenRead("foo.jpg")) using (FileStream output = File.OpenWrite("bar.jpg")) -using (Image image = Image.Load(stream)) +using (Image image = Image.Load(stream)) { image.Resize(image.Width / 2, image.Height / 2) .Grayscale() @@ -99,14 +109,14 @@ using (Image image = Image.Load(stream)) Setting individual pixel values is perfomed as follows: ```csharp -using (image = new Image(400, 400) -using (var pixels = image.Lock()) +using (Image image = new Image(400, 400) +using (PixelAccessor pixels = image.Lock()) { - pixels[200, 200] = Color.White; + pixels[200, 200] = Rgba32.White; } ``` -For advanced usage the `Image` and `PixelAccessor` classes are available allowing developers to implement their own color models in the same manner as Microsoft XNA Game Studio and MonoGame. +For advanced usage there are multiple [PixelFormat implementations](https://github.com/JimBobSquarePants/ImageSharp/tree/master/src/ImageSharp/PixelFormats) available allowing developers to implement their own color models in the same manner as Microsoft XNA Game Studio and MonoGame. All in all this should allow image processing to be much more accessible to developers which has always been my goal from the start. @@ -114,7 +124,7 @@ All in all this should allow image processing to be much more accessible to deve Please... Spread the word, contribute algorithms, submit performance improvements, unit tests. -Performance is a biggie, if you know anything about the new vector types and can apply some fancy new stuff with that it would be awesome. +Performance is a biggie, if you know anything about the `System.Numerics.Vectors` types and can apply some fancy new stuff with that it would be awesome. There's a lot of developers out there who could write this stuff a lot better and faster than I and I would love to see what we collectively can come up with so please, if you can help in any way it would be most welcome and benificial for all. @@ -198,4 +208,4 @@ Become a sponsor and get your logo on our README on Github with a link to your s - + \ No newline at end of file diff --git a/Rebracer.xml b/Rebracer.xml deleted file mode 100644 index 9b0a654d21..0000000000 --- a/Rebracer.xml +++ /dev/null @@ -1,249 +0,0 @@ - - - - - - - - - - - - - HACK:2 - TODO:2 - UNDONE:2 - UnresolvedMergeConflict:3 - - false - false - false - - - - - 0 - 0 - 1 - 1 - -1 - -1 - 0 - 1 - 0 - 1 - 1 - 1 - 1 - 1 - 0 - 1 - 1 - 0 - 2 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 0 - 1 - 0 - 1 - 0 - 0 - 0 - 1 - 1 - 1 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - 1 - 0 - 1 - 0 - 0 - 0 - 1 - 1 - - - false - true - true - true - true - Implicit (Windows)|$(VSInstallDir)\JavaScript\References\libhelp.js|$(VSInstallDir)\JavaScript\References\domWindows.js|$(VSInstallDir)\JavaScript\References\underscorefilter.js|$(VSInstallDir)\JavaScript\References\showPlainComments.js;Implicit (Windows 8.1)|$(VSInstallDir)\JavaScript\References\libhelp.js|$(VSInstallDir)\JavaScript\References\sitetypesWindows.js|$(VSInstallDir)\JavaScript\References\domWindows_8.1.js|$(VSInstallDir)\JavaScript\References\underscorefilter.js|$(VSInstallDir)\JavaScript\References\showPlainComments.js;Implicit (Windows Phone 8.1)|$(VSInstallDir)\JavaScript\References\libhelp.js|$(VSInstallDir)\JavaScript\References\sitetypesWindows.js|$(VSInstallDir)\JavaScript\References\domWindowsPhone_8.1.js|$(VSInstallDir)\JavaScript\References\underscorefilter.js|$(VSInstallDir)\JavaScript\References\showPlainComments.js;Implicit (Web)|$(VSInstallDir)\JavaScript\References\libhelp.js|$(VSInstallDir)\JavaScript\References\sitetypesWeb.js|$(VSInstallDir)\JavaScript\References\domWeb.js|$(VSInstallDir)\JavaScript\References\underscorefilter.js|$(VSInstallDir)\JavaScript\References\showPlainComments.js|C:\Users\james.south\AppData\Local\Web Essentials 2015\Modern.Intellisense.js;Dedicated Worker|$(VSInstallDir)\JavaScript\References\libhelp.js|$(VSInstallDir)\JavaScript\References\dedicatedworker.js|$(VSInstallDir)\JavaScript\References\underscorefilter.js|$(VSInstallDir)\JavaScript\References\showPlainComments.js;Generic|$(VSInstallDir)\JavaScript\References\libhelp.js|$(VSInstallDir)\JavaScript\References\underscorefilter.js|$(VSInstallDir)\JavaScript\References\showPlainComments.js; - true - true - true - false - true - true - false - false - true - true - - - true - false - false - true - true - true - 1 - true - false - true - true - false - false - false - false - false - false - false - true - true - false - true - true - true - true - false - true - false - false - true - false - 1 - true - 2 - 2 - false - false - 0 - true - true - 0 - 0 - true - true - false - 0 - 0 - false - 0 - 1 - false - true - false - 2 - true - false - false - false - false - true - true - false - false - false - false - true - true - 2 - 2 - 2 - true - false - false - true - true - false - false - 1 - true - false - false - false - false - false - false - false - false - false - false - false - true - false - false - true - true - - - false - false - true - false - true - true - true - true - true - true - true - false - true - true - false - false - true - true - false - false - false - false - false - false - false - false - - - false - true - true - true - false - true - true - true - - - - \ No newline at end of file diff --git a/Settings.StyleCop b/Settings.StyleCop deleted file mode 100644 index 2716e8d0ac..0000000000 --- a/Settings.StyleCop +++ /dev/null @@ -1,56 +0,0 @@ - - - - enum - exif - uint - lossy - octree - png - quantizer - unzig - cb - cr - Laplacian - Sobel - Scharr - rgb - rgba - rrggbb - rrggbbaa - scanline - scanlines - png's - codeword - unscaled - zig-zag - crc - zlib - xff - xda - ss - Vol - pp - cmyk - Paeth - th - bool - bools - desensitivity - premultiplied - endianness - thresholding - - - - - - James Jackson-South - - Copyright © James Jackson-South and contributors. - Licensed under the Apache License, Version 2.0. - - - - - \ No newline at end of file diff --git a/features.md b/features.md index 6bc5630eed..1e35b88e0d 100644 --- a/features.md +++ b/features.md @@ -13,6 +13,7 @@ We've achieved a lot so far and hope to do a lot more in the future. We're alway - [ ] Tiff (Help needed) - **Metadata** - [x] EXIF Read/Write (Jpeg just now) + - [ ] ICC (In Progress) - **Quantizers (IQuantizer with alpha channel support, dithering, and thresholding)** - [x] Octree - [x] Xiaolin Wu @@ -28,7 +29,6 @@ We've achieved a lot so far and hope to do a lot more in the future. We're alway - [x] Bayer - [x] Ordered - **Basic color structs with implicit operators.** - - [x] Color - 32bit color in RGBA order (IPackedPixel\). - [x] Bgra32 - [x] CIE Lab - [x] CIE XYZ @@ -38,7 +38,7 @@ We've achieved a lot so far and hope to do a lot more in the future. We're alway - [x] YCbCr - **IPackedPixel representations of color models. Compatible with Microsoft XNA Game Studio and MonoGame IPackedVector\.** - [x] Alpha8 - - [x] Argb + - [x] Argb32 - [x] Bgr565 - [x] Bgra444 - [x] Bgra565 @@ -52,7 +52,9 @@ We've achieved a lot so far and hope to do a lot more in the future. We're alway - [x] NormalizedShort4 - [x] Rg32 - [x] Rgba1010102 + - [x] Rgba32 - 32bit color in RGBA order - Our default pixel format. - [x] Rgba64 + - [x] RgbaVector - [x] Short2 - [x] Short4 - **Basic shape primitives.** diff --git a/src/ImageSharp.Drawing/Brushes/Brushes.cs b/src/ImageSharp.Drawing/Brushes/Brushes.cs index e8269848ce..e39f3dd497 100644 --- a/src/ImageSharp.Drawing/Brushes/Brushes.cs +++ b/src/ImageSharp.Drawing/Brushes/Brushes.cs @@ -5,150 +5,254 @@ namespace ImageSharp.Drawing.Brushes { + using ImageSharp.PixelFormats; + /// - /// A collection of methods for creating brushes. Brushes use for painting. + /// A collection of methods for creating generic brushes. /// - public class Brushes + /// A New + public static class Brushes { + /// + /// Percent10 Hatch Pattern + /// + /// ---> x axis + /// ^ + /// | y - axis + /// | + /// see PatternBrush for details about how to make new patterns work + private static readonly bool[,] Percent10Pattern = + { + { true, false, false, false }, + { false, false, false, false }, + { false, false, true, false }, + { false, false, false, false } + }; + + /// + /// Percent20 pattern. + /// + private static readonly bool[,] Percent20Pattern = + { + { true, false, false, false }, + { false, false, true, false }, + { true, false, false, false }, + { false, false, true, false } + }; + + /// + /// Horizontal Hatch Pattern + /// + private static readonly bool[,] HorizontalPattern = + { + { false }, + { true }, + { false }, + { false } + }; + + /// + /// Min Pattern + /// + private static readonly bool[,] MinPattern = + { + { false }, + { false }, + { false }, + { true } + }; + + /// + /// Vertical Pattern + /// + private static readonly bool[,] VerticalPattern = + { + { false, true, false, false }, + }; + + /// + /// Forward Diagonal Pattern + /// + private static readonly bool[,] ForwardDiagonalPattern = + { + { false, false, false, true }, + { false, false, true, false }, + { false, true, false, false }, + { true, false, false, false } + }; + + /// + /// Backward Diagonal Pattern + /// + private static readonly bool[,] BackwardDiagonalPattern = + { + { true, false, false, false }, + { false, true, false, false }, + { false, false, true, false }, + { false, false, false, true } + }; + /// /// Create as brush that will paint a solid color /// /// The color. - /// A Brush - public static SolidBrush Solid(Color color) - => new SolidBrush(color); + /// The pixel format. + /// A New + public static SolidBrush Solid(TPixel color) + where TPixel : struct, IPixel + => new SolidBrush(color); /// - /// Create as brush that will paint a Percent10 Hatch Pattern with - /// in the specified foreground color and a transparent background + /// Create as brush that will paint a Percent10 Hatch Pattern with the specified colors /// /// Color of the foreground. - /// A Brush - public static PatternBrush Percent10(Color foreColor) - => new PatternBrush(Brushes.Percent10(foreColor, Color.Transparent)); + /// The pixel format. + /// A New + public static PatternBrush Percent10(TPixel foreColor) + where TPixel : struct, IPixel + => new PatternBrush(foreColor, NamedColors.Transparent, Percent10Pattern); /// - /// Create as brush that will paint a Percent10 Hatch Pattern with - /// in the specified foreground and background colors + /// Create as brush that will paint a Percent10 Hatch Pattern with the specified colors /// /// Color of the foreground. /// Color of the background. - /// A Brush - public static PatternBrush Percent10(Color foreColor, Color backColor) - => new PatternBrush(Brushes.Percent10(foreColor, backColor)); + /// The pixel format. + /// A New + public static PatternBrush Percent10(TPixel foreColor, TPixel backColor) + where TPixel : struct, IPixel + => new PatternBrush(foreColor, backColor, Percent10Pattern); /// - /// Create as brush that will paint a Percent20 Hatch Pattern with - /// in the specified foreground color and a transparent background + /// Create as brush that will paint a Percent20 Hatch Pattern with the specified foreground color and a + /// transparent background. /// /// Color of the foreground. - /// A Brush - public static PatternBrush Percent20(Color foreColor) - => new PatternBrush(Brushes.Percent20(foreColor, Color.Transparent)); + /// The pixel format. + /// A New + public static PatternBrush Percent20(TPixel foreColor) + where TPixel : struct, IPixel + => new PatternBrush(foreColor, NamedColors.Transparent, Percent20Pattern); /// - /// Create as brush that will paint a Percent20 Hatch Pattern with - /// in the specified foreground and background colors + /// Create as brush that will paint a Percent20 Hatch Pattern with the specified colors /// /// Color of the foreground. /// Color of the background. - /// A Brush - public static PatternBrush Percent20(Color foreColor, Color backColor) - => new PatternBrush(Brushes.Percent20(foreColor, backColor)); + /// The pixel format. + /// A New + public static PatternBrush Percent20(TPixel foreColor, TPixel backColor) + where TPixel : struct, IPixel + => new PatternBrush(foreColor, backColor, Percent20Pattern); /// - /// Create as brush that will paint a Horizontal Hatch Pattern with - /// in the specified foreground color and a transparent background + /// Create as brush that will paint a Horizontal Hatch Pattern with the specified foreground color and a + /// transparent background. /// /// Color of the foreground. - /// A Brush - public static PatternBrush Horizontal(Color foreColor) - => new PatternBrush(Brushes.Horizontal(foreColor, Color.Transparent)); + /// The pixel format. + /// A New + public static PatternBrush Horizontal(TPixel foreColor) + where TPixel : struct, IPixel + => new PatternBrush(foreColor, NamedColors.Transparent, HorizontalPattern); /// - /// Create as brush that will paint a Horizontal Hatch Pattern with - /// in the specified foreground and background colors + /// Create as brush that will paint a Horizontal Hatch Pattern with the specified colors /// /// Color of the foreground. /// Color of the background. - /// A Brush - public static PatternBrush Horizontal(Color foreColor, Color backColor) - => new PatternBrush(Brushes.Horizontal(foreColor, backColor)); + /// The pixel format. + /// A New + public static PatternBrush Horizontal(TPixel foreColor, TPixel backColor) + where TPixel : struct, IPixel + => new PatternBrush(foreColor, backColor, HorizontalPattern); /// - /// Create as brush that will paint a Min Hatch Pattern with - /// in the specified foreground color and a transparent background + /// Create as brush that will paint a Min Hatch Pattern with the specified foreground color and a + /// transparent background. /// /// Color of the foreground. - /// A Brush - public static PatternBrush Min(Color foreColor) - => new PatternBrush(Brushes.Min(foreColor, Color.Transparent)); + /// The pixel format. + /// A New + public static PatternBrush Min(TPixel foreColor) + where TPixel : struct, IPixel + => new PatternBrush(foreColor, NamedColors.Transparent, MinPattern); /// - /// Create as brush that will paint a Min Hatch Pattern with - /// in the specified foreground and background colors + /// Create as brush that will paint a Min Hatch Pattern with the specified colors /// /// Color of the foreground. /// Color of the background. - /// A Brush - public static PatternBrush Min(Color foreColor, Color backColor) - => new PatternBrush(Brushes.Min(foreColor, backColor)); + /// The pixel format. + /// A New + public static PatternBrush Min(TPixel foreColor, TPixel backColor) + where TPixel : struct, IPixel + => new PatternBrush(foreColor, backColor, MinPattern); /// - /// Create as brush that will paint a Vertical Hatch Pattern with - /// in the specified foreground color and a transparent background + /// Create as brush that will paint a Vertical Hatch Pattern with the specified foreground color and a + /// transparent background. /// /// Color of the foreground. - /// A Brush - public static PatternBrush Vertical(Color foreColor) - => new PatternBrush(Brushes.Vertical(foreColor, Color.Transparent)); + /// The pixel format. + /// A New + public static PatternBrush Vertical(TPixel foreColor) + where TPixel : struct, IPixel + => new PatternBrush(foreColor, NamedColors.Transparent, VerticalPattern); /// - /// Create as brush that will paint a Vertical Hatch Pattern with - /// in the specified foreground and background colors + /// Create as brush that will paint a Vertical Hatch Pattern with the specified colors /// /// Color of the foreground. /// Color of the background. - /// A Brush - public static PatternBrush Vertical(Color foreColor, Color backColor) - => new PatternBrush(Brushes.Vertical(foreColor, backColor)); + /// The pixel format. + /// A New + public static PatternBrush Vertical(TPixel foreColor, TPixel backColor) + where TPixel : struct, IPixel + => new PatternBrush(foreColor, backColor, VerticalPattern); /// - /// Create as brush that will paint a Forward Diagonal Hatch Pattern with - /// in the specified foreground color and a transparent background + /// Create as brush that will paint a Forward Diagonal Hatch Pattern with the specified foreground color and a + /// transparent background. /// /// Color of the foreground. - /// A Brush - public static PatternBrush ForwardDiagonal(Color foreColor) - => new PatternBrush(Brushes.ForwardDiagonal(foreColor, Color.Transparent)); + /// The pixel format. + /// A New + public static PatternBrush ForwardDiagonal(TPixel foreColor) + where TPixel : struct, IPixel + => new PatternBrush(foreColor, NamedColors.Transparent, ForwardDiagonalPattern); /// - /// Create as brush that will paint a Forward Diagonal Hatch Pattern with - /// in the specified foreground and background colors + /// Create as brush that will paint a Forward Diagonal Hatch Pattern with the specified colors /// /// Color of the foreground. /// Color of the background. - /// A Brush - public static PatternBrush ForwardDiagonal(Color foreColor, Color backColor) - => new PatternBrush(Brushes.ForwardDiagonal(foreColor, backColor)); + /// The pixel format. + /// A New + public static PatternBrush ForwardDiagonal(TPixel foreColor, TPixel backColor) + where TPixel : struct, IPixel + => new PatternBrush(foreColor, backColor, ForwardDiagonalPattern); /// - /// Create as brush that will paint a Backward Diagonal Hatch Pattern with - /// in the specified foreground color and a transparent background + /// Create as brush that will paint a Backward Diagonal Hatch Pattern with the specified foreground color and a + /// transparent background. /// /// Color of the foreground. - /// A Brush - public static PatternBrush BackwardDiagonal(Color foreColor) - => new PatternBrush(Brushes.BackwardDiagonal(foreColor, Color.Transparent)); + /// The pixel format. + /// A New + public static PatternBrush BackwardDiagonal(TPixel foreColor) + where TPixel : struct, IPixel + => new PatternBrush(foreColor, NamedColors.Transparent, BackwardDiagonalPattern); /// - /// Create as brush that will paint a Backward Diagonal Hatch Pattern with - /// in the specified foreground and background colors + /// Create as brush that will paint a Backward Diagonal Hatch Pattern with the specified colors /// /// Color of the foreground. /// Color of the background. - /// A Brush - public static PatternBrush BackwardDiagonal(Color foreColor, Color backColor) - => new PatternBrush(Brushes.BackwardDiagonal(foreColor, backColor)); + /// The pixel format. + /// A New + public static PatternBrush BackwardDiagonal(TPixel foreColor, TPixel backColor) + where TPixel : struct, IPixel + => new PatternBrush(foreColor, backColor, BackwardDiagonalPattern); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Brushes/Brushes{TColor}.cs b/src/ImageSharp.Drawing/Brushes/Brushes{TColor}.cs deleted file mode 100644 index 6e092bf185..0000000000 --- a/src/ImageSharp.Drawing/Brushes/Brushes{TColor}.cs +++ /dev/null @@ -1,168 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Brushes -{ - using System; - - /// - /// A collection of methods for creating generic brushes. - /// - /// The pixel format. - /// A Brush - public class Brushes - where TColor : struct, IPixel - { - /// - /// Percent10 Hatch Pattern - /// - /// ---> x axis - /// ^ - /// | y - axis - /// | - /// see PatternBrush for details about how to make new patterns work - private static readonly bool[,] Percent10Pattern = - { - { true, false, false, false }, - { false, false, false, false }, - { false, false, true, false }, - { false, false, false, false } - }; - - /// - /// Percent20 pattern. - /// - private static readonly bool[,] Percent20Pattern = - { - { true, false, false, false }, - { false, false, true, false }, - { true, false, false, false }, - { false, false, true, false } - }; - - /// - /// Horizontal Hatch Pattern - /// - private static readonly bool[,] HorizontalPattern = - { - { false }, - { true }, - { false }, - { false } - }; - - /// - /// Min Pattern - /// - private static readonly bool[,] MinPattern = - { - { false }, - { false }, - { false }, - { true } - }; - - /// - /// Vertical Pattern - /// - private static readonly bool[,] VerticalPattern = - { - { false, true, false, false }, - }; - - /// - /// Forward Diagonal Pattern - /// - private static readonly bool[,] ForwardDiagonalPattern = - { - { false, false, false, true }, - { false, false, true, false }, - { false, true, false, false }, - { true, false, false, false } - }; - - /// - /// Backward Diagonal Pattern - /// - private static readonly bool[,] BackwardDiagonalPattern = - { - { true, false, false, false }, - { false, true, false, false }, - { false, false, true, false }, - { false, false, false, true } - }; - - /// - /// Create as brush that will paint a solid color - /// - /// The color. - /// A Brush - public static SolidBrush Solid(TColor color) - => new SolidBrush(color); - - /// - /// Create as brush that will paint a Percent10 Hatch Pattern within the specified colors - /// - /// Color of the foreground. - /// Color of the background. - /// A Brush - public static PatternBrush Percent10(TColor foreColor, TColor backColor) - => new PatternBrush(foreColor, backColor, Percent10Pattern); - - /// - /// Create as brush that will paint a Percent20 Hatch Pattern within the specified colors - /// - /// Color of the foreground. - /// Color of the background. - /// A Brush - public static PatternBrush Percent20(TColor foreColor, TColor backColor) - => new PatternBrush(foreColor, backColor, Percent20Pattern); - - /// - /// Create as brush that will paint a Horizontal Hatch Pattern within the specified colors - /// - /// Color of the foreground. - /// Color of the background. - /// A Brush - public static PatternBrush Horizontal(TColor foreColor, TColor backColor) - => new PatternBrush(foreColor, backColor, HorizontalPattern); - - /// - /// Create as brush that will paint a Min Hatch Pattern within the specified colors - /// - /// Color of the foreground. - /// Color of the background. - /// A Brush - public static PatternBrush Min(TColor foreColor, TColor backColor) - => new PatternBrush(foreColor, backColor, MinPattern); - - /// - /// Create as brush that will paint a Vertical Hatch Pattern within the specified colors - /// - /// Color of the foreground. - /// Color of the background. - /// A Brush - public static PatternBrush Vertical(TColor foreColor, TColor backColor) - => new PatternBrush(foreColor, backColor, VerticalPattern); - - /// - /// Create as brush that will paint a Forward Diagonal Hatch Pattern within the specified colors - /// - /// Color of the foreground. - /// Color of the background. - /// A Brush - public static PatternBrush ForwardDiagonal(TColor foreColor, TColor backColor) - => new PatternBrush(foreColor, backColor, ForwardDiagonalPattern); - - /// - /// Create as brush that will paint a Backward Diagonal Hatch Pattern within the specified colors - /// - /// Color of the foreground. - /// Color of the background. - /// A Brush - public static PatternBrush BackwardDiagonal(TColor foreColor, TColor backColor) - => new PatternBrush(foreColor, backColor, BackwardDiagonalPattern); - } -} diff --git a/src/ImageSharp.Drawing/Brushes/IBrush.cs b/src/ImageSharp.Drawing/Brushes/IBrush.cs index df05fa23e1..9534c7a882 100644 --- a/src/ImageSharp.Drawing/Brushes/IBrush.cs +++ b/src/ImageSharp.Drawing/Brushes/IBrush.cs @@ -5,26 +5,26 @@ namespace ImageSharp.Drawing { - using System; - + using ImageSharp.PixelFormats; using Processors; /// /// Brush represents a logical configuration of a brush which can be used to source pixel colors /// - /// The pixel format. + /// The pixel format. /// - /// A brush is a simple class that will return an that will perform the - /// logic for converting a pixel location to a . + /// A brush is a simple class that will return an that will perform the + /// logic for converting a pixel location to a . /// - public interface IBrush - where TColor : struct, IPixel + public interface IBrush + where TPixel : struct, IPixel { /// /// Creates the applicator for this brush. /// /// The pixel source. /// The region the brush will be applied to. + /// The graphic options /// /// The brush applicator for this brush /// @@ -32,6 +32,6 @@ namespace ImageSharp.Drawing /// The when being applied to things like shapes would usually be the /// bounding box of the shape not necessarily the bounds of the whole image /// - BrushApplicator CreateApplicator(PixelAccessor pixelSource, RectangleF region); + BrushApplicator CreateApplicator(PixelAccessor pixelSource, RectangleF region, GraphicsOptions options); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush.cs deleted file mode 100644 index a7124bfe86..0000000000 --- a/src/ImageSharp.Drawing/Brushes/ImageBrush.cs +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Brushes -{ - /// - /// Provides an implementation of a solid brush for painting with repeating images. The brush uses for painting. - /// - public class ImageBrush : ImageBrush - { - /// - /// Initializes a new instance of the class. - /// - /// The image to paint. - public ImageBrush(IImageBase image) - : base(image) - { - } - } -} diff --git a/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs similarity index 50% rename from src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs rename to src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs index ace929bd6a..6f851e5c3b 100644 --- a/src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/ImageBrush{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -8,44 +8,46 @@ namespace ImageSharp.Drawing.Brushes using System; using System.Numerics; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; using Processors; /// /// Provides an implementation of an image brush for painting images within areas. /// - /// The pixel format. - public class ImageBrush : IBrush - where TColor : struct, IPixel + /// The pixel format. + public class ImageBrush : IBrush + where TPixel : struct, IPixel { /// /// The image to paint. /// - private readonly IImageBase image; + private readonly IImageBase image; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The image. - public ImageBrush(IImageBase image) + public ImageBrush(IImageBase image) { this.image = image; } /// - public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region) + public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options) { - return new ImageBrushApplicator(sourcePixels, this.image, region); + return new ImageBrushApplicator(sourcePixels, this.image, region, options); } /// /// The image brush applicator. /// - private class ImageBrushApplicator : BrushApplicator + private class ImageBrushApplicator : BrushApplicator { /// /// The source pixel accessor. /// - private readonly PixelAccessor source; + private readonly PixelAccessor source; /// /// The y-length. @@ -58,9 +60,14 @@ namespace ImageSharp.Drawing.Brushes private readonly int xLength; /// - /// The offset. + /// The Y offset. /// - private readonly Vector2 offset; + private readonly int offsetY; + + /// + /// The X offset. + /// + private readonly int offsetX; /// /// Initializes a new instance of the class. @@ -71,16 +78,18 @@ namespace ImageSharp.Drawing.Brushes /// /// The region. /// + /// The options /// /// The sourcePixels. /// - public ImageBrushApplicator(PixelAccessor sourcePixels, IImageBase image, RectangleF region) - : base(sourcePixels) + public ImageBrushApplicator(PixelAccessor sourcePixels, IImageBase image, RectangleF region, GraphicsOptions options) + : base(sourcePixels, options) { this.source = image.Lock(); this.xLength = image.Width; this.yLength = image.Height; - this.offset = new Vector2(MathF.Max(MathF.Floor(region.Top), 0), MathF.Max(MathF.Floor(region.Left), 0)); + this.offsetY = (int)MathF.Max(MathF.Floor(region.Top), 0); + this.offsetX = (int)MathF.Max(MathF.Floor(region.Left), 0); } /// @@ -91,17 +100,12 @@ namespace ImageSharp.Drawing.Brushes /// /// The color /// - internal override TColor this[int x, int y] + internal override TPixel this[int x, int y] { get { - Vector2 point = new Vector2(x, y); - - // Offset the requested pixel by the value in the rectangle (the shapes position) - point = point - this.offset; - int srcX = (int)point.X % this.xLength; - int srcY = (int)point.Y % this.yLength; - + int srcX = (x - this.offsetX) % this.xLength; + int srcY = (y - this.offsetY) % this.yLength; return this.source[srcX, srcY]; } } @@ -113,33 +117,27 @@ namespace ImageSharp.Drawing.Brushes } /// - internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y) + internal override void Apply(Span scanline, int x, int y) { - Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - - using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer)) + // create a span for colors + using (Buffer amountBuffer = new Buffer(scanline.Length)) + using (Buffer overlay = new Buffer(scanline.Length)) { - BufferSpan slice = buffer.Slice(offset); + int sourceY = (y - this.offsetY) % this.yLength; + int offsetX = x - this.offsetX; + Span sourceRow = this.source.GetRowSpan(sourceY); - for (int xPos = 0; xPos < scanlineWidth; xPos++) + for (int i = 0; i < scanline.Length; i++) { - int targetX = xPos + x; - int targetY = y; - - float opacity = slice[xPos]; - if (opacity > Constants.Epsilon) - { - Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4(); - - Vector4 sourceVector = this[targetX, targetY].ToVector4(); + amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; - Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); - - TColor packed = default(TColor); - packed.PackFromVector4(finalColor); - this.Target[targetX, targetY] = packed; - } + int sourceX = (i + offsetX) % this.xLength; + TPixel pixel = sourceRow[sourceX]; + overlay[i] = pixel; } + + Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); } } } diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush.cs deleted file mode 100644 index 5093a7df06..0000000000 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush.cs +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Brushes -{ - /// - /// Provides an implementation of a pattern brush for painting patterns. The brush use for painting. - /// - public class PatternBrush : PatternBrush - { - /// - /// Initializes a new instance of the class. - /// - /// Color of the fore. - /// Color of the back. - /// The pattern. - public PatternBrush(Color foreColor, Color backColor, bool[,] pattern) - : base(foreColor, backColor, pattern) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The brush. - internal PatternBrush(PatternBrush brush) - : base(brush) - { - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs similarity index 65% rename from src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs rename to src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs index df492a764e..90990e54a9 100644 --- a/src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/PatternBrush{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -8,6 +8,8 @@ namespace ImageSharp.Drawing.Brushes using System; using System.Numerics; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; using Processors; /// @@ -31,38 +33,38 @@ namespace ImageSharp.Drawing.Brushes /// 0 /// /// - /// The pixel format. - public class PatternBrush : IBrush - where TColor : struct, IPixel + /// The pixel format. + public class PatternBrush : IBrush + where TPixel : struct, IPixel { /// /// The pattern. /// - private readonly Fast2DArray pattern; + private readonly Fast2DArray pattern; private readonly Fast2DArray patternVector; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Color of the fore. /// Color of the back. /// The pattern. - public PatternBrush(TColor foreColor, TColor backColor, bool[,] pattern) + public PatternBrush(TPixel foreColor, TPixel backColor, bool[,] pattern) : this(foreColor, backColor, new Fast2DArray(pattern)) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Color of the fore. /// Color of the back. /// The pattern. - internal PatternBrush(TColor foreColor, TColor backColor, Fast2DArray pattern) + internal PatternBrush(TPixel foreColor, TPixel backColor, Fast2DArray pattern) { Vector4 foreColorVector = foreColor.ToVector4(); Vector4 backColorVector = backColor.ToVector4(); - this.pattern = new Fast2DArray(pattern.Width, pattern.Height); + this.pattern = new Fast2DArray(pattern.Width, pattern.Height); this.patternVector = new Fast2DArray(pattern.Width, pattern.Height); for (int i = 0; i < pattern.Data.Length; i++) { @@ -80,30 +82,30 @@ namespace ImageSharp.Drawing.Brushes } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The brush. - internal PatternBrush(PatternBrush brush) + internal PatternBrush(PatternBrush brush) { this.pattern = brush.pattern; this.patternVector = brush.patternVector; } /// - public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region) + public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options) { - return new PatternBrushApplicator(sourcePixels, this.pattern, this.patternVector); + return new PatternBrushApplicator(sourcePixels, this.pattern, this.patternVector, options); } /// /// The pattern brush applicator. /// - private class PatternBrushApplicator : BrushApplicator + private class PatternBrushApplicator : BrushApplicator { /// /// The pattern. /// - private readonly Fast2DArray pattern; + private readonly Fast2DArray pattern; private readonly Fast2DArray patternVector; /// @@ -112,8 +114,9 @@ namespace ImageSharp.Drawing.Brushes /// The sourcePixels. /// The pattern. /// The patternVector. - public PatternBrushApplicator(PixelAccessor sourcePixels, Fast2DArray pattern, Fast2DArray patternVector) - : base(sourcePixels) + /// The options + public PatternBrushApplicator(PixelAccessor sourcePixels, Fast2DArray pattern, Fast2DArray patternVector, GraphicsOptions options) + : base(sourcePixels, options) { this.pattern = pattern; this.patternVector = patternVector; @@ -127,7 +130,7 @@ namespace ImageSharp.Drawing.Brushes /// /// The Color. /// - internal override TColor this[int x, int y] + internal override TPixel this[int x, int y] { get { @@ -146,34 +149,22 @@ namespace ImageSharp.Drawing.Brushes } /// - internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y) + internal override void Apply(Span scanline, int x, int y) { - Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - - using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer)) + int patternY = y % this.pattern.Height; + using (Buffer amountBuffer = new Buffer(scanline.Length)) + using (Buffer overlay = new Buffer(scanline.Length)) { - BufferSpan slice = buffer.Slice(offset); - - for (int xPos = 0; xPos < scanlineWidth; xPos++) + for (int i = 0; i < scanline.Length; i++) { - int targetX = xPos + x; - int targetY = y; - - float opacity = slice[xPos]; - if (opacity > Constants.Epsilon) - { - Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4(); + amountBuffer[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1); - // 2d array index at row/column - Vector4 sourceVector = this.patternVector[targetY % this.patternVector.Height, targetX % this.patternVector.Width]; - - Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); - - TColor packed = default(TColor); - packed.PackFromVector4(finalColor); - this.Target[targetX, targetY] = packed; - } + int patternX = (x + i) % this.pattern.Width; + overlay[i] = this.pattern[patternY, patternX]; } + + Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); } } } diff --git a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs index 46444e5503..29629324ab 100644 --- a/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs @@ -7,37 +7,54 @@ namespace ImageSharp.Drawing.Processors { using System; using System.Numerics; - using System.Runtime.CompilerServices; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; /// /// primitive that converts a point in to a color for discovering the fill color based on an implementation /// - /// The pixel format. + /// The pixel format. /// - public abstract class BrushApplicator : IDisposable // disposable will be required if/when there is an ImageBrush - where TColor : struct, IPixel + public abstract class BrushApplicator : IDisposable // disposable will be required if/when there is an ImageBrush + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The target. - internal BrushApplicator(PixelAccessor target) + /// The options. + internal BrushApplicator(PixelAccessor target, GraphicsOptions options) { this.Target = target; + + this.Options = options; + + this.Blender = PixelOperations.Instance.GetPixelBlender(options.BlenderMode); } + /// + /// Gets the blendder + /// + internal PixelBlender Blender { get; } + /// /// Gets the destinaion /// - protected PixelAccessor Target { get; } + protected PixelAccessor Target { get; } + + /// + /// Gets the blend percentage + /// + protected GraphicsOptions Options { get; private set; } /// /// Gets the color for a single pixel. /// /// The x cordinate. /// The y cordinate. - /// The a that should be applied to the pixel. - internal abstract TColor this[int x, int y] { get; } + /// The a that should be applied to the pixel. + internal abstract TPixel this[int x, int y] { get; } /// public abstract void Dispose(); @@ -45,39 +62,27 @@ namespace ImageSharp.Drawing.Processors /// /// Applies the opactiy weighting for each pixel in a scanline to the target based on the pattern contained in the brush. /// - /// The a collection of opacity values between 0 and 1 to be merged with the brushed color value before being applied to the target. - /// The number of pixels effected by this scanline. - /// The offset fromthe begining of the opacity data starts. + /// The a collection of opacity values between 0 and 1 to be merged with the brushed color value before being applied to the target. /// The x position in the target pixel space that the start of the scanline data corresponds to. /// The y position in the target pixel space that whole scanline corresponds to. /// scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs. - internal virtual void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y) + internal virtual void Apply(Span scanline, int x, int y) { - DebugGuard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - - using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer)) + using (Buffer amountBuffer = new Buffer(scanline.Length)) + using (Buffer overlay = new Buffer(scanline.Length)) { - BufferSpan slice = buffer.Slice(offset); - - for (int xPos = 0; xPos < scanlineWidth; xPos++) + for (int i = 0; i < scanline.Length; i++) { - int targetX = xPos + x; - int targetY = y; - - float opacity = slice[xPos]; - if (opacity > Constants.Epsilon) + if (this.Options.BlendPercentage < 1) { - Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4(); - - Vector4 sourceVector = this[targetX, targetY].ToVector4(); - - Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); - - TColor packed = default(TColor); - packed.PackFromVector4(finalColor); - this.Target[targetX, targetY] = packed; + amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; } + + overlay[i] = this[x + i, y]; } + + Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); } } } diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush.cs deleted file mode 100644 index 0452a3f015..0000000000 --- a/src/ImageSharp.Drawing/Brushes/RecolorBrush.cs +++ /dev/null @@ -1,24 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Brushes -{ - /// - /// Provides an implementation of a recolor brush for painting color changes. - /// - public class RecolorBrush : RecolorBrush - { - /// - /// Initializes a new instance of the class. - /// - /// Color of the source. - /// Color of the target. - /// The threshold. - public RecolorBrush(Color sourceColor, Color targetColor, float threshold) - : base(sourceColor, targetColor, threshold) - { - } - } -} diff --git a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs similarity index 56% rename from src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs rename to src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs index 257eeb3ae5..64b91e3844 100644 --- a/src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs +++ b/src/ImageSharp.Drawing/Brushes/RecolorBrush{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -8,26 +8,28 @@ namespace ImageSharp.Drawing.Brushes using System; using System.Numerics; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; using Processors; /// /// Provides an implementation of a brush that can recolor an image /// - /// The pixel format. - public class RecolorBrush : IBrush - where TColor : struct, IPixel + /// The pixel format. + public class RecolorBrush : IBrush + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Color of the source. - /// Color of the target. + /// Color of the target. /// The threshold as a value between 0 and 1. - public RecolorBrush(TColor sourceColor, TColor targetColor, float threshold) + public RecolorBrush(TPixel sourceColor, TPixel targeTPixel, float threshold) { this.SourceColor = sourceColor; this.Threshold = threshold; - this.TargetColor = targetColor; + this.TargeTPixel = targeTPixel; } /// @@ -44,7 +46,7 @@ namespace ImageSharp.Drawing.Brushes /// /// The color of the source. /// - public TColor SourceColor { get; } + public TPixel SourceColor { get; } /// /// Gets the target color. @@ -52,18 +54,18 @@ namespace ImageSharp.Drawing.Brushes /// /// The color of the target. /// - public TColor TargetColor { get; } + public TPixel TargeTPixel { get; } /// - public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region) + public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options) { - return new RecolorBrushApplicator(sourcePixels, this.SourceColor, this.TargetColor, this.Threshold); + return new RecolorBrushApplicator(sourcePixels, this.SourceColor, this.TargeTPixel, this.Threshold, options); } /// /// The recolor brush applicator. /// - private class RecolorBrushApplicator : BrushApplicator + private class RecolorBrushApplicator : BrushApplicator { /// /// The source color. @@ -80,6 +82,8 @@ namespace ImageSharp.Drawing.Brushes /// private readonly float threshold; + private readonly TPixel targetColorPixel; + /// /// Initializes a new instance of the class. /// @@ -87,16 +91,18 @@ namespace ImageSharp.Drawing.Brushes /// Color of the source. /// Color of the target. /// The threshold . - public RecolorBrushApplicator(PixelAccessor sourcePixels, TColor sourceColor, TColor targetColor, float threshold) - : base(sourcePixels) + /// The options + public RecolorBrushApplicator(PixelAccessor sourcePixels, TPixel sourceColor, TPixel targetColor, float threshold, GraphicsOptions options) + : base(sourcePixels, options) { this.sourceColor = sourceColor.ToVector4(); this.targetColor = targetColor.ToVector4(); + this.targetColorPixel = targetColor; // Lets hack a min max extreams for a color space by letteing the IPackedPixel clamp our values to something in the correct spaces :) - TColor maxColor = default(TColor); + TPixel maxColor = default(TPixel); maxColor.PackFromVector4(new Vector4(float.MaxValue)); - TColor minColor = default(TColor); + TPixel minColor = default(TPixel); minColor.PackFromVector4(new Vector4(float.MinValue)); this.threshold = Vector4.DistanceSquared(maxColor.ToVector4(), minColor.ToVector4()) * threshold; } @@ -109,22 +115,21 @@ namespace ImageSharp.Drawing.Brushes /// /// The color /// - internal override TColor this[int x, int y] + internal override TPixel this[int x, int y] { get { // Offset the requested pixel by the value in the rectangle (the shapes position) - TColor result = this.Target[x, y]; + TPixel result = this.Target[x, y]; Vector4 background = result.ToVector4(); float distance = Vector4.DistanceSquared(background, this.sourceColor); if (distance <= this.threshold) { float lerpAmount = (this.threshold - distance) / this.threshold; - Vector4 blended = Vector4BlendTransforms.PremultipliedLerp( - background, - this.targetColor, + return this.Blender.Blend( + result, + this.targetColorPixel, lerpAmount); - result.PackFromVector4(blended); } return result; @@ -137,42 +142,24 @@ namespace ImageSharp.Drawing.Brushes } /// - internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y) + internal override void Apply(Span scanline, int x, int y) { - Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - - using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer)) + using (Buffer amountBuffer = new Buffer(scanline.Length)) + using (Buffer overlay = new Buffer(scanline.Length)) { - BufferSpan slice = buffer.Slice(offset); - - for (int xPos = 0; xPos < scanlineWidth; xPos++) + for (int i = 0; i < scanline.Length; i++) { - int targetX = xPos + x; - int targetY = y; - - float opacity = slice[xPos]; - if (opacity > Constants.Epsilon) - { - Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4(); - - Vector4 sourceVector = backgroundVector; - float distance = Vector4.DistanceSquared(sourceVector, this.sourceColor); - if (distance <= this.threshold) - { - float lerpAmount = (this.threshold - distance) / this.threshold; - sourceVector = Vector4BlendTransforms.PremultipliedLerp( - sourceVector, - this.targetColor, - lerpAmount); - - Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); - - TColor packed = default(TColor); - packed.PackFromVector4(finalColor); - this.Target[targetX, targetY] = packed; - } - } + amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; + + int offsetX = x + i; + + // no doubt this one can be optermised further but I can't imagine its + // actually being used and can probably be removed/interalised for now + overlay[i] = this[offsetX, y]; } + + Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); } } } diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush.cs deleted file mode 100644 index 123d8a7e31..0000000000 --- a/src/ImageSharp.Drawing/Brushes/SolidBrush.cs +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Brushes -{ - /// - /// Provides an implementation of a solid brush for painting solid color areas. The brush uses for painting. - /// - public class SolidBrush : SolidBrush - { - /// - /// Initializes a new instance of the class. - /// - /// The color. - public SolidBrush(Color color) - : base(color) - { - } - } -} diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs deleted file mode 100644 index 125b07bcac..0000000000 --- a/src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs +++ /dev/null @@ -1,118 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Brushes -{ - using System; - using System.Numerics; - - using Processors; - - /// - /// Provides an implementation of a solid brush for painting solid color areas. - /// - /// The pixel format. - public class SolidBrush : IBrush - where TColor : struct, IPixel - { - /// - /// The color to paint. - /// - private readonly TColor color; - - /// - /// Initializes a new instance of the class. - /// - /// The color. - public SolidBrush(TColor color) - { - this.color = color; - } - - /// - /// Gets the color. - /// - /// - /// The color. - /// - public TColor Color => this.color; - - /// - public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region) - { - return new SolidBrushApplicator(sourcePixels, this.color); - } - - /// - /// The solid brush applicator. - /// - private class SolidBrushApplicator : BrushApplicator - { - /// - /// The solid color. - /// - private readonly TColor color; - private readonly Vector4 colorVector; - - /// - /// Initializes a new instance of the class. - /// - /// The color. - /// The sourcePixels. - public SolidBrushApplicator(PixelAccessor sourcePixels, TColor color) - : base(sourcePixels) - { - this.color = color; - this.colorVector = color.ToVector4(); - } - - /// - /// Gets the color for a single pixel. - /// - /// The x. - /// The y. - /// - /// The color - /// - internal override TColor this[int x, int y] => this.color; - - /// - public override void Dispose() - { - // noop - } - - /// - internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y) - { - Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); - - using (PinnedBuffer buffer = new PinnedBuffer(scanlineBuffer)) - { - BufferSpan slice = buffer.Slice(offset); - - for (int xPos = 0; xPos < scanlineWidth; xPos++) - { - int targetX = xPos + x; - int targetY = y; - - float opacity = slice[xPos]; - if (opacity > Constants.Epsilon) - { - Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4(); - Vector4 sourceVector = this.colorVector; - - Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); - - TColor packed = default(TColor); - packed.PackFromVector4(finalColor); - this.Target[targetX, targetY] = packed; - } - } - } - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs new file mode 100644 index 0000000000..28f7b0e454 --- /dev/null +++ b/src/ImageSharp.Drawing/Brushes/SolidBrush{TPixel}.cs @@ -0,0 +1,109 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Drawing.Brushes +{ + using System; + using System.Numerics; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + using Processors; + + /// + /// Provides an implementation of a solid brush for painting solid color areas. + /// + /// The pixel format. + public class SolidBrush : IBrush + where TPixel : struct, IPixel + { + /// + /// The color to paint. + /// + private readonly TPixel color; + + /// + /// Initializes a new instance of the class. + /// + /// The color. + public SolidBrush(TPixel color) + { + this.color = color; + } + + /// + /// Gets the color. + /// + /// + /// The color. + /// + public TPixel Color => this.color; + + /// + public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options) + { + return new SolidBrushApplicator(sourcePixels, this.color, options); + } + + /// + /// The solid brush applicator. + /// + private class SolidBrushApplicator : BrushApplicator + { + /// + /// Initializes a new instance of the class. + /// + /// The color. + /// The options + /// The sourcePixels. + public SolidBrushApplicator(PixelAccessor sourcePixels, TPixel color, GraphicsOptions options) + : base(sourcePixels, options) + { + this.Colors = new Buffer(sourcePixels.Width); + for (int i = 0; i < this.Colors.Length; i++) + { + this.Colors[i] = color; + } + } + + /// + /// Gets the colors. + /// + protected Buffer Colors { get; } + + /// + /// Gets the color for a single pixel. + /// + /// The x. + /// The y. + /// + /// The color + /// + internal override TPixel this[int x, int y] => this.Colors[x]; + + /// + public override void Dispose() + { + this.Colors.Dispose(); + } + + /// + internal override void Apply(Span scanline, int x, int y) + { + Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); + + using (Buffer amountBuffer = new Buffer(scanline.Length)) + { + for (int i = 0; i < scanline.Length; i++) + { + amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; + } + + this.Blender.Blend(destinationRow, destinationRow, this.Colors, amountBuffer); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/DrawImage.cs b/src/ImageSharp.Drawing/DrawImage.cs index 16582e7ee5..975bce9ed7 100644 --- a/src/ImageSharp.Drawing/DrawImage.cs +++ b/src/ImageSharp.Drawing/DrawImage.cs @@ -1,58 +1,129 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - - using Drawing.Processors; - - /// - /// Extension methods for the type. - /// - public static partial class ImageExtensions - { - /// - /// Draws the given image together with the current one by blending their pixels. - /// - /// The pixel format. - /// The image this method extends. - /// The image to blend with the currently processing image. - /// The opacity of the image image to blend. Must be between 0 and 100. - /// The . - public static Image Blend(this Image source, Image image, int percent = 50) - where TColor : struct, IPixel - { - return DrawImage(source, image, percent, default(Size), default(Point)); - } - - /// - /// Draws the given image together with the current one by blending their pixels. - /// - /// The image this method extends. - /// The image to blend with the currently processing image. - /// The pixel format. - /// The opacity of the image image to blend. Must be between 0 and 100. - /// The size to draw the blended image. - /// The location to draw the blended image. - /// The . - public static Image DrawImage(this Image source, Image image, int percent, Size size, Point location) - where TColor : struct, IPixel - { - if (size == default(Size)) - { - size = new Size(image.Width, image.Height); - } - - if (location == default(Point)) - { - location = Point.Empty; - } - - source.ApplyProcessor(new DrawImageProcessor(image, size, location, percent), source.Bounds); - return source; - } - } +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using Drawing.Processors; + using ImageSharp.PixelFormats; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The pixel format. + /// The size to draw the blended image. + /// The location to draw the blended image. + /// The options. + /// The . + public static Image DrawImage(this Image source, Image image, Size size, Point location, GraphicsOptions options) + where TPixel : struct, IPixel + { + if (size == default(Size)) + { + size = new Size(image.Width, image.Height); + } + + if (location == default(Point)) + { + location = Point.Empty; + } + + source.ApplyProcessor(new DrawImageProcessor(image, size, location, options), source.Bounds); + return source; + } + + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The pixel format. + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The opacity of the image image to blend. Must be between 0 and 1. + /// The . + public static Image Blend(this Image source, Image image, float percent) + where TPixel : struct, IPixel + { + GraphicsOptions options = GraphicsOptions.Default; + options.BlendPercentage = percent; + return DrawImage(source, image, default(Size), default(Point), options); + } + + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The pixel format. + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The blending mode. + /// The opacity of the image image to blend. Must be between 0 and 1. + /// The . + public static Image Blend(this Image source, Image image, PixelBlenderMode blender, float percent) + where TPixel : struct, IPixel + { + GraphicsOptions options = GraphicsOptions.Default; + options.BlendPercentage = percent; + options.BlenderMode = blender; + return DrawImage(source, image, default(Size), default(Point), options); + } + + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The pixel format. + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The options, including the blending type and belnding amount. + /// The . + public static Image Blend(this Image source, Image image, GraphicsOptions options) + where TPixel : struct, IPixel + { + return DrawImage(source, image, default(Size), default(Point), options); + } + + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The pixel format. + /// The opacity of the image image to blend. Must be between 0 and 1. + /// The size to draw the blended image. + /// The location to draw the blended image. + /// The . + public static Image DrawImage(this Image source, Image image, float percent, Size size, Point location) + where TPixel : struct, IPixel + { + GraphicsOptions options = GraphicsOptions.Default; + options.BlendPercentage = percent; + return source.DrawImage(image, size, location, options); + } + + /// + /// Draws the given image together with the current one by blending their pixels. + /// + /// The image this method extends. + /// The image to blend with the currently processing image. + /// The pixel format. + /// The type of bending to apply. + /// The opacity of the image image to blend. Must be between 0 and 1. + /// The size to draw the blended image. + /// The location to draw the blended image. + /// The . + public static Image DrawImage(this Image source, Image image, PixelBlenderMode blender, float percent, Size size, Point location) + where TPixel : struct, IPixel + { + GraphicsOptions options = GraphicsOptions.Default; + options.BlenderMode = blender; + options.BlendPercentage = percent; + return source.DrawImage(image, size, location, options); + } + } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/DrawPath.cs b/src/ImageSharp.Drawing/DrawPath.cs index e91b972033..09d3dbb028 100644 --- a/src/ImageSharp.Drawing/DrawPath.cs +++ b/src/ImageSharp.Drawing/DrawPath.cs @@ -5,43 +5,42 @@ namespace ImageSharp { - using System; - using Drawing; using Drawing.Brushes; using Drawing.Pens; using Drawing.Processors; + using ImageSharp.PixelFormats; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Draws the outline of the region with the provided pen. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The pen. /// The path. /// The options. - /// The . - public static Image Draw(this Image source, IPen pen, Drawable path, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, IPen pen, Drawable path, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.Apply(new DrawPathProcessor(pen, path, options)); + return source.Apply(new DrawPathProcessor(pen, path, options)); } /// /// Draws the outline of the polygon with the provided pen. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The pen. /// The path. - /// The . - public static Image Draw(this Image source, IPen pen, Drawable path) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, IPen pen, Drawable path) + where TPixel : struct, IPixel { return source.Draw(pen, path, GraphicsOptions.Default); } @@ -49,63 +48,63 @@ namespace ImageSharp /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The thickness. /// The path. /// The options. - /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, Drawable path, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, IBrush brush, float thickness, Drawable path, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.Draw(new Pen(brush, thickness), path, options); + return source.Draw(new Pen(brush, thickness), path, options); } /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The thickness. /// The path. - /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, Drawable path) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, IBrush brush, float thickness, Drawable path) + where TPixel : struct, IPixel { - return source.Draw(new Pen(brush, thickness), path); + return source.Draw(new Pen(brush, thickness), path); } /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The thickness. /// The path. /// The options. - /// The . - public static Image Draw(this Image source, TColor color, float thickness, Drawable path, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, TPixel color, float thickness, Drawable path, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.Draw(new SolidBrush(color), thickness, path, options); + return source.Draw(new SolidBrush(color), thickness, path, options); } /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The thickness. /// The path. - /// The . - public static Image Draw(this Image source, TColor color, float thickness, Drawable path) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, TPixel color, float thickness, Drawable path) + where TPixel : struct, IPixel { - return source.Draw(new SolidBrush(color), thickness, path); + return source.Draw(new SolidBrush(color), thickness, path); } } } diff --git a/src/ImageSharp.Drawing/FillRegion.cs b/src/ImageSharp.Drawing/FillRegion.cs index 8aab202519..b3ee2ed996 100644 --- a/src/ImageSharp.Drawing/FillRegion.cs +++ b/src/ImageSharp.Drawing/FillRegion.cs @@ -5,68 +5,81 @@ namespace ImageSharp { - using System; - using Drawing; using Drawing.Brushes; using Drawing.Processors; + using ImageSharp.PixelFormats; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Flood fills the image with the specified brush. /// - /// The type of the color. + /// The type of the color. + /// The image this method extends. + /// The details how to fill the region of interest. + /// The graphics options. + /// The . + public static Image Fill(this Image source, IBrush brush, GraphicsOptions options) + where TPixel : struct, IPixel + { + return source.Apply(new FillProcessor(brush, options)); + } + + /// + /// Flood fills the image with the specified brush. + /// + /// The type of the color. /// The image this method extends. /// The details how to fill the region of interest. - /// The . - public static Image Fill(this Image source, IBrush brush) - where TColor : struct, IPixel + /// The . + public static Image Fill(this Image source, IBrush brush) + where TPixel : struct, IPixel { - return source.Apply(new FillProcessor(brush)); + return source.Fill(brush, GraphicsOptions.Default); } /// /// Flood fills the image with the specified color. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. - /// The . - public static Image Fill(this Image source, TColor color) - where TColor : struct, IPixel + /// The . + public static Image Fill(this Image source, TPixel color) + where TPixel : struct, IPixel { - return source.Fill(new SolidBrush(color)); + return source.Fill(new SolidBrush(color)); } /// /// Flood fills the image with in the region with the specified brush. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The region. /// The graphics options. - /// The . - public static Image Fill(this Image source, IBrush brush, Region region, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image Fill(this Image source, IBrush brush, Region region, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.Apply(new FillRegionProcessor(brush, region, options)); + return source.Apply(new FillRegionProcessor(brush, region, options)); } /// /// Flood fills the image with in the region with the specified brush. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The region. - /// The . - public static Image Fill(this Image source, IBrush brush, Region region) - where TColor : struct, IPixel + /// The . + public static Image Fill(this Image source, IBrush brush, Region region) + where TPixel : struct, IPixel { return source.Fill(brush, region, GraphicsOptions.Default); } @@ -74,30 +87,30 @@ namespace ImageSharp /// /// Flood fills the image with in the region with the specified color. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The region. /// The options. - /// The . - public static Image Fill(this Image source, TColor color, Region region, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image Fill(this Image source, TPixel color, Region region, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.Fill(new SolidBrush(color), region, options); + return source.Fill(new SolidBrush(color), region, options); } /// /// Flood fills the image with in the region with the specified color. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The region. - /// The . - public static Image Fill(this Image source, TColor color, Region region) - where TColor : struct, IPixel + /// The . + public static Image Fill(this Image source, TPixel color, Region region) + where TPixel : struct, IPixel { - return source.Fill(new SolidBrush(color), region); + return source.Fill(new SolidBrush(color), region); } } } diff --git a/src/ImageSharp.Drawing/GraphicsOptions.cs b/src/ImageSharp.Drawing/GraphicsOptions.cs deleted file mode 100644 index a21617eadf..0000000000 --- a/src/ImageSharp.Drawing/GraphicsOptions.cs +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing -{ - /// - /// Options for influencing the drawing functions. - /// - public struct GraphicsOptions - { - /// - /// Represents the default . - /// - public static readonly GraphicsOptions Default = new GraphicsOptions(true); - - /// - /// Whether antialiasing should be applied. - /// - public bool Antialias; - - /// - /// The number of subpixels to use while rendering with antialiasing enabled. - /// - public int AntialiasSubpixelDepth; - - /// - /// Initializes a new instance of the struct. - /// - /// If set to true [enable antialiasing]. - public GraphicsOptions(bool enableAntialiasing) - { - this.Antialias = enableAntialiasing; - this.AntialiasSubpixelDepth = 16; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index 351764bb95..a3552a09cc 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -2,7 +2,7 @@ An extension to ImageSharp that allows the drawing of images, paths, and text. ImageSharp.Drawing - 1.0.0-alpha5 + 1.0.0-alpha9 James Jackson-South and contributors netstandard1.1 true @@ -39,8 +39,8 @@ All - - + + ..\..\ImageSharp.ruleset diff --git a/src/ImageSharp.Drawing/Paths/DrawBeziers.cs b/src/ImageSharp.Drawing/Paths/DrawBeziers.cs index 936d5a9ce5..c4ea8c3785 100644 --- a/src/ImageSharp.Drawing/Paths/DrawBeziers.cs +++ b/src/ImageSharp.Drawing/Paths/DrawBeziers.cs @@ -5,92 +5,91 @@ namespace ImageSharp { - using System; using System.Numerics; using Drawing; using Drawing.Brushes; using Drawing.Pens; - + using ImageSharp.PixelFormats; using SixLabors.Shapes; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Draws the provided Points as an open Bezier path at the provided thickness with the supplied brush /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The thickness. /// The points. /// The options. - /// The . - public static Image DrawBeziers(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image DrawBeziers(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.Draw(new Pen(brush, thickness), new Path(new BezierLineSegment(points)), options); + return source.Draw(new Pen(brush, thickness), new Path(new BezierLineSegment(points)), options); } /// /// Draws the provided Points as an open Bezier path at the provided thickness with the supplied brush /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The thickness. /// The points. - /// The . - public static Image DrawBeziers(this Image source, IBrush brush, float thickness, Vector2[] points) - where TColor : struct, IPixel + /// The . + public static Image DrawBeziers(this Image source, IBrush brush, float thickness, Vector2[] points) + where TPixel : struct, IPixel { - return source.Draw(new Pen(brush, thickness), new Path(new BezierLineSegment(points))); + return source.Draw(new Pen(brush, thickness), new Path(new BezierLineSegment(points))); } /// /// Draws the provided Points as an open Bezier path at the provided thickness with the supplied brush /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The thickness. /// The points. - /// The . - public static Image DrawBeziers(this Image source, TColor color, float thickness, Vector2[] points) - where TColor : struct, IPixel + /// The . + public static Image DrawBeziers(this Image source, TPixel color, float thickness, Vector2[] points) + where TPixel : struct, IPixel { - return source.DrawBeziers(new SolidBrush(color), thickness, points); + return source.DrawBeziers(new SolidBrush(color), thickness, points); } /// /// Draws the provided Points as an open Bezier path at the provided thickness with the supplied brush /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The thickness. /// The points. /// The options. - /// The . - public static Image DrawBeziers(this Image source, TColor color, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image DrawBeziers(this Image source, TPixel color, float thickness, Vector2[] points, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.DrawBeziers(new SolidBrush(color), thickness, points, options); + return source.DrawBeziers(new SolidBrush(color), thickness, points, options); } /// /// Draws the provided Points as an open Bezier path with the supplied pen /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The pen. /// The points. /// The options. - /// The . - public static Image DrawBeziers(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image DrawBeziers(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) + where TPixel : struct, IPixel { return source.Draw(pen, new Path(new BezierLineSegment(points)), options); } @@ -98,13 +97,13 @@ namespace ImageSharp /// /// Draws the provided Points as an open Bezier path with the supplied pen /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The pen. /// The points. - /// The . - public static Image DrawBeziers(this Image source, IPen pen, Vector2[] points) - where TColor : struct, IPixel + /// The . + public static Image DrawBeziers(this Image source, IPen pen, Vector2[] points) + where TPixel : struct, IPixel { return source.Draw(pen, new Path(new BezierLineSegment(points))); } diff --git a/src/ImageSharp.Drawing/Paths/DrawLines.cs b/src/ImageSharp.Drawing/Paths/DrawLines.cs index 42f4406e83..e8c463638f 100644 --- a/src/ImageSharp.Drawing/Paths/DrawLines.cs +++ b/src/ImageSharp.Drawing/Paths/DrawLines.cs @@ -5,92 +5,91 @@ namespace ImageSharp { - using System; using System.Numerics; using Drawing; using Drawing.Brushes; using Drawing.Pens; - + using ImageSharp.PixelFormats; using SixLabors.Shapes; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The thickness. /// The points. /// The options. - /// The . - public static Image DrawLines(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image DrawLines(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.Draw(new Pen(brush, thickness), new Path(new LinearLineSegment(points)), options); + return source.Draw(new Pen(brush, thickness), new Path(new LinearLineSegment(points)), options); } /// /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The thickness. /// The points. - /// The . - public static Image DrawLines(this Image source, IBrush brush, float thickness, Vector2[] points) - where TColor : struct, IPixel + /// The . + public static Image DrawLines(this Image source, IBrush brush, float thickness, Vector2[] points) + where TPixel : struct, IPixel { - return source.Draw(new Pen(brush, thickness), new Path(new LinearLineSegment(points))); + return source.Draw(new Pen(brush, thickness), new Path(new LinearLineSegment(points))); } /// /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The thickness. /// The points. - /// The . - public static Image DrawLines(this Image source, TColor color, float thickness, Vector2[] points) - where TColor : struct, IPixel + /// The . + public static Image DrawLines(this Image source, TPixel color, float thickness, Vector2[] points) + where TPixel : struct, IPixel { - return source.DrawLines(new SolidBrush(color), thickness, points); + return source.DrawLines(new SolidBrush(color), thickness, points); } /// /// Draws the provided Points as an open Linear path at the provided thickness with the supplied brush /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The thickness. /// The points. /// The options. - /// The .> - public static Image DrawLines(this Image source, TColor color, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPixel + /// The .> + public static Image DrawLines(this Image source, TPixel color, float thickness, Vector2[] points, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.DrawLines(new SolidBrush(color), thickness, points, options); + return source.DrawLines(new SolidBrush(color), thickness, points, options); } /// /// Draws the provided Points as an open Linear path with the supplied pen /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The pen. /// The points. /// The options. - /// The . - public static Image DrawLines(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image DrawLines(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) + where TPixel : struct, IPixel { return source.Draw(pen, new Path(new LinearLineSegment(points)), options); } @@ -98,13 +97,13 @@ namespace ImageSharp /// /// Draws the provided Points as an open Linear path with the supplied pen /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The pen. /// The points. - /// The . - public static Image DrawLines(this Image source, IPen pen, Vector2[] points) - where TColor : struct, IPixel + /// The . + public static Image DrawLines(this Image source, IPen pen, Vector2[] points) + where TPixel : struct, IPixel { return source.Draw(pen, new Path(new LinearLineSegment(points))); } diff --git a/src/ImageSharp.Drawing/Paths/DrawPath.cs b/src/ImageSharp.Drawing/Paths/DrawPath.cs index e2c1442de8..176539663c 100644 --- a/src/ImageSharp.Drawing/Paths/DrawPath.cs +++ b/src/ImageSharp.Drawing/Paths/DrawPath.cs @@ -5,30 +5,28 @@ namespace ImageSharp { - using System; - using Drawing; using Drawing.Brushes; using Drawing.Pens; - + using ImageSharp.PixelFormats; using SixLabors.Shapes; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Draws the outline of the polygon with the provided pen. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The pen. /// The path. /// The options. - /// The . - public static Image Draw(this Image source, IPen pen, IPath path, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, IPen pen, IPath path, GraphicsOptions options) + where TPixel : struct, IPixel { return source.Draw(pen, new ShapePath(path), options); } @@ -36,13 +34,13 @@ namespace ImageSharp /// /// Draws the outline of the polygon with the provided pen. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The pen. /// The path. - /// The . - public static Image Draw(this Image source, IPen pen, IPath path) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, IPen pen, IPath path) + where TPixel : struct, IPixel { return source.Draw(pen, path, GraphicsOptions.Default); } @@ -50,63 +48,63 @@ namespace ImageSharp /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The thickness. /// The shape. /// The options. - /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, IPath path, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, IBrush brush, float thickness, IPath path, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.Draw(new Pen(brush, thickness), path, options); + return source.Draw(new Pen(brush, thickness), path, options); } /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The thickness. /// The path. - /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, IPath path) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, IBrush brush, float thickness, IPath path) + where TPixel : struct, IPixel { - return source.Draw(new Pen(brush, thickness), path); + return source.Draw(new Pen(brush, thickness), path); } /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The thickness. /// The path. /// The options. - /// The . - public static Image Draw(this Image source, TColor color, float thickness, IPath path, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, TPixel color, float thickness, IPath path, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.Draw(new SolidBrush(color), thickness, path, options); + return source.Draw(new SolidBrush(color), thickness, path, options); } /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The thickness. /// The path. - /// The . - public static Image Draw(this Image source, TColor color, float thickness, IPath path) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, TPixel color, float thickness, IPath path) + where TPixel : struct, IPixel { - return source.Draw(new SolidBrush(color), thickness, path); + return source.Draw(new SolidBrush(color), thickness, path); } } } diff --git a/src/ImageSharp.Drawing/Paths/DrawPolygon.cs b/src/ImageSharp.Drawing/Paths/DrawPolygon.cs index 8043d18e56..4b99e60c02 100644 --- a/src/ImageSharp.Drawing/Paths/DrawPolygon.cs +++ b/src/ImageSharp.Drawing/Paths/DrawPolygon.cs @@ -5,91 +5,90 @@ namespace ImageSharp { - using System; using System.Numerics; using Drawing; using Drawing.Brushes; using Drawing.Pens; - + using ImageSharp.PixelFormats; using SixLabors.Shapes; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The thickness. /// The points. /// The options. - /// The . - public static Image DrawPolygon(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image DrawPolygon(this Image source, IBrush brush, float thickness, Vector2[] points, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.Draw(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points)), options); + return source.Draw(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points)), options); } /// /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The thickness. /// The points. - /// The . - public static Image DrawPolygon(this Image source, IBrush brush, float thickness, Vector2[] points) - where TColor : struct, IPixel + /// The . + public static Image DrawPolygon(this Image source, IBrush brush, float thickness, Vector2[] points) + where TPixel : struct, IPixel { - return source.Draw(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points))); + return source.Draw(new Pen(brush, thickness), new Polygon(new LinearLineSegment(points))); } /// /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The thickness. /// The points. - /// The . - public static Image DrawPolygon(this Image source, TColor color, float thickness, Vector2[] points) - where TColor : struct, IPixel + /// The . + public static Image DrawPolygon(this Image source, TPixel color, float thickness, Vector2[] points) + where TPixel : struct, IPixel { - return source.DrawPolygon(new SolidBrush(color), thickness, points); + return source.DrawPolygon(new SolidBrush(color), thickness, points); } /// /// Draws the provided Points as a closed Linear Polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The thickness. /// The points. /// The options. - /// The . - public static Image DrawPolygon(this Image source, TColor color, float thickness, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image DrawPolygon(this Image source, TPixel color, float thickness, Vector2[] points, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.DrawPolygon(new SolidBrush(color), thickness, points, options); + return source.DrawPolygon(new SolidBrush(color), thickness, points, options); } /// /// Draws the provided Points as a closed Linear Polygon with the provided Pen. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The pen. /// The points. - /// The . - public static Image DrawPolygon(this Image source, IPen pen, Vector2[] points) - where TColor : struct, IPixel + /// The . + public static Image DrawPolygon(this Image source, IPen pen, Vector2[] points) + where TPixel : struct, IPixel { return source.Draw(pen, new Polygon(new LinearLineSegment(points)), GraphicsOptions.Default); } @@ -97,14 +96,14 @@ namespace ImageSharp /// /// Draws the provided Points as a closed Linear Polygon with the provided Pen. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The pen. /// The points. /// The options. - /// The . - public static Image DrawPolygon(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image DrawPolygon(this Image source, IPen pen, Vector2[] points, GraphicsOptions options) + where TPixel : struct, IPixel { return source.Draw(pen, new Polygon(new LinearLineSegment(points)), options); } diff --git a/src/ImageSharp.Drawing/Paths/DrawRectangle.cs b/src/ImageSharp.Drawing/Paths/DrawRectangle.cs index b356652409..0fefc6cab4 100644 --- a/src/ImageSharp.Drawing/Paths/DrawRectangle.cs +++ b/src/ImageSharp.Drawing/Paths/DrawRectangle.cs @@ -5,28 +5,27 @@ namespace ImageSharp { - using System; - using Drawing; using Drawing.Brushes; using Drawing.Pens; + using ImageSharp.PixelFormats; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Draws the outline of the polygon with the provided pen. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The pen. /// The shape. /// The options. - /// The . - public static Image Draw(this Image source, IPen pen, Rectangle shape, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, IPen pen, Rectangle shape, GraphicsOptions options) + where TPixel : struct, IPixel { return source.Draw(pen, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options); } @@ -34,13 +33,13 @@ namespace ImageSharp /// /// Draws the outline of the polygon with the provided pen. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The pen. /// The shape. - /// The . - public static Image Draw(this Image source, IPen pen, Rectangle shape) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, IPen pen, Rectangle shape) + where TPixel : struct, IPixel { return source.Draw(pen, shape, GraphicsOptions.Default); } @@ -48,63 +47,63 @@ namespace ImageSharp /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The thickness. /// The shape. /// The options. - /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, Rectangle shape, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, IBrush brush, float thickness, Rectangle shape, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.Draw(new Pen(brush, thickness), shape, options); + return source.Draw(new Pen(brush, thickness), shape, options); } /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The thickness. /// The shape. - /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, Rectangle shape) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, IBrush brush, float thickness, Rectangle shape) + where TPixel : struct, IPixel { - return source.Draw(new Pen(brush, thickness), shape); + return source.Draw(new Pen(brush, thickness), shape); } /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The thickness. /// The shape. /// The options. - /// The . - public static Image Draw(this Image source, TColor color, float thickness, Rectangle shape, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, TPixel color, float thickness, Rectangle shape, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.Draw(new SolidBrush(color), thickness, shape, options); + return source.Draw(new SolidBrush(color), thickness, shape, options); } /// /// Draws the outline of the polygon with the provided brush at the provided thickness. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The thickness. /// The shape. - /// The . - public static Image Draw(this Image source, TColor color, float thickness, Rectangle shape) - where TColor : struct, IPixel + /// The . + public static Image Draw(this Image source, TPixel color, float thickness, Rectangle shape) + where TPixel : struct, IPixel { - return source.Draw(new SolidBrush(color), thickness, shape); + return source.Draw(new SolidBrush(color), thickness, shape); } } } diff --git a/src/ImageSharp.Drawing/Paths/FillPaths.cs b/src/ImageSharp.Drawing/Paths/FillPaths.cs index 92e227ce1f..f579c4ad02 100644 --- a/src/ImageSharp.Drawing/Paths/FillPaths.cs +++ b/src/ImageSharp.Drawing/Paths/FillPaths.cs @@ -5,29 +5,27 @@ namespace ImageSharp { - using System; - using Drawing; using Drawing.Brushes; - + using ImageSharp.PixelFormats; using SixLabors.Shapes; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The shape. /// The graphics options. - /// The . - public static Image Fill(this Image source, IBrush brush, IPath path, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image Fill(this Image source, IBrush brush, IPath path, GraphicsOptions options) + where TPixel : struct, IPixel { return source.Fill(brush, new ShapeRegion(path), options); } @@ -35,13 +33,13 @@ namespace ImageSharp /// /// Flood fills the image in the shape of the provided polygon with the specified brush. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The path. - /// The . - public static Image Fill(this Image source, IBrush brush, IPath path) - where TColor : struct, IPixel + /// The . + public static Image Fill(this Image source, IBrush brush, IPath path) + where TPixel : struct, IPixel { return source.Fill(brush, new ShapeRegion(path), GraphicsOptions.Default); } @@ -49,30 +47,30 @@ namespace ImageSharp /// /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The path. /// The options. - /// The . - public static Image Fill(this Image source, TColor color, IPath path, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image Fill(this Image source, TPixel color, IPath path, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.Fill(new SolidBrush(color), path, options); + return source.Fill(new SolidBrush(color), path, options); } /// /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The path. - /// The . - public static Image Fill(this Image source, TColor color, IPath path) - where TColor : struct, IPixel + /// The . + public static Image Fill(this Image source, TPixel color, IPath path) + where TPixel : struct, IPixel { - return source.Fill(new SolidBrush(color), path); + return source.Fill(new SolidBrush(color), path); } } } diff --git a/src/ImageSharp.Drawing/Paths/FillPolygon.cs b/src/ImageSharp.Drawing/Paths/FillPolygon.cs index cd3d154666..3360cff13c 100644 --- a/src/ImageSharp.Drawing/Paths/FillPolygon.cs +++ b/src/ImageSharp.Drawing/Paths/FillPolygon.cs @@ -9,25 +9,25 @@ namespace ImageSharp using System.Numerics; using Drawing; using Drawing.Brushes; - + using ImageSharp.PixelFormats; using SixLabors.Shapes; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Flood fills the image in the shape of a Linear polygon described by the points /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The points. /// The options. - /// The . - public static Image FillPolygon(this Image source, IBrush brush, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image FillPolygon(this Image source, IBrush brush, Vector2[] points, GraphicsOptions options) + where TPixel : struct, IPixel { return source.Fill(brush, new Polygon(new LinearLineSegment(points)), options); } @@ -35,13 +35,13 @@ namespace ImageSharp /// /// Flood fills the image in the shape of a Linear polygon described by the points /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The points. - /// The . - public static Image FillPolygon(this Image source, IBrush brush, Vector2[] points) - where TColor : struct, IPixel + /// The . + public static Image FillPolygon(this Image source, IBrush brush, Vector2[] points) + where TPixel : struct, IPixel { return source.Fill(brush, new Polygon(new LinearLineSegment(points))); } @@ -49,30 +49,30 @@ namespace ImageSharp /// /// Flood fills the image in the shape of a Linear polygon described by the points /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The points. /// The options. - /// The . - public static Image FillPolygon(this Image source, TColor color, Vector2[] points, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image FillPolygon(this Image source, TPixel color, Vector2[] points, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points)), options); + return source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points)), options); } /// /// Flood fills the image in the shape of a Linear polygon described by the points /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The points. - /// The . - public static Image FillPolygon(this Image source, TColor color, Vector2[] points) - where TColor : struct, IPixel + /// The . + public static Image FillPolygon(this Image source, TPixel color, Vector2[] points) + where TPixel : struct, IPixel { - return source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points))); + return source.Fill(new SolidBrush(color), new Polygon(new LinearLineSegment(points))); } } } diff --git a/src/ImageSharp.Drawing/Paths/FillRectangle.cs b/src/ImageSharp.Drawing/Paths/FillRectangle.cs index 1928e54d3c..07ff4c69c5 100644 --- a/src/ImageSharp.Drawing/Paths/FillRectangle.cs +++ b/src/ImageSharp.Drawing/Paths/FillRectangle.cs @@ -5,27 +5,26 @@ namespace ImageSharp { - using System; - using Drawing; using Drawing.Brushes; + using ImageSharp.PixelFormats; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The shape. /// The options. - /// The . - public static Image Fill(this Image source, IBrush brush, Rectangle shape, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image Fill(this Image source, IBrush brush, Rectangle shape, GraphicsOptions options) + where TPixel : struct, IPixel { return source.Fill(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height), options); } @@ -33,13 +32,13 @@ namespace ImageSharp /// /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The brush. /// The shape. - /// The . - public static Image Fill(this Image source, IBrush brush, Rectangle shape) - where TColor : struct, IPixel + /// The . + public static Image Fill(this Image source, IBrush brush, Rectangle shape) + where TPixel : struct, IPixel { return source.Fill(brush, new SixLabors.Shapes.Rectangle(shape.X, shape.Y, shape.Width, shape.Height)); } @@ -47,30 +46,30 @@ namespace ImageSharp /// /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The shape. /// The options. - /// The . - public static Image Fill(this Image source, TColor color, Rectangle shape, GraphicsOptions options) - where TColor : struct, IPixel + /// The . + public static Image Fill(this Image source, TPixel color, Rectangle shape, GraphicsOptions options) + where TPixel : struct, IPixel { - return source.Fill(new SolidBrush(color), shape, options); + return source.Fill(new SolidBrush(color), shape, options); } /// /// Flood fills the image in the shape of the provided polygon with the specified brush.. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The color. /// The shape. - /// The . - public static Image Fill(this Image source, TColor color, Rectangle shape) - where TColor : struct, IPixel + /// The . + public static Image Fill(this Image source, TPixel color, Rectangle shape) + where TPixel : struct, IPixel { - return source.Fill(new SolidBrush(color), shape); + return source.Fill(new SolidBrush(color), shape); } } } diff --git a/src/ImageSharp.Drawing/Pens/IPen.cs b/src/ImageSharp.Drawing/Pens/IPen.cs index 72a5ffc367..d488dbfb0a 100644 --- a/src/ImageSharp.Drawing/Pens/IPen.cs +++ b/src/ImageSharp.Drawing/Pens/IPen.cs @@ -5,27 +5,28 @@ namespace ImageSharp.Drawing.Pens { - using System; + using ImageSharp.PixelFormats; using Processors; /// /// Interface representing a Pen /// - /// The type of the color. - public interface IPen - where TColor : struct, IPixel + /// The type of the color. + public interface IPen + where TPixel : struct, IPixel { /// /// Creates the applicator for applying this pen to an Image /// /// The pixel source. /// The region the pen will be applied to. + /// The currently active graphic options. /// /// Returns a the applicator for the pen. /// /// /// The when being applied to things like shapes would usually be the bounding box of the shape not necessarily the shape of the whole image. /// - PenApplicator CreateApplicator(PixelAccessor pixelSource, RectangleF region); + PenApplicator CreateApplicator(PixelAccessor pixelSource, RectangleF region, GraphicsOptions options); } } diff --git a/src/ImageSharp.Drawing/Pens/Pen.cs b/src/ImageSharp.Drawing/Pens/Pen.cs deleted file mode 100644 index 09fe89419c..0000000000 --- a/src/ImageSharp.Drawing/Pens/Pen.cs +++ /dev/null @@ -1,53 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Pens -{ - /// - /// Represents a in the color space. - /// - public class Pen : Pen - { - /// - /// Initializes a new instance of the class. - /// - /// The color. - /// The width. - public Pen(Color color, float width) - : base(color, width) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The brush. - /// The width. - public Pen(IBrush brush, float width) - : base(brush, width) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The brush. - /// The width. - /// The pattern. - public Pen(IBrush brush, float width, float[] pattern) - : base(brush, width, pattern) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The pen. - internal Pen(Pen pen) - : base(pen) - { - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Pens/Pens.cs b/src/ImageSharp.Drawing/Pens/Pens.cs index 039b7113ff..364115cb77 100644 --- a/src/ImageSharp.Drawing/Pens/Pens.cs +++ b/src/ImageSharp.Drawing/Pens/Pens.cs @@ -5,89 +5,126 @@ namespace ImageSharp.Drawing.Pens { + using ImageSharp.PixelFormats; + /// /// Common Pen styles /// - public class Pens + public static class Pens { + private static readonly float[] DashDotPattern = new[] { 3f, 1f, 1f, 1f }; + private static readonly float[] DashDotDotPattern = new[] { 3f, 1f, 1f, 1f, 1f, 1f }; + private static readonly float[] DottedPattern = new[] { 1f, 1f }; + private static readonly float[] DashedPattern = new[] { 3f, 1f }; + /// /// Create a solid pen with out any drawing patterns /// /// The color. /// The width. + /// The type of the color. /// The Pen - public static Pen Solid(Color color, float width) => new Pen(color, width); + public static Pen Solid(TPixel color, float width) + where TPixel : struct, IPixel + => new Pen(color, width); /// /// Create a solid pen with out any drawing patterns /// /// The brush. /// The width. + /// The type of the color. /// The Pen - public static Pen Solid(IBrush brush, float width) => new Pen(brush, width); + public static Pen Solid(IBrush brush, float width) + where TPixel : struct, IPixel + => new Pen(brush, width); /// /// Create a pen with a 'Dash' drawing patterns /// /// The color. /// The width. + /// The type of the color. /// The Pen - public static Pen Dash(Color color, float width) => new Pen(Pens.Dash(color, width)); + public static Pen Dash(TPixel color, float width) + where TPixel : struct, IPixel + => new Pen(color, width, DashedPattern); /// /// Create a pen with a 'Dash' drawing patterns /// /// The brush. /// The width. + /// The type of the color. /// The Pen - public static Pen Dash(IBrush brush, float width) => new Pen(Pens.Dash(brush, width)); + public static Pen Dash(IBrush brush, float width) + where TPixel : struct, IPixel + => new Pen(brush, width, DashedPattern); /// /// Create a pen with a 'Dot' drawing patterns /// /// The color. /// The width. + /// The type of the color. /// The Pen - public static Pen Dot(Color color, float width) => new Pen(Pens.Dot(color, width)); + public static Pen Dot(TPixel color, float width) + where TPixel : struct, IPixel + => new Pen(color, width, DottedPattern); /// /// Create a pen with a 'Dot' drawing patterns /// /// The brush. /// The width. + /// The type of the color. /// The Pen - public static Pen Dot(IBrush brush, float width) => new Pen(Pens.Dot(brush, width)); + public static Pen Dot(IBrush brush, float width) + where TPixel : struct, IPixel + => new Pen(brush, width, DottedPattern); /// /// Create a pen with a 'Dash Dot' drawing patterns /// /// The color. /// The width. + /// The type of the color. /// The Pen - public static Pen DashDot(Color color, float width) => new Pen(Pens.DashDot(color, width)); + public static Pen DashDot(TPixel color, float width) + where TPixel : struct, IPixel + => new Pen(color, width, DashDotPattern); /// /// Create a pen with a 'Dash Dot' drawing patterns /// /// The brush. /// The width. + /// The type of the color. /// The Pen - public static Pen DashDot(IBrush brush, float width) => new Pen(Pens.DashDot(brush, width)); + public static Pen DashDot(IBrush brush, float width) + where TPixel : struct, IPixel + => new Pen(brush, width, DashDotPattern); /// /// Create a pen with a 'Dash Dot Dot' drawing patterns /// /// The color. /// The width. + /// The type of the color. /// The Pen - public static Pen DashDotDot(Color color, float width) => new Pen(Pens.DashDotDot(color, width)); + public static Pen DashDotDot(TPixel color, float width) + where TPixel : struct, IPixel + => new Pen(color, width, DashDotDotPattern); /// /// Create a pen with a 'Dash Dot Dot' drawing patterns /// /// The brush. /// The width. + /// The type of the color. /// The Pen - public static Pen DashDotDot(IBrush brush, float width) => new Pen(Pens.DashDotDot(brush, width)); + public static Pen DashDotDot(IBrush brush, float width) + where TPixel : struct, IPixel + => new Pen(brush, width, DashDotDotPattern); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Pens/Pens{TColor}.cs b/src/ImageSharp.Drawing/Pens/Pens{TColor}.cs deleted file mode 100644 index 49eed370df..0000000000 --- a/src/ImageSharp.Drawing/Pens/Pens{TColor}.cs +++ /dev/null @@ -1,112 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Pens -{ - using System; - - /// - /// Common Pen styles - /// - /// The type of the color. - public class Pens - where TColor : struct, IPixel - { - private static readonly float[] DashDotPattern = new[] { 3f, 1f, 1f, 1f }; - private static readonly float[] DashDotDotPattern = new[] { 3f, 1f, 1f, 1f, 1f, 1f }; - private static readonly float[] DottedPattern = new[] { 1f, 1f }; - private static readonly float[] DashedPattern = new[] { 3f, 1f }; - - /// - /// Create a solid pen with out any drawing patterns - /// - /// The color. - /// The width. - /// The Pen - public static Pen Solid(TColor color, float width) - => new Pen(color, width); - - /// - /// Create a solid pen with out any drawing patterns - /// - /// The brush. - /// The width. - /// The Pen - public static Pen Solid(IBrush brush, float width) - => new Pen(brush, width); - - /// - /// Create a pen with a 'Dash' drawing patterns - /// - /// The color. - /// The width. - /// The Pen - public static Pen Dash(TColor color, float width) - => new Pen(color, width, DashedPattern); - - /// - /// Create a pen with a 'Dash' drawing patterns - /// - /// The brush. - /// The width. - /// The Pen - public static Pen Dash(IBrush brush, float width) - => new Pen(brush, width, DashedPattern); - - /// - /// Create a pen with a 'Dot' drawing patterns - /// - /// The color. - /// The width. - /// The Pen - public static Pen Dot(TColor color, float width) - => new Pen(color, width, DottedPattern); - - /// - /// Create a pen with a 'Dot' drawing patterns - /// - /// The brush. - /// The width. - /// The Pen - public static Pen Dot(IBrush brush, float width) - => new Pen(brush, width, DottedPattern); - - /// - /// Create a pen with a 'Dash Dot' drawing patterns - /// - /// The color. - /// The width. - /// The Pen - public static Pen DashDot(TColor color, float width) - => new Pen(color, width, DashDotPattern); - - /// - /// Create a pen with a 'Dash Dot' drawing patterns - /// - /// The brush. - /// The width. - /// The Pen - public static Pen DashDot(IBrush brush, float width) - => new Pen(brush, width, DashDotPattern); - - /// - /// Create a pen with a 'Dash Dot Dot' drawing patterns - /// - /// The color. - /// The width. - /// The Pen - public static Pen DashDotDot(TColor color, float width) - => new Pen(color, width, DashDotDotPattern); - - /// - /// Create a pen with a 'Dash Dot Dot' drawing patterns - /// - /// The brush. - /// The width. - /// The Pen - public static Pen DashDotDot(IBrush brush, float width) - => new Pen(brush, width, DashDotDotPattern); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs b/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs similarity index 78% rename from src/ImageSharp.Drawing/Pens/Pen{TColor}.cs rename to src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs index e3716124e3..1da50e0d6c 100644 --- a/src/ImageSharp.Drawing/Pens/Pen{TColor}.cs +++ b/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs @@ -1,20 +1,20 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageSharp.Drawing.Pens { - using System; using System.Numerics; using ImageSharp.Drawing.Brushes; + using ImageSharp.PixelFormats; using Processors; /// /// Provides a pen that can apply a pattern to a line with a set brush and thickness /// - /// The type of the color. + /// The type of the color. /// /// The pattern will be in to the form of new float[]{ 1f, 2f, 0.5f} this will be /// converted into a pattern that is 3.5 times longer that the width with 3 sections @@ -23,30 +23,30 @@ namespace ImageSharp.Drawing.Pens /// section 3 will be width/2 long and will be filled /// the the pattern will imidiatly repeat without gap. /// - public class Pen : IPen - where TColor : struct, IPixel + public class Pen : IPen + where TPixel : struct, IPixel { private static readonly float[] EmptyPattern = new float[0]; private readonly float[] pattern; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The color. /// The width. /// The pattern. - public Pen(TColor color, float width, float[] pattern) - : this(new SolidBrush(color), width, pattern) + public Pen(TPixel color, float width, float[] pattern) + : this(new SolidBrush(color), width, pattern) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The brush. /// The width. /// The pattern. - public Pen(IBrush brush, float width, float[] pattern) + public Pen(IBrush brush, float width, float[] pattern) { this.Brush = brush; this.Width = width; @@ -54,30 +54,30 @@ namespace ImageSharp.Drawing.Pens } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The color. /// The width. - public Pen(TColor color, float width) - : this(new SolidBrush(color), width) + public Pen(TPixel color, float width) + : this(new SolidBrush(color), width) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The brush. /// The width. - public Pen(IBrush brush, float width) + public Pen(IBrush brush, float width) : this(brush, width, EmptyPattern) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The pen. - internal Pen(Pen pen) + internal Pen(Pen pen) : this(pen.Brush, pen.Width, pen.pattern) { } @@ -88,7 +88,7 @@ namespace ImageSharp.Drawing.Pens /// /// The brush. /// - public IBrush Brush { get; } + public IBrush Brush { get; } /// /// Gets the width. @@ -103,6 +103,7 @@ namespace ImageSharp.Drawing.Pens /// /// The source pixels. /// The region the pen will be applied to. + /// The Graphics options /// /// Returns a the applicator for the pen. /// @@ -110,26 +111,26 @@ namespace ImageSharp.Drawing.Pens /// The when being applied to things like shapes would ussually be the /// bounding box of the shape not necorserrally the shape of the whole image /// - public PenApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region) + public PenApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options) { if (this.pattern == null || this.pattern.Length < 2) { // if there is only one item in the pattern then 100% of it will // be solid so use the quicker applicator - return new SolidPenApplicator(sourcePixels, this.Brush, region, this.Width); + return new SolidPenApplicator(sourcePixels, this.Brush, region, this.Width, options); } - return new PatternPenApplicator(sourcePixels, this.Brush, region, this.Width, this.pattern); + return new PatternPenApplicator(sourcePixels, this.Brush, region, this.Width, this.pattern, options); } - private class SolidPenApplicator : PenApplicator + private class SolidPenApplicator : PenApplicator { - private readonly BrushApplicator brush; + private readonly BrushApplicator brush; private readonly float halfWidth; - public SolidPenApplicator(PixelAccessor sourcePixels, IBrush brush, RectangleF region, float width) + public SolidPenApplicator(PixelAccessor sourcePixels, IBrush brush, RectangleF region, float width, GraphicsOptions options) { - this.brush = brush.CreateApplicator(sourcePixels, region); + this.brush = brush.CreateApplicator(sourcePixels, region, options); this.halfWidth = width / 2; this.RequiredRegion = RectangleF.Outset(region, width); } @@ -144,9 +145,9 @@ namespace ImageSharp.Drawing.Pens this.brush.Dispose(); } - public override ColoredPointInfo GetColor(int x, int y, PointInfo info) + public override ColoredPointInfo GetColor(int x, int y, PointInfo info) { - ColoredPointInfo result = default(ColoredPointInfo); + ColoredPointInfo result = default(ColoredPointInfo); result.Color = this.brush[x, y]; if (info.DistanceFromPath < this.halfWidth) @@ -163,16 +164,16 @@ namespace ImageSharp.Drawing.Pens } } - private class PatternPenApplicator : PenApplicator + private class PatternPenApplicator : PenApplicator { - private readonly BrushApplicator brush; + private readonly BrushApplicator brush; private readonly float halfWidth; private readonly float[] pattern; private readonly float totalLength; - public PatternPenApplicator(PixelAccessor sourcePixels, IBrush brush, RectangleF region, float width, float[] pattern) + public PatternPenApplicator(PixelAccessor sourcePixels, IBrush brush, RectangleF region, float width, float[] pattern, GraphicsOptions options) { - this.brush = brush.CreateApplicator(sourcePixels, region); + this.brush = brush.CreateApplicator(sourcePixels, region, options); this.halfWidth = width / 2; this.totalLength = 0; @@ -197,16 +198,16 @@ namespace ImageSharp.Drawing.Pens this.brush.Dispose(); } - public override ColoredPointInfo GetColor(int x, int y, PointInfo info) + public override ColoredPointInfo GetColor(int x, int y, PointInfo info) { - ColoredPointInfo infoResult = default(ColoredPointInfo); + ColoredPointInfo infoResult = default(ColoredPointInfo); infoResult.DistanceFromElement = float.MaxValue; // is really outside the element float length = info.DistanceAlongPath % this.totalLength; // we can treat the DistanceAlongPath and DistanceFromPath as x,y coords for the pattern // we need to calcualte the distance from the outside edge of the pattern - // and set them on the ColoredPointInfo along with the color. + // and set them on the ColoredPointInfo along with the color. infoResult.Color = this.brush[x, y]; float distanceWAway = 0; diff --git a/src/ImageSharp.Drawing/Pens/Processors/ColoredPointInfo.cs b/src/ImageSharp.Drawing/Pens/Processors/ColoredPointInfo.cs index d042bdccbb..65a8a61319 100644 --- a/src/ImageSharp.Drawing/Pens/Processors/ColoredPointInfo.cs +++ b/src/ImageSharp.Drawing/Pens/Processors/ColoredPointInfo.cs @@ -5,19 +5,19 @@ namespace ImageSharp.Drawing.Processors { - using System; + using ImageSharp.PixelFormats; /// /// Returns details about how far away from the inside of a shape and the color the pixel could be. /// - /// The type of the color. - public struct ColoredPointInfo - where TColor : struct, IPixel + /// The type of the color. + public struct ColoredPointInfo + where TPixel : struct, IPixel { /// /// The color /// - public TColor Color; + public TPixel Color; /// /// The distance from element diff --git a/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs b/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs index 8cdb04b455..ac18890685 100644 --- a/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs +++ b/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs @@ -6,14 +6,14 @@ namespace ImageSharp.Drawing.Processors { using System; - using System.Numerics; + using ImageSharp.PixelFormats; /// /// primitive that converts a into a color and a distance away from the drawable part of the path. /// - /// The type of the color. - public abstract class PenApplicator : IDisposable - where TColor : struct, IPixel + /// The type of the color. + public abstract class PenApplicator : IDisposable + where TPixel : struct, IPixel { /// /// Gets the required region. @@ -27,7 +27,7 @@ namespace ImageSharp.Drawing.Processors public abstract void Dispose(); /// - /// Gets a from a point represented by a . + /// Gets a from a point represented by a . /// /// The x. /// The y. @@ -35,6 +35,6 @@ namespace ImageSharp.Drawing.Processors /// /// Returns the color details and distance from a solid bit of the line. /// - public abstract ColoredPointInfo GetColor(int x, int y, PointInfo info); + public abstract ColoredPointInfo GetColor(int x, int y, PointInfo info); } } diff --git a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs index c67ba0370b..ed45417fc9 100644 --- a/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawImageProcessor.cs @@ -9,40 +9,45 @@ namespace ImageSharp.Drawing.Processors using System.Numerics; using System.Threading.Tasks; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; using ImageSharp.Processing; /// /// Combines two images together by blending the pixels. /// - /// The pixel format. - internal class DrawImageProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class DrawImageProcessor : ImageProcessor + where TPixel : struct, IPixel { + private readonly PixelBlender blender; + /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The image to blend with the currently processing image. /// The size to draw the blended image. /// The location to draw the blended image. - /// The opacity of the image to blend. Between 0 and 100. - public DrawImageProcessor(Image image, Size size, Point location, int alpha = 100) + /// The opacity of the image to blend. Between 0 and 100. + public DrawImageProcessor(Image image, Size size, Point location, GraphicsOptions options) { - Guard.MustBeBetweenOrEqualTo(alpha, 0, 100, nameof(alpha)); + Guard.MustBeBetweenOrEqualTo(options.BlendPercentage, 0, 1, nameof(options.BlendPercentage)); this.Image = image; this.Size = size; - this.Alpha = alpha; + this.Alpha = options.BlendPercentage; + this.blender = PixelOperations.Instance.GetPixelBlender(options.BlenderMode); this.Location = location; } /// /// Gets the image to blend. /// - public Image Image { get; private set; } + public Image Image { get; private set; } /// /// Gets the alpha percentage value. /// - public int Alpha { get; } + public float Alpha { get; } /// /// Gets the size to draw the blended image. @@ -55,45 +60,54 @@ namespace ImageSharp.Drawing.Processors public Point Location { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - if (this.Image.Bounds.Size != this.Size) + Image disposableImage = null; + Image targetImage = this.Image; + + try { - // should Resize be moved to core? - this.Image = this.Image.Resize(this.Size.Width, this.Size.Height); - } + if (targetImage.Bounds.Size != this.Size) + { + targetImage = disposableImage = new Image(this.Image).Resize(this.Size.Width, this.Size.Height); + } - // Align start/end positions. - Rectangle bounds = this.Image.Bounds; - int minX = Math.Max(this.Location.X, sourceRectangle.X); - int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); - int minY = Math.Max(this.Location.Y, sourceRectangle.Y); - int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); + // Align start/end positions. + Rectangle bounds = this.Image.Bounds; + int minX = Math.Max(this.Location.X, sourceRectangle.X); + int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); + maxX = Math.Min(this.Location.X + this.Size.Width, maxX); - float alpha = this.Alpha / 100F; + int minY = Math.Max(this.Location.Y, sourceRectangle.Y); + int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); - using (PixelAccessor toBlendPixels = this.Image.Lock()) - using (PixelAccessor sourcePixels = source.Lock()) - { - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - for (int x = minX; x < maxX; x++) - { - Vector4 backgroundVector = sourcePixels[x, y].ToVector4(); - Vector4 sourceVector = toBlendPixels[x - minX, y - minY].ToVector4(); + maxY = Math.Min(this.Location.Y + this.Size.Height, maxY); - // Lerping colors is dependent on the alpha of the blended color - backgroundVector = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, alpha); + int width = maxX - minX; + using (Buffer amount = new Buffer(width)) + using (PixelAccessor toBlendPixels = targetImage.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) + { + for (int i = 0; i < width; i++) + { + amount[i] = this.Alpha; + } - TColor packed = default(TColor); - packed.PackFromVector4(backgroundVector); - sourcePixels[x, y] = packed; - } - }); + Parallel.For( + minY, + maxY, + this.ParallelOptions, + y => + { + Span background = sourcePixels.GetRowSpan(y).Slice(minX, width); + Span foreground = toBlendPixels.GetRowSpan(y - this.Location.Y).Slice(0, width); + this.blender.Blend(background, background, foreground, amount); + }); + } + } + finally + { + disposableImage?.Dispose(); } } } diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs index 89ba0968bb..d1332c4355 100644 --- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs @@ -9,27 +9,29 @@ namespace ImageSharp.Drawing.Processors using System.Numerics; using System.Threading.Tasks; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; using ImageSharp.Processing; using Pens; /// /// Draws a path using the processor pipeline /// - /// The type of the color. - /// - internal class DrawPathProcessor : ImageProcessor - where TColor : struct, IPixel + /// The type of the color. + /// + internal class DrawPathProcessor : ImageProcessor + where TPixel : struct, IPixel { private const float AntialiasFactor = 1f; private const int PaddingFactor = 1; // needs to been the same or greater than AntialiasFactor /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The details how to draw the outline/path. /// The details of the paths and outlines to draw. /// The drawing configuration options. - public DrawPathProcessor(IPen pen, Drawable drawable, GraphicsOptions options) + public DrawPathProcessor(IPen pen, Drawable drawable, GraphicsOptions options) { this.Path = drawable; this.Pen = pen; @@ -44,7 +46,7 @@ namespace ImageSharp.Drawing.Processors /// /// Gets the pen. /// - public IPen Pen { get; } + public IPen Pen { get; } /// /// Gets the path. @@ -52,10 +54,10 @@ namespace ImageSharp.Drawing.Processors public Drawable Path { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - using (PixelAccessor sourcePixels = source.Lock()) - using (PenApplicator applicator = this.Pen.CreateApplicator(sourcePixels, this.Path.Bounds)) + using (PixelAccessor sourcePixels = source.Lock()) + using (PenApplicator applicator = this.Pen.CreateApplicator(sourcePixels, this.Path.Bounds, this.Options)) { Rectangle rect = RectangleF.Ceiling(applicator.RequiredRegion); @@ -86,37 +88,34 @@ namespace ImageSharp.Drawing.Processors polyStartY = 0; } + int width = maxX - minX; + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(this.Options.BlenderMode); + Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - int offsetY = y - polyStartY; + minY, + maxY, + this.ParallelOptions, + y => + { + int offsetY = y - polyStartY; - for (int x = minX; x < maxX; x++) + using (Buffer amount = new Buffer(width)) + using (Buffer colors = new Buffer(width)) + { + for (int i = 0; i < width; i++) { - // TODO add find intersections code to skip and scan large regions of this. + int x = i + minX; int offsetX = x - startX; PointInfo info = this.Path.GetPointInfo(offsetX, offsetY); - - ColoredPointInfo color = applicator.GetColor(offsetX, offsetY, info); - - float opacity = this.Opacity(color.DistanceFromElement); - - if (opacity > Constants.Epsilon) - { - Vector4 backgroundVector = sourcePixels[offsetX, offsetY].ToVector4(); - Vector4 sourceVector = color.Color.ToVector4(); - - Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); - - TColor packed = default(TColor); - packed.PackFromVector4(finalColor); - sourcePixels[offsetX, offsetY] = packed; - } + ColoredPointInfo color = applicator.GetColor(offsetX, offsetY, info); + amount[i] = (this.Opacity(color.DistanceFromElement) * this.Options.BlendPercentage).Clamp(0, 1); + colors[i] = color.Color; } - }); + + Span destination = sourcePixels.GetRowSpan(offsetY).Slice(minX - startX, width); + blender.Blend(destination, destination, colors, amount); + } + }); } } diff --git a/src/ImageSharp.Drawing/Processors/FillProcessor.cs b/src/ImageSharp.Drawing/Processors/FillProcessor.cs index 635829e9f1..fa6f48156c 100644 --- a/src/ImageSharp.Drawing/Processors/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillProcessor.cs @@ -10,31 +10,37 @@ namespace ImageSharp.Drawing.Processors using System.Threading.Tasks; using Drawing; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; using ImageSharp.Processing; /// /// Using the bursh as a source of pixels colors blends the brush color with source. /// - /// The pixel format. - internal class FillProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class FillProcessor : ImageProcessor + where TPixel : struct, IPixel { /// /// The brush. /// - private readonly IBrush brush; + private readonly IBrush brush; + private readonly GraphicsOptions options; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The brush to source pixel colors from. - public FillProcessor(IBrush brush) + /// The options + public FillProcessor(IBrush brush, GraphicsOptions options) { this.brush = brush; + this.options = options; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { int startX = sourceRectangle.X; int endX = sourceRectangle.Right; @@ -58,32 +64,30 @@ namespace ImageSharp.Drawing.Processors startY = 0; } + int width = maxX - minX; + // we could possibly do some optermising by having knowledge about the individual brushes operate - // for example If brush is SolidBrush then we could just get the color upfront - // and skip using the IBrushApplicator?. - using (PixelAccessor sourcePixels = source.Lock()) - using (BrushApplicator applicator = this.brush.CreateApplicator(sourcePixels, sourceRectangle)) + // for example If brush is SolidBrush then we could just get the color upfront + // and skip using the IBrushApplicator?. + using (PixelAccessor sourcePixels = source.Lock()) + using (Buffer amount = new Buffer(width)) + using (BrushApplicator applicator = this.brush.CreateApplicator(sourcePixels, sourceRectangle, this.options)) { - Parallel.For( + for (int i = 0; i < width; i++) + { + amount[i] = this.options.BlendPercentage; + } + + Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; - for (int x = minX; x < maxX; x++) - { - int offsetX = x - startX; - - Vector4 backgroundVector = sourcePixels[offsetX, offsetY].ToVector4(); - Vector4 sourceVector = applicator[offsetX, offsetY].ToVector4(); - - Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, 1); + int offsetX = minX - startX; - TColor packed = default(TColor); - packed.PackFromVector4(finalColor); - sourcePixels[offsetX, offsetY] = packed; - } + applicator.Apply(amount, offsetX, offsetY); }); } } diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index 80a3e67932..a57be3a5a1 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -7,29 +7,30 @@ namespace ImageSharp.Drawing.Processors { using System; using System.Buffers; - using System.Numerics; - using System.Threading.Tasks; using Drawing; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; using ImageSharp.Processing; /// /// Usinf a brsuh and a shape fills shape with contents of brush the /// - /// The type of the color. - /// - internal class FillRegionProcessor : ImageProcessor - where TColor : struct, IPixel + /// The type of the color. + /// + internal class FillRegionProcessor : ImageProcessor + where TPixel : struct, IPixel { private const float AntialiasFactor = 1f; private const int DrawPadding = 1; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The details how to fill the region of interest. /// The region of interest to be filled. /// The configuration options. - public FillRegionProcessor(IBrush brush, Region region, GraphicsOptions options) + public FillRegionProcessor(IBrush brush, Region region, GraphicsOptions options) { this.Region = region; this.Brush = brush; @@ -39,7 +40,7 @@ namespace ImageSharp.Drawing.Processors /// /// Gets the brush. /// - public IBrush Brush { get; } + public IBrush Brush { get; } /// /// Gets the region that this processor applies to. @@ -55,7 +56,7 @@ namespace ImageSharp.Drawing.Processors public GraphicsOptions Options { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { Region region = this.Region; Rectangle rect = region.Bounds; @@ -88,105 +89,106 @@ namespace ImageSharp.Drawing.Processors } } - using (PixelAccessor sourcePixels = source.Lock()) - using (BrushApplicator applicator = this.Brush.CreateApplicator(sourcePixels, rect)) + using (PixelAccessor sourcePixels = source.Lock()) + using (BrushApplicator applicator = this.Brush.CreateApplicator(sourcePixels, rect, this.Options)) { float[] buffer = arrayPool.Rent(maxIntersections); int scanlineWidth = maxX - minX; - float[] scanline = ArrayPool.Shared.Rent(scanlineWidth); - try + using (Buffer scanline = new Buffer(scanlineWidth)) { - bool scanlineDirty = true; - for (int y = minY; y < maxY; y++) + try { - if (scanlineDirty) + bool scanlineDirty = true; + for (int y = minY; y < maxY; y++) { - // clear the buffer - for (int x = 0; x < scanlineWidth; x++) + if (scanlineDirty) { - scanline[x] = 0; - } - - scanlineDirty = false; - } + // clear the buffer + for (int x = 0; x < scanlineWidth; x++) + { + scanline[x] = 0; + } - float subpixelFraction = 1f / subpixelCount; - float subpixelFractionPoint = subpixelFraction / subpixelCount; - for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction) - { - int pointsFound = region.Scan(subPixel, buffer, maxIntersections, 0); - if (pointsFound == 0) - { - // nothing on this line skip - continue; + scanlineDirty = false; } - QuickSort(buffer, pointsFound); - - for (int point = 0; point < pointsFound; point += 2) + float subpixelFraction = 1f / subpixelCount; + float subpixelFractionPoint = subpixelFraction / subpixelCount; + for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction) { - // points will be paired up - float scanStart = buffer[point] - minX; - float scanEnd = buffer[point + 1] - minX; - int startX = (int)MathF.Floor(scanStart); - int endX = (int)MathF.Floor(scanEnd); + int pointsFound = region.Scan(subPixel, buffer, maxIntersections, 0); + if (pointsFound == 0) + { + // nothing on this line skip + continue; + } - if (startX >= 0 && startX < scanline.Length) + QuickSort(buffer, pointsFound); + + for (int point = 0; point < pointsFound; point += 2) { - for (float x = scanStart; x < startX + 1; x += subpixelFraction) + // points will be paired up + float scanStart = buffer[point] - minX; + float scanEnd = buffer[point + 1] - minX; + int startX = (int)MathF.Floor(scanStart); + int endX = (int)MathF.Floor(scanEnd); + + if (startX >= 0 && startX < scanline.Length) { - scanline[startX] += subpixelFractionPoint; - scanlineDirty = true; + for (float x = scanStart; x < startX + 1; x += subpixelFraction) + { + scanline[startX] += subpixelFractionPoint; + scanlineDirty = true; + } } - } - if (endX >= 0 && endX < scanline.Length) - { - for (float x = endX; x < scanEnd; x += subpixelFraction) + if (endX >= 0 && endX < scanline.Length) { - scanline[endX] += subpixelFractionPoint; - scanlineDirty = true; + for (float x = endX; x < scanEnd; x += subpixelFraction) + { + scanline[endX] += subpixelFractionPoint; + scanlineDirty = true; + } } - } - int nextX = startX + 1; - endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge - if (nextX >= 0) - { - for (int x = nextX; x < endX; x++) + int nextX = startX + 1; + endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge + if (nextX >= 0) { - scanline[x] += subpixelFraction; - scanlineDirty = true; + for (int x = nextX; x < endX; x++) + { + scanline[x] += subpixelFraction; + scanlineDirty = true; + } } } } - } - if (scanlineDirty) - { - if (!this.Options.Antialias) + if (scanlineDirty) { - for (int x = 0; x < scanlineWidth; x++) + if (!this.Options.Antialias) { - if (scanline[x] > 0.5) - { - scanline[x] = 1; - } - else + for (int x = 0; x < scanlineWidth; x++) { - scanline[x] = 0; + if (scanline[x] > 0.5) + { + scanline[x] = 1; + } + else + { + scanline[x] = 0; + } } } - } - applicator.Apply(scanline, scanlineWidth, 0, minX, y); + applicator.Apply(scanline, minX, y); + } } } - } - finally - { - arrayPool.Return(buffer); - ArrayPool.Shared.Return(scanline); + finally + { + arrayPool.Return(buffer); + } } } } diff --git a/src/ImageSharp.Drawing/Text/DrawText.cs b/src/ImageSharp.Drawing/Text/DrawText.cs index 1f5f4cdb12..bd33289fa6 100644 --- a/src/ImageSharp.Drawing/Text/DrawText.cs +++ b/src/ImageSharp.Drawing/Text/DrawText.cs @@ -10,11 +10,11 @@ namespace ImageSharp using Drawing; using Drawing.Brushes; using Drawing.Pens; - + using ImageSharp.PixelFormats; using SixLabors.Fonts; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { @@ -23,17 +23,17 @@ namespace ImageSharp /// /// Draws the text onto the the image filled via the brush. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The text. /// The font. /// The color. /// The location. /// - /// The . + /// The . /// - public static Image DrawText(this Image source, string text, Font font, TColor color, Vector2 location) - where TColor : struct, IPixel + public static Image DrawText(this Image source, string text, Font font, TPixel color, Vector2 location) + where TPixel : struct, IPixel { return source.DrawText(text, font, color, location, TextGraphicsOptions.Default); } @@ -41,7 +41,7 @@ namespace ImageSharp /// /// Draws the text onto the the image filled via the brush. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The text. /// The font. @@ -49,28 +49,28 @@ namespace ImageSharp /// The location. /// The options. /// - /// The . + /// The . /// - public static Image DrawText(this Image source, string text, Font font, TColor color, Vector2 location, TextGraphicsOptions options) - where TColor : struct, IPixel + public static Image DrawText(this Image source, string text, Font font, TPixel color, Vector2 location, TextGraphicsOptions options) + where TPixel : struct, IPixel { - return source.DrawText(text, font, Brushes.Solid(color), null, location, options); + return source.DrawText(text, font, Brushes.Solid(color), null, location, options); } /// /// Draws the text onto the the image filled via the brush. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The text. /// The font. /// The brush. /// The location. /// - /// The . + /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, Vector2 location) - where TColor : struct, IPixel + public static Image DrawText(this Image source, string text, Font font, IBrush brush, Vector2 location) + where TPixel : struct, IPixel { return source.DrawText(text, font, brush, location, TextGraphicsOptions.Default); } @@ -78,7 +78,7 @@ namespace ImageSharp /// /// Draws the text onto the the image filled via the brush. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The text. /// The font. @@ -86,10 +86,10 @@ namespace ImageSharp /// The location. /// The options. /// - /// The . + /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, Vector2 location, TextGraphicsOptions options) - where TColor : struct, IPixel + public static Image DrawText(this Image source, string text, Font font, IBrush brush, Vector2 location, TextGraphicsOptions options) + where TPixel : struct, IPixel { return source.DrawText(text, font, brush, null, location, options); } @@ -97,17 +97,17 @@ namespace ImageSharp /// /// Draws the text onto the the image outlined via the pen. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The text. /// The font. /// The pen. /// The location. /// - /// The . + /// The . /// - public static Image DrawText(this Image source, string text, Font font, IPen pen, Vector2 location) - where TColor : struct, IPixel + public static Image DrawText(this Image source, string text, Font font, IPen pen, Vector2 location) + where TPixel : struct, IPixel { return source.DrawText(text, font, pen, location, TextGraphicsOptions.Default); } @@ -115,7 +115,7 @@ namespace ImageSharp /// /// Draws the text onto the the image outlined via the pen. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The text. /// The font. @@ -123,10 +123,10 @@ namespace ImageSharp /// The location. /// The options. /// - /// The . + /// The . /// - public static Image DrawText(this Image source, string text, Font font, IPen pen, Vector2 location, TextGraphicsOptions options) - where TColor : struct, IPixel + public static Image DrawText(this Image source, string text, Font font, IPen pen, Vector2 location, TextGraphicsOptions options) + where TPixel : struct, IPixel { return source.DrawText(text, font, null, pen, location, options); } @@ -134,7 +134,7 @@ namespace ImageSharp /// /// Draws the text onto the the image filled via the brush then outlined via the pen. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The text. /// The font. @@ -142,10 +142,10 @@ namespace ImageSharp /// The pen. /// The location. /// - /// The . + /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, Vector2 location) - where TColor : struct, IPixel + public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, Vector2 location) + where TPixel : struct, IPixel { return source.DrawText(text, font, brush, pen, location, TextGraphicsOptions.Default); } @@ -153,7 +153,7 @@ namespace ImageSharp /// /// Draws the text onto the the image filled via the brush then outlined via the pen. /// - /// The type of the color. + /// The type of the color. /// The image this method extends. /// The text. /// The font. @@ -162,10 +162,10 @@ namespace ImageSharp /// The location. /// The options. /// - /// The . + /// The . /// - public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, Vector2 location, TextGraphicsOptions options) - where TColor : struct, IPixel + public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, Vector2 location, TextGraphicsOptions options) + where TPixel : struct, IPixel { GlyphBuilder glyphBuilder = new GlyphBuilder(location); @@ -177,13 +177,15 @@ namespace ImageSharp dpi = new Vector2((float)source.MetaData.HorizontalResolution, (float)source.MetaData.VerticalResolution); } - FontSpan style = new FontSpan(font) + FontSpan style = new FontSpan(font, dpi) { ApplyKerning = options.ApplyKerning, - TabWidth = options.TabWidth + TabWidth = options.TabWidth, + WrappingWidth = options.WrapTextWidth, + Alignment = options.TextAlignment }; - renderer.RenderText(text, style, dpi); + renderer.RenderText(text, style); System.Collections.Generic.IEnumerable shapesToDraw = glyphBuilder.Paths; diff --git a/src/ImageSharp.Drawing/Text/GlyphBuilder.cs b/src/ImageSharp.Drawing/Text/GlyphBuilder.cs index ac5d01de72..0033a608c3 100644 --- a/src/ImageSharp.Drawing/Text/GlyphBuilder.cs +++ b/src/ImageSharp.Drawing/Text/GlyphBuilder.cs @@ -48,7 +48,8 @@ namespace ImageSharp.Drawing /// /// Begins the glyph. /// - void IGlyphRenderer.BeginGlyph() + /// The offset that the glyph will be rendered at. + void IGlyphRenderer.BeginGlyph(Vector2 location) { this.builder.Clear(); } diff --git a/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs b/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs index b58a40b34d..388b39bcc5 100644 --- a/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs +++ b/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs @@ -2,9 +2,11 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // - namespace ImageSharp.Drawing { + using ImageSharp.PixelFormats; + using SixLabors.Fonts; + /// /// Options for influencing the drawing functions. /// @@ -15,44 +17,94 @@ namespace ImageSharp.Drawing /// public static readonly TextGraphicsOptions Default = new TextGraphicsOptions(true); + private float? blendPercentage; + + private int? antialiasSubpixelDepth; + + private bool? antialias; + + private bool? applyKerning; + + private float? tabWidth; + + private PixelBlenderMode blenderMode; + + private bool? useImageResolution; + + private float wrapTextWidth; + + private SixLabors.Fonts.TextAlignment? textAlignment; + /// - /// Whether antialiasing should be applied. + /// Initializes a new instance of the struct. /// - public bool Antialias; + /// If set to true [enable antialiasing]. + public TextGraphicsOptions(bool enableAntialiasing) + { + this.applyKerning = true; + this.tabWidth = 4; + this.useImageResolution = false; + this.wrapTextWidth = 0; + this.textAlignment = SixLabors.Fonts.TextAlignment.Left; + + this.antialiasSubpixelDepth = 16; + this.blenderMode = PixelBlenderMode.Normal; + this.blendPercentage = 1; + this.antialias = enableAntialiasing; + } /// - /// The number of subpixels to use while rendering with antialiasing enabled. + /// Gets or sets a value indicating whether antialiasing should be applied. /// - public int AntialiasSubpixelDepth; + public bool Antialias { get => this.antialias ?? true; set => this.antialias = value; } /// - /// Whether the text should be drawing with kerning enabled. + /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. /// - public bool ApplyKerning; + public int AntialiasSubpixelDepth { get => this.antialiasSubpixelDepth ?? 16; set => this.antialiasSubpixelDepth = value; } /// - /// The number of space widths a tab should lock to. + /// Gets or sets a value indicating the blending percentage to apply to the drawing operation /// - public float TabWidth; + public float BlendPercentage { get => (this.blendPercentage ?? 1).Clamp(0, 1); set => this.blendPercentage = value; } + + // In the future we could expose a PixelBlender directly on here + // or some forms of PixelBlender factory for each pixel type. Will need + // some API thought post V1. /// - /// Flag weather to use the current image resultion to for point size scaling. + /// Gets or sets a value indicating the blending percentage to apply to the drawing operation + /// + public PixelBlenderMode BlenderMode { get => this.blenderMode; set => this.blenderMode = value; } + + /// + /// Gets or sets a value indicating whether the text should be drawing with kerning enabled. + /// + public bool ApplyKerning { get => this.applyKerning ?? true; set => this.applyKerning = value; } + + /// + /// Gets or sets a value indicating the number of space widths a tab should lock to. + /// + public float TabWidth { get => this.tabWidth ?? 4; set => this.tabWidth = value; } + + /// + /// Gets or sets a value indicating whether to use the current image resultion to for point size scaling. /// If this is [false] the text renders at 72dpi otherwise it renders at Image resolution /// - public bool UseImageResolution; + public bool UseImageResolution { get => this.useImageResolution ?? false; set => this.useImageResolution = value; } /// - /// Initializes a new instance of the struct. + /// Gets or sets a value indicating if greater than zero determine the width at which text should wrap. /// - /// If set to true [enable antialiasing]. - public TextGraphicsOptions(bool enableAntialiasing) - { - this.Antialias = enableAntialiasing; - this.ApplyKerning = true; - this.TabWidth = 4; - this.AntialiasSubpixelDepth = 16; - this.UseImageResolution = false; - } + public float WrapTextWidth { get => this.wrapTextWidth; set => this.wrapTextWidth = value; } + + /// + /// Gets or sets a value indicating how to align the text relative to the rendering space. + /// If is greater than zero it will align relative to the space + /// defined by the location and width, if equals zero, and thus + /// wrapping disabled, then the alignment is relative to the drawing location. + /// + public TextAlignment TextAlignment { get => this.textAlignment ?? TextAlignment.Left; set => this.textAlignment = value; } /// /// Performs an implicit conversion from to . @@ -65,7 +117,9 @@ namespace ImageSharp.Drawing { return new TextGraphicsOptions(options.Antialias) { - AntialiasSubpixelDepth = options.AntialiasSubpixelDepth + AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, + blendPercentage = options.BlendPercentage, + blenderMode = options.BlenderMode }; } @@ -80,7 +134,9 @@ namespace ImageSharp.Drawing { return new GraphicsOptions(options.Antialias) { - AntialiasSubpixelDepth = options.AntialiasSubpixelDepth + AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, + BlenderMode = options.BlenderMode, + BlendPercentage = options.BlendPercentage }; } } diff --git a/src/ImageSharp/Colors/Color.BulkOperations.cs b/src/ImageSharp/Colors/Color.BulkOperations.cs deleted file mode 100644 index 7d3b12ea0b..0000000000 --- a/src/ImageSharp/Colors/Color.BulkOperations.cs +++ /dev/null @@ -1,255 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - /// - /// Conains the definition of - /// - public partial struct Color - { - /// - /// implementation optimized for . - /// - internal class BulkOperations : BulkPixelOperations - { - /// - /// SIMD optimized bulk implementation of - /// that works only with `count` divisible by . - /// - /// The to the source colors. - /// The to the dstination vectors. - /// The number of pixels to convert. - /// - /// Implementation adapted from: - /// - /// http://stackoverflow.com/a/5362789 - /// - /// TODO: We can replace this implementation in the future using new Vector API-s: - /// - /// https://github.com/dotnet/corefx/issues/15957 - /// - /// - internal static unsafe void ToVector4SimdAligned( - BufferSpan sourceColors, - BufferSpan destVectors, - int count) - { - if (!Vector.IsHardwareAccelerated) - { - throw new InvalidOperationException( - "Color.BulkOperations.ToVector4SimdAligned() should not be called when Vector.IsHardwareAccelerated == false!"); - } - - int vecSize = Vector.Count; - - DebugGuard.IsTrue( - count % vecSize == 0, - nameof(count), - "Argument 'count' should divisible by Vector.Count!"); - - Vector bVec = new Vector(256.0f / 255.0f); - Vector magicFloat = new Vector(32768.0f); - Vector magicInt = new Vector(1191182336); // reinterpreded value of 32768.0f - Vector mask = new Vector(255); - - int unpackedRawCount = count * 4; - - uint* src = (uint*)sourceColors.PointerAtOffset; - uint* srcEnd = src + count; - - using (PinnedBuffer tempBuf = new PinnedBuffer( - unpackedRawCount + Vector.Count)) - { - uint* tPtr = (uint*)tempBuf.Pointer; - uint[] temp = tempBuf.Array; - float[] fTemp = Unsafe.As(temp); - UnpackedRGBA* dst = (UnpackedRGBA*)tPtr; - - for (; src < srcEnd; src++, dst++) - { - // This call is the bottleneck now: - dst->Load(*src); - } - - for (int i = 0; i < unpackedRawCount; i += vecSize) - { - Vector vi = new Vector(temp, i); - - vi &= mask; - vi |= magicInt; - - Vector vf = Vector.AsVectorSingle(vi); - vf = (vf - magicFloat) * bVec; - vf.CopyTo(fTemp, i); - } - - BufferSpan.Copy(tempBuf, (BufferSpan)destVectors, unpackedRawCount); - } - } - - /// - internal override void ToVector4(BufferSpan sourceColors, BufferSpan destVectors, int count) - { - if (count < 256 || !Vector.IsHardwareAccelerated) - { - // Doesn't worth to bother with SIMD: - base.ToVector4(sourceColors, destVectors, count); - return; - } - - int remainder = count % Vector.Count; - - int alignedCount = count - remainder; - - if (alignedCount > 0) - { - ToVector4SimdAligned(sourceColors, destVectors, alignedCount); - } - - if (remainder > 0) - { - sourceColors = sourceColors.Slice(alignedCount); - destVectors = destVectors.Slice(alignedCount); - base.ToVector4(sourceColors, destVectors, remainder); - } - } - - /// - internal override unsafe void PackFromXyzBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) - { - byte* source = (byte*)sourceBytes; - byte* destination = (byte*)destColors; - - for (int x = 0; x < count; x++) - { - Unsafe.Write(destination, (uint)(*source << 0 | *(source + 1) << 8 | *(source + 2) << 16 | 255 << 24)); - - source += 3; - destination += 4; - } - } - - /// - internal override unsafe void ToXyzBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) - { - byte* source = (byte*)sourceColors; - byte* destination = (byte*)destBytes; - - for (int x = 0; x < count; x++) - { - *destination = *(source + 0); - *(destination + 1) = *(source + 1); - *(destination + 2) = *(source + 2); - - source += 4; - destination += 3; - } - } - - /// - internal override void PackFromXyzwBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) - { - BufferSpan.Copy(sourceBytes, destColors, count); - } - - /// - internal override void ToXyzwBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) - { - BufferSpan.Copy(sourceColors, destBytes, count); - } - - /// - internal override unsafe void PackFromZyxBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) - { - byte* source = (byte*)sourceBytes; - byte* destination = (byte*)destColors; - - for (int x = 0; x < count; x++) - { - Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | 255 << 24)); - - source += 3; - destination += 4; - } - } - - /// - internal override unsafe void ToZyxBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) - { - byte* source = (byte*)sourceColors; - byte* destination = (byte*)destBytes; - - for (int x = 0; x < count; x++) - { - *destination = *(source + 2); - *(destination + 1) = *(source + 1); - *(destination + 2) = *(source + 0); - - source += 4; - destination += 3; - } - } - - /// - internal override unsafe void PackFromZyxwBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) - { - byte* source = (byte*)sourceBytes; - byte* destination = (byte*)destColors; - - for (int x = 0; x < count; x++) - { - Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | *(source + 3) << 24)); - - source += 4; - destination += 4; - } - } - - /// - internal override unsafe void ToZyxwBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) - { - byte* source = (byte*)sourceColors; - byte* destination = (byte*)destBytes; - - for (int x = 0; x < count; x++) - { - *destination = *(source + 2); - *(destination + 1) = *(source + 1); - *(destination + 2) = *(source + 0); - *(destination + 3) = *(source + 3); - - source += 4; - destination += 4; - } - } - - /// - /// Value type to store -s unpacked into multiple -s. - /// - private struct UnpackedRGBA - { - private uint r; - private uint g; - private uint b; - private uint a; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Load(uint p) - { - this.r = p; - this.g = p >> Color.GreenShift; - this.b = p >> Color.BlueShift; - this.a = p >> Color.AlphaShift; - } - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Colors/ColorConstants.cs b/src/ImageSharp/Colors/ColorConstants.cs deleted file mode 100644 index 1397b6da6f..0000000000 --- a/src/ImageSharp/Colors/ColorConstants.cs +++ /dev/null @@ -1,179 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Collections.Generic; - - /// - /// Provides useful color definitions. - /// - public static class ColorConstants - { - /// - /// Provides a lazy, one time method of returning the colors. - /// - private static readonly Lazy SafeColors = new Lazy(GetWebSafeColors); - - /// - /// Gets a collection of named, web safe, colors as defined in the CSS Color Module Level 4. - /// - public static Color[] WebSafeColors => SafeColors.Value; - - /// - /// Returns an array of web safe colors. - /// - /// The - private static Color[] GetWebSafeColors() - { - return new List - { - Color.AliceBlue, - Color.AntiqueWhite, - Color.Aqua, - Color.Aquamarine, - Color.Azure, - Color.Beige, - Color.Bisque, - Color.Black, - Color.BlanchedAlmond, - Color.Blue, - Color.BlueViolet, - Color.Brown, - Color.BurlyWood, - Color.CadetBlue, - Color.Chartreuse, - Color.Chocolate, - Color.Coral, - Color.CornflowerBlue, - Color.Cornsilk, - Color.Crimson, - Color.Cyan, - Color.DarkBlue, - Color.DarkCyan, - Color.DarkGoldenrod, - Color.DarkGray, - Color.DarkGreen, - Color.DarkKhaki, - Color.DarkMagenta, - Color.DarkOliveGreen, - Color.DarkOrange, - Color.DarkOrchid, - Color.DarkRed, - Color.DarkSalmon, - Color.DarkSeaGreen, - Color.DarkSlateBlue, - Color.DarkSlateGray, - Color.DarkTurquoise, - Color.DarkViolet, - Color.DeepPink, - Color.DeepSkyBlue, - Color.DimGray, - Color.DodgerBlue, - Color.Firebrick, - Color.FloralWhite, - Color.ForestGreen, - Color.Fuchsia, - Color.Gainsboro, - Color.GhostWhite, - Color.Gold, - Color.Goldenrod, - Color.Gray, - Color.Green, - Color.GreenYellow, - Color.Honeydew, - Color.HotPink, - Color.IndianRed, - Color.Indigo, - Color.Ivory, - Color.Khaki, - Color.Lavender, - Color.LavenderBlush, - Color.LawnGreen, - Color.LemonChiffon, - Color.LightBlue, - Color.LightCoral, - Color.LightCyan, - Color.LightGoldenrodYellow, - Color.LightGray, - Color.LightGreen, - Color.LightPink, - Color.LightSalmon, - Color.LightSeaGreen, - Color.LightSkyBlue, - Color.LightSlateGray, - Color.LightSteelBlue, - Color.LightYellow, - Color.Lime, - Color.LimeGreen, - Color.Linen, - Color.Magenta, - Color.Maroon, - Color.MediumAquamarine, - Color.MediumBlue, - Color.MediumOrchid, - Color.MediumPurple, - Color.MediumSeaGreen, - Color.MediumSlateBlue, - Color.MediumSpringGreen, - Color.MediumTurquoise, - Color.MediumVioletRed, - Color.MidnightBlue, - Color.MintCream, - Color.MistyRose, - Color.Moccasin, - Color.NavajoWhite, - Color.Navy, - Color.OldLace, - Color.Olive, - Color.OliveDrab, - Color.Orange, - Color.OrangeRed, - Color.Orchid, - Color.PaleGoldenrod, - Color.PaleGreen, - Color.PaleTurquoise, - Color.PaleVioletRed, - Color.PapayaWhip, - Color.PeachPuff, - Color.Peru, - Color.Pink, - Color.Plum, - Color.PowderBlue, - Color.Purple, - Color.RebeccaPurple, - Color.Red, - Color.RosyBrown, - Color.RoyalBlue, - Color.SaddleBrown, - Color.Salmon, - Color.SandyBrown, - Color.SeaGreen, - Color.SeaShell, - Color.Sienna, - Color.Silver, - Color.SkyBlue, - Color.SlateBlue, - Color.SlateGray, - Color.Snow, - Color.SpringGreen, - Color.SteelBlue, - Color.Tan, - Color.Teal, - Color.Thistle, - Color.Tomato, - Color.Transparent, - Color.Turquoise, - Color.Violet, - Color.Wheat, - Color.White, - Color.WhiteSmoke, - Color.Yellow, - Color.YellowGreen - }.ToArray(); - } - } -} diff --git a/src/ImageSharp/Colors/ColorDefinitions.cs b/src/ImageSharp/Colors/ColorDefinitions.cs deleted file mode 100644 index 65165289df..0000000000 --- a/src/ImageSharp/Colors/ColorDefinitions.cs +++ /dev/null @@ -1,728 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - /// - /// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255. - /// The color components are stored in red, green, blue, and alpha order. - /// - /// - /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, - /// as it avoids the need to create new values for modification operations. - /// - public partial struct Color - { - /// - /// Represents a matching the W3C definition that has an hex value of #F0F8FF. - /// - public static readonly Color AliceBlue = NamedColors.AliceBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #FAEBD7. - /// - public static readonly Color AntiqueWhite = NamedColors.AntiqueWhite; - - /// - /// Represents a matching the W3C definition that has an hex value of #00FFFF. - /// - public static readonly Color Aqua = NamedColors.Aqua; - - /// - /// Represents a matching the W3C definition that has an hex value of #7FFFD4. - /// - public static readonly Color Aquamarine = NamedColors.Aquamarine; - - /// - /// Represents a matching the W3C definition that has an hex value of #F0FFFF. - /// - public static readonly Color Azure = NamedColors.Azure; - - /// - /// Represents a matching the W3C definition that has an hex value of #F5F5DC. - /// - public static readonly Color Beige = NamedColors.Beige; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFE4C4. - /// - public static readonly Color Bisque = NamedColors.Bisque; - - /// - /// Represents a matching the W3C definition that has an hex value of #000000. - /// - public static readonly Color Black = NamedColors.Black; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFEBCD. - /// - public static readonly Color BlanchedAlmond = NamedColors.BlanchedAlmond; - - /// - /// Represents a matching the W3C definition that has an hex value of #0000FF. - /// - public static readonly Color Blue = NamedColors.Blue; - - /// - /// Represents a matching the W3C definition that has an hex value of #8A2BE2. - /// - public static readonly Color BlueViolet = NamedColors.BlueViolet; - - /// - /// Represents a matching the W3C definition that has an hex value of #A52A2A. - /// - public static readonly Color Brown = NamedColors.Brown; - - /// - /// Represents a matching the W3C definition that has an hex value of #DEB887. - /// - public static readonly Color BurlyWood = NamedColors.BurlyWood; - - /// - /// Represents a matching the W3C definition that has an hex value of #5F9EA0. - /// - public static readonly Color CadetBlue = NamedColors.CadetBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #7FFF00. - /// - public static readonly Color Chartreuse = NamedColors.Chartreuse; - - /// - /// Represents a matching the W3C definition that has an hex value of #D2691E. - /// - public static readonly Color Chocolate = NamedColors.Chocolate; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF7F50. - /// - public static readonly Color Coral = NamedColors.Coral; - - /// - /// Represents a matching the W3C definition that has an hex value of #6495ED. - /// - public static readonly Color CornflowerBlue = NamedColors.CornflowerBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFF8DC. - /// - public static readonly Color Cornsilk = NamedColors.Cornsilk; - - /// - /// Represents a matching the W3C definition that has an hex value of #DC143C. - /// - public static readonly Color Crimson = NamedColors.Crimson; - - /// - /// Represents a matching the W3C definition that has an hex value of #00FFFF. - /// - public static readonly Color Cyan = NamedColors.Cyan; - - /// - /// Represents a matching the W3C definition that has an hex value of #00008B. - /// - public static readonly Color DarkBlue = NamedColors.DarkBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #008B8B. - /// - public static readonly Color DarkCyan = NamedColors.DarkCyan; - - /// - /// Represents a matching the W3C definition that has an hex value of #B8860B. - /// - public static readonly Color DarkGoldenrod = NamedColors.DarkGoldenrod; - - /// - /// Represents a matching the W3C definition that has an hex value of #A9A9A9. - /// - public static readonly Color DarkGray = NamedColors.DarkGray; - - /// - /// Represents a matching the W3C definition that has an hex value of #006400. - /// - public static readonly Color DarkGreen = NamedColors.DarkGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #BDB76B. - /// - public static readonly Color DarkKhaki = NamedColors.DarkKhaki; - - /// - /// Represents a matching the W3C definition that has an hex value of #8B008B. - /// - public static readonly Color DarkMagenta = NamedColors.DarkMagenta; - - /// - /// Represents a matching the W3C definition that has an hex value of #556B2F. - /// - public static readonly Color DarkOliveGreen = NamedColors.DarkOliveGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF8C00. - /// - public static readonly Color DarkOrange = NamedColors.DarkOrange; - - /// - /// Represents a matching the W3C definition that has an hex value of #9932CC. - /// - public static readonly Color DarkOrchid = NamedColors.DarkOrchid; - - /// - /// Represents a matching the W3C definition that has an hex value of #8B0000. - /// - public static readonly Color DarkRed = NamedColors.DarkRed; - - /// - /// Represents a matching the W3C definition that has an hex value of #E9967A. - /// - public static readonly Color DarkSalmon = NamedColors.DarkSalmon; - - /// - /// Represents a matching the W3C definition that has an hex value of #8FBC8B. - /// - public static readonly Color DarkSeaGreen = NamedColors.DarkSeaGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #483D8B. - /// - public static readonly Color DarkSlateBlue = NamedColors.DarkSlateBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #2F4F4F. - /// - public static readonly Color DarkSlateGray = NamedColors.DarkSlateGray; - - /// - /// Represents a matching the W3C definition that has an hex value of #00CED1. - /// - public static readonly Color DarkTurquoise = NamedColors.DarkTurquoise; - - /// - /// Represents a matching the W3C definition that has an hex value of #9400D3. - /// - public static readonly Color DarkViolet = NamedColors.DarkViolet; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF1493. - /// - public static readonly Color DeepPink = NamedColors.DeepPink; - - /// - /// Represents a matching the W3C definition that has an hex value of #00BFFF. - /// - public static readonly Color DeepSkyBlue = NamedColors.DeepSkyBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #696969. - /// - public static readonly Color DimGray = NamedColors.DimGray; - - /// - /// Represents a matching the W3C definition that has an hex value of #1E90FF. - /// - public static readonly Color DodgerBlue = NamedColors.DodgerBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #B22222. - /// - public static readonly Color Firebrick = NamedColors.Firebrick; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFAF0. - /// - public static readonly Color FloralWhite = NamedColors.FloralWhite; - - /// - /// Represents a matching the W3C definition that has an hex value of #228B22. - /// - public static readonly Color ForestGreen = NamedColors.ForestGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF00FF. - /// - public static readonly Color Fuchsia = NamedColors.Fuchsia; - - /// - /// Represents a matching the W3C definition that has an hex value of #DCDCDC. - /// - public static readonly Color Gainsboro = NamedColors.Gainsboro; - - /// - /// Represents a matching the W3C definition that has an hex value of #F8F8FF. - /// - public static readonly Color GhostWhite = NamedColors.GhostWhite; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFD700. - /// - public static readonly Color Gold = NamedColors.Gold; - - /// - /// Represents a matching the W3C definition that has an hex value of #DAA520. - /// - public static readonly Color Goldenrod = NamedColors.Goldenrod; - - /// - /// Represents a matching the W3C definition that has an hex value of #808080. - /// - public static readonly Color Gray = NamedColors.Gray; - - /// - /// Represents a matching the W3C definition that has an hex value of #008000. - /// - public static readonly Color Green = NamedColors.Green; - - /// - /// Represents a matching the W3C definition that has an hex value of #ADFF2F. - /// - public static readonly Color GreenYellow = NamedColors.GreenYellow; - - /// - /// Represents a matching the W3C definition that has an hex value of #F0FFF0. - /// - public static readonly Color Honeydew = NamedColors.Honeydew; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF69B4. - /// - public static readonly Color HotPink = NamedColors.HotPink; - - /// - /// Represents a matching the W3C definition that has an hex value of #CD5C5C. - /// - public static readonly Color IndianRed = NamedColors.IndianRed; - - /// - /// Represents a matching the W3C definition that has an hex value of #4B0082. - /// - public static readonly Color Indigo = NamedColors.Indigo; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFFF0. - /// - public static readonly Color Ivory = NamedColors.Ivory; - - /// - /// Represents a matching the W3C definition that has an hex value of #F0E68C. - /// - public static readonly Color Khaki = NamedColors.Khaki; - - /// - /// Represents a matching the W3C definition that has an hex value of #E6E6FA. - /// - public static readonly Color Lavender = NamedColors.Lavender; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFF0F5. - /// - public static readonly Color LavenderBlush = NamedColors.LavenderBlush; - - /// - /// Represents a matching the W3C definition that has an hex value of #7CFC00. - /// - public static readonly Color LawnGreen = NamedColors.LawnGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFACD. - /// - public static readonly Color LemonChiffon = NamedColors.LemonChiffon; - - /// - /// Represents a matching the W3C definition that has an hex value of #ADD8E6. - /// - public static readonly Color LightBlue = NamedColors.LightBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #F08080. - /// - public static readonly Color LightCoral = NamedColors.LightCoral; - - /// - /// Represents a matching the W3C definition that has an hex value of #E0FFFF. - /// - public static readonly Color LightCyan = NamedColors.LightCyan; - - /// - /// Represents a matching the W3C definition that has an hex value of #FAFAD2. - /// - public static readonly Color LightGoldenrodYellow = NamedColors.LightGoldenrodYellow; - - /// - /// Represents a matching the W3C definition that has an hex value of #D3D3D3. - /// - public static readonly Color LightGray = NamedColors.LightGray; - - /// - /// Represents a matching the W3C definition that has an hex value of #90EE90. - /// - public static readonly Color LightGreen = NamedColors.LightGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFB6C1. - /// - public static readonly Color LightPink = NamedColors.LightPink; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFA07A. - /// - public static readonly Color LightSalmon = NamedColors.LightSalmon; - - /// - /// Represents a matching the W3C definition that has an hex value of #20B2AA. - /// - public static readonly Color LightSeaGreen = NamedColors.LightSeaGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #87CEFA. - /// - public static readonly Color LightSkyBlue = NamedColors.LightSkyBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #778899. - /// - public static readonly Color LightSlateGray = NamedColors.LightSlateGray; - - /// - /// Represents a matching the W3C definition that has an hex value of #B0C4DE. - /// - public static readonly Color LightSteelBlue = NamedColors.LightSteelBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFFE0. - /// - public static readonly Color LightYellow = NamedColors.LightYellow; - - /// - /// Represents a matching the W3C definition that has an hex value of #00FF00. - /// - public static readonly Color Lime = NamedColors.Lime; - - /// - /// Represents a matching the W3C definition that has an hex value of #32CD32. - /// - public static readonly Color LimeGreen = NamedColors.LimeGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #FAF0E6. - /// - public static readonly Color Linen = NamedColors.Linen; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF00FF. - /// - public static readonly Color Magenta = NamedColors.Magenta; - - /// - /// Represents a matching the W3C definition that has an hex value of #800000. - /// - public static readonly Color Maroon = NamedColors.Maroon; - - /// - /// Represents a matching the W3C definition that has an hex value of #66CDAA. - /// - public static readonly Color MediumAquamarine = NamedColors.MediumAquamarine; - - /// - /// Represents a matching the W3C definition that has an hex value of #0000CD. - /// - public static readonly Color MediumBlue = NamedColors.MediumBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #BA55D3. - /// - public static readonly Color MediumOrchid = NamedColors.MediumOrchid; - - /// - /// Represents a matching the W3C definition that has an hex value of #9370DB. - /// - public static readonly Color MediumPurple = NamedColors.MediumPurple; - - /// - /// Represents a matching the W3C definition that has an hex value of #3CB371. - /// - public static readonly Color MediumSeaGreen = NamedColors.MediumSeaGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #7B68EE. - /// - public static readonly Color MediumSlateBlue = NamedColors.MediumSlateBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #00FA9A. - /// - public static readonly Color MediumSpringGreen = NamedColors.MediumSpringGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #48D1CC. - /// - public static readonly Color MediumTurquoise = NamedColors.MediumTurquoise; - - /// - /// Represents a matching the W3C definition that has an hex value of #C71585. - /// - public static readonly Color MediumVioletRed = NamedColors.MediumVioletRed; - - /// - /// Represents a matching the W3C definition that has an hex value of #191970. - /// - public static readonly Color MidnightBlue = NamedColors.MidnightBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #F5FFFA. - /// - public static readonly Color MintCream = NamedColors.MintCream; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFE4E1. - /// - public static readonly Color MistyRose = NamedColors.MistyRose; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFE4B5. - /// - public static readonly Color Moccasin = NamedColors.Moccasin; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFDEAD. - /// - public static readonly Color NavajoWhite = NamedColors.NavajoWhite; - - /// - /// Represents a matching the W3C definition that has an hex value of #000080. - /// - public static readonly Color Navy = NamedColors.Navy; - - /// - /// Represents a matching the W3C definition that has an hex value of #FDF5E6. - /// - public static readonly Color OldLace = NamedColors.OldLace; - - /// - /// Represents a matching the W3C definition that has an hex value of #808000. - /// - public static readonly Color Olive = NamedColors.Olive; - - /// - /// Represents a matching the W3C definition that has an hex value of #6B8E23. - /// - public static readonly Color OliveDrab = NamedColors.OliveDrab; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFA500. - /// - public static readonly Color Orange = NamedColors.Orange; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF4500. - /// - public static readonly Color OrangeRed = NamedColors.OrangeRed; - - /// - /// Represents a matching the W3C definition that has an hex value of #DA70D6. - /// - public static readonly Color Orchid = NamedColors.Orchid; - - /// - /// Represents a matching the W3C definition that has an hex value of #EEE8AA. - /// - public static readonly Color PaleGoldenrod = NamedColors.PaleGoldenrod; - - /// - /// Represents a matching the W3C definition that has an hex value of #98FB98. - /// - public static readonly Color PaleGreen = NamedColors.PaleGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #AFEEEE. - /// - public static readonly Color PaleTurquoise = NamedColors.PaleTurquoise; - - /// - /// Represents a matching the W3C definition that has an hex value of #DB7093. - /// - public static readonly Color PaleVioletRed = NamedColors.PaleVioletRed; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFEFD5. - /// - public static readonly Color PapayaWhip = NamedColors.PapayaWhip; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFDAB9. - /// - public static readonly Color PeachPuff = NamedColors.PeachPuff; - - /// - /// Represents a matching the W3C definition that has an hex value of #CD853F. - /// - public static readonly Color Peru = NamedColors.Peru; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFC0CB. - /// - public static readonly Color Pink = NamedColors.Pink; - - /// - /// Represents a matching the W3C definition that has an hex value of #DDA0DD. - /// - public static readonly Color Plum = NamedColors.Plum; - - /// - /// Represents a matching the W3C definition that has an hex value of #B0E0E6. - /// - public static readonly Color PowderBlue = NamedColors.PowderBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #800080. - /// - public static readonly Color Purple = NamedColors.Purple; - - /// - /// Represents a matching the W3C definition that has an hex value of #663399. - /// - public static readonly Color RebeccaPurple = NamedColors.RebeccaPurple; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF0000. - /// - public static readonly Color Red = NamedColors.Red; - - /// - /// Represents a matching the W3C definition that has an hex value of #BC8F8F. - /// - public static readonly Color RosyBrown = NamedColors.RosyBrown; - - /// - /// Represents a matching the W3C definition that has an hex value of #4169E1. - /// - public static readonly Color RoyalBlue = NamedColors.RoyalBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #8B4513. - /// - public static readonly Color SaddleBrown = NamedColors.SaddleBrown; - - /// - /// Represents a matching the W3C definition that has an hex value of #FA8072. - /// - public static readonly Color Salmon = NamedColors.Salmon; - - /// - /// Represents a matching the W3C definition that has an hex value of #F4A460. - /// - public static readonly Color SandyBrown = NamedColors.SandyBrown; - - /// - /// Represents a matching the W3C definition that has an hex value of #2E8B57. - /// - public static readonly Color SeaGreen = NamedColors.SeaGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFF5EE. - /// - public static readonly Color SeaShell = NamedColors.SeaShell; - - /// - /// Represents a matching the W3C definition that has an hex value of #A0522D. - /// - public static readonly Color Sienna = NamedColors.Sienna; - - /// - /// Represents a matching the W3C definition that has an hex value of #C0C0C0. - /// - public static readonly Color Silver = NamedColors.Silver; - - /// - /// Represents a matching the W3C definition that has an hex value of #87CEEB. - /// - public static readonly Color SkyBlue = NamedColors.SkyBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #6A5ACD. - /// - public static readonly Color SlateBlue = NamedColors.SlateBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #708090. - /// - public static readonly Color SlateGray = NamedColors.SlateGray; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFAFA. - /// - public static readonly Color Snow = NamedColors.Snow; - - /// - /// Represents a matching the W3C definition that has an hex value of #00FF7F. - /// - public static readonly Color SpringGreen = NamedColors.SpringGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #4682B4. - /// - public static readonly Color SteelBlue = NamedColors.SteelBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #D2B48C. - /// - public static readonly Color Tan = NamedColors.Tan; - - /// - /// Represents a matching the W3C definition that has an hex value of #008080. - /// - public static readonly Color Teal = NamedColors.Teal; - - /// - /// Represents a matching the W3C definition that has an hex value of #D8BFD8. - /// - public static readonly Color Thistle = NamedColors.Thistle; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF6347. - /// - public static readonly Color Tomato = NamedColors.Tomato; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFFFF. - /// - public static readonly Color Transparent = NamedColors.Transparent; - - /// - /// Represents a matching the W3C definition that has an hex value of #40E0D0. - /// - public static readonly Color Turquoise = NamedColors.Turquoise; - - /// - /// Represents a matching the W3C definition that has an hex value of #EE82EE. - /// - public static readonly Color Violet = NamedColors.Violet; - - /// - /// Represents a matching the W3C definition that has an hex value of #F5DEB3. - /// - public static readonly Color Wheat = NamedColors.Wheat; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFFFF. - /// - public static readonly Color White = NamedColors.White; - - /// - /// Represents a matching the W3C definition that has an hex value of #F5F5F5. - /// - public static readonly Color WhiteSmoke = NamedColors.WhiteSmoke; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFF00. - /// - public static readonly Color Yellow = NamedColors.Yellow; - - /// - /// Represents a matching the W3C definition that has an hex value of #9ACD32. - /// - public static readonly Color YellowGreen = NamedColors.YellowGreen; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Colors/ColorTransforms.cs b/src/ImageSharp/Colors/ColorTransforms.cs deleted file mode 100644 index f61afbf5b2..0000000000 --- a/src/ImageSharp/Colors/ColorTransforms.cs +++ /dev/null @@ -1,255 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System.Numerics; - - /// - /// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255. - /// The color components are stored in red, green, blue, and alpha order. - /// - /// - /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, - /// as it avoids the need to create new values for modification operations. - /// - public partial struct Color - { - /// - /// Adds the second color to the first. - /// - /// The first source color. - /// The second source color. - /// - /// The . - /// - public static Color operator +(Color left, Color right) - { - Vector4 add = left.ToVector4() + right.ToVector4(); - return new Color(Pack(ref add)); - } - - /// - /// Subtracts the second color from the first. - /// - /// The first source color. - /// The second source color. - /// - /// The . - /// - public static Color operator -(Color left, Color right) - { - Vector4 sub = left.ToVector4() - right.ToVector4(); - return new Color(Pack(ref sub)); - } - - /// - /// The blending formula simply selects the source color. - /// - /// The backdrop color. - /// The source color. - /// - /// The . - /// - public static Color Normal(Color backdrop, Color source) - { - Vector4 normal = Vector4BlendTransforms.Normal(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref normal)); - } - - /// - /// Blends two colors by multiplication. - /// - /// The source color is multiplied by the destination color and replaces the destination. - /// The resultant color is always at least as dark as either the source or destination color. - /// Multiplying any color with black results in black. Multiplying any color with white preserves the - /// original color. - /// - /// - /// The backdrop color. - /// The source color. - /// - /// The . - /// - public static Color Multiply(Color backdrop, Color source) - { - Vector4 multiply = Vector4BlendTransforms.Multiply(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref multiply)); - } - - /// - /// Multiplies the complements of the backdrop and source color values, then complements the result. - /// - /// The result color is always at least as light as either of the two constituent colors. Screening any - /// color with white produces white; screening with black leaves the original color unchanged. - /// The effect is similar to projecting multiple photographic slides simultaneously onto a single screen. - /// - /// - /// The backdrop color. - /// The source color. - /// - /// The . - /// - public static Color Screen(Color backdrop, Color source) - { - Vector4 subtract = Vector4BlendTransforms.Screen(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref subtract)); - } - - /// - /// Multiplies or screens the colors, depending on the source color value. The effect is similar to - /// shining a harsh spotlight on the backdrop. - /// - /// The backdrop color. - /// The source color. - /// - /// The . - /// - public static Color HardLight(Color backdrop, Color source) - { - Vector4 hardlight = Vector4BlendTransforms.HardLight(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref hardlight)); - } - - /// - /// Multiplies or screens the colors, depending on the backdrop color value. - /// - /// Source colors overlay the backdrop while preserving its highlights and shadows. - /// The backdrop color is not replaced but is mixed with the source color to reflect the lightness or darkness - /// of the backdrop. - /// - /// - /// The backdrop color. - /// The source color. - /// - /// The . - /// - public static Color Overlay(Color backdrop, Color source) - { - Vector4 overlay = Vector4BlendTransforms.Overlay(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref overlay)); - } - - /// - /// Selects the darker of the backdrop and source colors. - /// The backdrop is replaced with the source where the source is darker; otherwise, it is left unchanged. - /// - /// The backdrop color. - /// The source color. - /// - /// The . - /// - public static Color Darken(Color backdrop, Color source) - { - Vector4 darken = Vector4BlendTransforms.Darken(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref darken)); - } - - /// - /// Selects the lighter of the backdrop and source colors. - /// The backdrop is replaced with the source where the source is lighter; otherwise, it is left unchanged. - /// - /// The backdrop color. - /// The source color. - /// - /// The . - /// - public static Color Lighten(Color backdrop, Color source) - { - Vector4 lighten = Vector4BlendTransforms.Lighten(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref lighten)); - } - - /// - /// Darkens or lightens the colors, depending on the source color value. The effect is similar to shining - /// a diffused spotlight on the backdrop. - /// - /// The backdrop color. - /// The source color. - /// - /// The . - /// - public static Color SoftLight(Color backdrop, Color source) - { - Vector4 softlight = Vector4BlendTransforms.SoftLight(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref softlight)); - } - - /// - /// Brightens the backdrop color to reflect the source color. Painting with black produces no changes. - /// - /// The backdrop color. - /// The source color. - /// - /// The . - /// - public static Color ColorDodge(Color backdrop, Color source) - { - Vector4 dodge = Vector4BlendTransforms.Dodge(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref dodge)); - } - - /// - /// Darkens the backdrop color to reflect the source color. Painting with white produces no change. - /// - /// The backdrop color. - /// The source color. - /// - /// The . - /// - public static Color ColorBurn(Color backdrop, Color source) - { - Vector4 burn = Vector4BlendTransforms.Burn(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref burn)); - } - - /// - /// Subtracts the darker of the two constituent colors from the lighter color. - /// Painting with white inverts the backdrop color; painting with black produces no change. - /// - /// The backdrop color. - /// The source color. - /// - /// The . - /// - public static Color Difference(Color backdrop, Color source) - { - Vector4 difference = Vector4BlendTransforms.Difference(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref difference)); - } - - /// - /// Produces an effect similar to that of the mode but lower in contrast. Painting with white - /// inverts the backdrop color; painting with black produces no change - /// - /// The backdrop color. - /// The source color. - /// - /// The . - /// - public static Color Exclusion(Color backdrop, Color source) - { - Vector4 exclusion = Vector4BlendTransforms.Exclusion(backdrop.ToVector4(), source.ToVector4()); - return new Color(Pack(ref exclusion)); - } - - /// - /// Linearly interpolates from one color to another based on the given weighting. - /// - /// The first color value. - /// The second color value. - /// - /// A value between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. - /// - /// - /// The - /// - public static Color Lerp(Color from, Color to, float amount) - { - return new Color(Vector4.Lerp(from.ToVector4(), to.ToVector4(), amount)); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Colors/NamedColors{TColor}.cs b/src/ImageSharp/Colors/NamedColors{TColor}.cs deleted file mode 100644 index bcad4dd40b..0000000000 --- a/src/ImageSharp/Colors/NamedColors{TColor}.cs +++ /dev/null @@ -1,727 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - - /// - /// A set of named colors mapped to the provided Color space. - /// - /// The type of the color. - public static class NamedColors - where TColor : struct, IPixel - { - /// - /// Represents a matching the W3C definition that has an hex value of #F0F8FF. - /// - public static readonly TColor AliceBlue = ColorBuilder.FromRGBA(240, 248, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FAEBD7. - /// - public static readonly TColor AntiqueWhite = ColorBuilder.FromRGBA(250, 235, 215, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #00FFFF. - /// - public static readonly TColor Aqua = ColorBuilder.FromRGBA(0, 255, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #7FFFD4. - /// - public static readonly TColor Aquamarine = ColorBuilder.FromRGBA(127, 255, 212, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F0FFFF. - /// - public static readonly TColor Azure = ColorBuilder.FromRGBA(240, 255, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F5F5DC. - /// - public static readonly TColor Beige = ColorBuilder.FromRGBA(245, 245, 220, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFE4C4. - /// - public static readonly TColor Bisque = ColorBuilder.FromRGBA(255, 228, 196, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #000000. - /// - public static readonly TColor Black = ColorBuilder.FromRGBA(0, 0, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFEBCD. - /// - public static readonly TColor BlanchedAlmond = ColorBuilder.FromRGBA(255, 235, 205, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #0000FF. - /// - public static readonly TColor Blue = ColorBuilder.FromRGBA(0, 0, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #8A2BE2. - /// - public static readonly TColor BlueViolet = ColorBuilder.FromRGBA(138, 43, 226, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #A52A2A. - /// - public static readonly TColor Brown = ColorBuilder.FromRGBA(165, 42, 42, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #DEB887. - /// - public static readonly TColor BurlyWood = ColorBuilder.FromRGBA(222, 184, 135, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #5F9EA0. - /// - public static readonly TColor CadetBlue = ColorBuilder.FromRGBA(95, 158, 160, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #7FFF00. - /// - public static readonly TColor Chartreuse = ColorBuilder.FromRGBA(127, 255, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #D2691E. - /// - public static readonly TColor Chocolate = ColorBuilder.FromRGBA(210, 105, 30, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF7F50. - /// - public static readonly TColor Coral = ColorBuilder.FromRGBA(255, 127, 80, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #6495ED. - /// - public static readonly TColor CornflowerBlue = ColorBuilder.FromRGBA(100, 149, 237, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFF8DC. - /// - public static readonly TColor Cornsilk = ColorBuilder.FromRGBA(255, 248, 220, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #DC143C. - /// - public static readonly TColor Crimson = ColorBuilder.FromRGBA(220, 20, 60, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #00FFFF. - /// - public static readonly TColor Cyan = ColorBuilder.FromRGBA(0, 255, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #00008B. - /// - public static readonly TColor DarkBlue = ColorBuilder.FromRGBA(0, 0, 139, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #008B8B. - /// - public static readonly TColor DarkCyan = ColorBuilder.FromRGBA(0, 139, 139, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #B8860B. - /// - public static readonly TColor DarkGoldenrod = ColorBuilder.FromRGBA(184, 134, 11, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #A9A9A9. - /// - public static readonly TColor DarkGray = ColorBuilder.FromRGBA(169, 169, 169, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #006400. - /// - public static readonly TColor DarkGreen = ColorBuilder.FromRGBA(0, 100, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #BDB76B. - /// - public static readonly TColor DarkKhaki = ColorBuilder.FromRGBA(189, 183, 107, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #8B008B. - /// - public static readonly TColor DarkMagenta = ColorBuilder.FromRGBA(139, 0, 139, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #556B2F. - /// - public static readonly TColor DarkOliveGreen = ColorBuilder.FromRGBA(85, 107, 47, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF8C00. - /// - public static readonly TColor DarkOrange = ColorBuilder.FromRGBA(255, 140, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #9932CC. - /// - public static readonly TColor DarkOrchid = ColorBuilder.FromRGBA(153, 50, 204, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #8B0000. - /// - public static readonly TColor DarkRed = ColorBuilder.FromRGBA(139, 0, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #E9967A. - /// - public static readonly TColor DarkSalmon = ColorBuilder.FromRGBA(233, 150, 122, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #8FBC8B. - /// - public static readonly TColor DarkSeaGreen = ColorBuilder.FromRGBA(143, 188, 139, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #483D8B. - /// - public static readonly TColor DarkSlateBlue = ColorBuilder.FromRGBA(72, 61, 139, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #2F4F4F. - /// - public static readonly TColor DarkSlateGray = ColorBuilder.FromRGBA(47, 79, 79, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #00CED1. - /// - public static readonly TColor DarkTurquoise = ColorBuilder.FromRGBA(0, 206, 209, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #9400D3. - /// - public static readonly TColor DarkViolet = ColorBuilder.FromRGBA(148, 0, 211, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF1493. - /// - public static readonly TColor DeepPink = ColorBuilder.FromRGBA(255, 20, 147, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #00BFFF. - /// - public static readonly TColor DeepSkyBlue = ColorBuilder.FromRGBA(0, 191, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #696969. - /// - public static readonly TColor DimGray = ColorBuilder.FromRGBA(105, 105, 105, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #1E90FF. - /// - public static readonly TColor DodgerBlue = ColorBuilder.FromRGBA(30, 144, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #B22222. - /// - public static readonly TColor Firebrick = ColorBuilder.FromRGBA(178, 34, 34, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFAF0. - /// - public static readonly TColor FloralWhite = ColorBuilder.FromRGBA(255, 250, 240, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #228B22. - /// - public static readonly TColor ForestGreen = ColorBuilder.FromRGBA(34, 139, 34, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF00FF. - /// - public static readonly TColor Fuchsia = ColorBuilder.FromRGBA(255, 0, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #DCDCDC. - /// - public static readonly TColor Gainsboro = ColorBuilder.FromRGBA(220, 220, 220, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F8F8FF. - /// - public static readonly TColor GhostWhite = ColorBuilder.FromRGBA(248, 248, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFD700. - /// - public static readonly TColor Gold = ColorBuilder.FromRGBA(255, 215, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #DAA520. - /// - public static readonly TColor Goldenrod = ColorBuilder.FromRGBA(218, 165, 32, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #808080. - /// - public static readonly TColor Gray = ColorBuilder.FromRGBA(128, 128, 128, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #008000. - /// - public static readonly TColor Green = ColorBuilder.FromRGBA(0, 128, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #ADFF2F. - /// - public static readonly TColor GreenYellow = ColorBuilder.FromRGBA(173, 255, 47, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F0FFF0. - /// - public static readonly TColor Honeydew = ColorBuilder.FromRGBA(240, 255, 240, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF69B4. - /// - public static readonly TColor HotPink = ColorBuilder.FromRGBA(255, 105, 180, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #CD5C5C. - /// - public static readonly TColor IndianRed = ColorBuilder.FromRGBA(205, 92, 92, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #4B0082. - /// - public static readonly TColor Indigo = ColorBuilder.FromRGBA(75, 0, 130, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFFF0. - /// - public static readonly TColor Ivory = ColorBuilder.FromRGBA(255, 255, 240, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F0E68C. - /// - public static readonly TColor Khaki = ColorBuilder.FromRGBA(240, 230, 140, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #E6E6FA. - /// - public static readonly TColor Lavender = ColorBuilder.FromRGBA(230, 230, 250, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFF0F5. - /// - public static readonly TColor LavenderBlush = ColorBuilder.FromRGBA(255, 240, 245, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #7CFC00. - /// - public static readonly TColor LawnGreen = ColorBuilder.FromRGBA(124, 252, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFACD. - /// - public static readonly TColor LemonChiffon = ColorBuilder.FromRGBA(255, 250, 205, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #ADD8E6. - /// - public static readonly TColor LightBlue = ColorBuilder.FromRGBA(173, 216, 230, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F08080. - /// - public static readonly TColor LightCoral = ColorBuilder.FromRGBA(240, 128, 128, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #E0FFFF. - /// - public static readonly TColor LightCyan = ColorBuilder.FromRGBA(224, 255, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FAFAD2. - /// - public static readonly TColor LightGoldenrodYellow = ColorBuilder.FromRGBA(250, 250, 210, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #D3D3D3. - /// - public static readonly TColor LightGray = ColorBuilder.FromRGBA(211, 211, 211, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #90EE90. - /// - public static readonly TColor LightGreen = ColorBuilder.FromRGBA(144, 238, 144, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFB6C1. - /// - public static readonly TColor LightPink = ColorBuilder.FromRGBA(255, 182, 193, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFA07A. - /// - public static readonly TColor LightSalmon = ColorBuilder.FromRGBA(255, 160, 122, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #20B2AA. - /// - public static readonly TColor LightSeaGreen = ColorBuilder.FromRGBA(32, 178, 170, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #87CEFA. - /// - public static readonly TColor LightSkyBlue = ColorBuilder.FromRGBA(135, 206, 250, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #778899. - /// - public static readonly TColor LightSlateGray = ColorBuilder.FromRGBA(119, 136, 153, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #B0C4DE. - /// - public static readonly TColor LightSteelBlue = ColorBuilder.FromRGBA(176, 196, 222, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFFE0. - /// - public static readonly TColor LightYellow = ColorBuilder.FromRGBA(255, 255, 224, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #00FF00. - /// - public static readonly TColor Lime = ColorBuilder.FromRGBA(0, 255, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #32CD32. - /// - public static readonly TColor LimeGreen = ColorBuilder.FromRGBA(50, 205, 50, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FAF0E6. - /// - public static readonly TColor Linen = ColorBuilder.FromRGBA(250, 240, 230, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF00FF. - /// - public static readonly TColor Magenta = ColorBuilder.FromRGBA(255, 0, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #800000. - /// - public static readonly TColor Maroon = ColorBuilder.FromRGBA(128, 0, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #66CDAA. - /// - public static readonly TColor MediumAquamarine = ColorBuilder.FromRGBA(102, 205, 170, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #0000CD. - /// - public static readonly TColor MediumBlue = ColorBuilder.FromRGBA(0, 0, 205, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #BA55D3. - /// - public static readonly TColor MediumOrchid = ColorBuilder.FromRGBA(186, 85, 211, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #9370DB. - /// - public static readonly TColor MediumPurple = ColorBuilder.FromRGBA(147, 112, 219, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #3CB371. - /// - public static readonly TColor MediumSeaGreen = ColorBuilder.FromRGBA(60, 179, 113, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #7B68EE. - /// - public static readonly TColor MediumSlateBlue = ColorBuilder.FromRGBA(123, 104, 238, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #00FA9A. - /// - public static readonly TColor MediumSpringGreen = ColorBuilder.FromRGBA(0, 250, 154, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #48D1CC. - /// - public static readonly TColor MediumTurquoise = ColorBuilder.FromRGBA(72, 209, 204, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #C71585. - /// - public static readonly TColor MediumVioletRed = ColorBuilder.FromRGBA(199, 21, 133, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #191970. - /// - public static readonly TColor MidnightBlue = ColorBuilder.FromRGBA(25, 25, 112, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F5FFFA. - /// - public static readonly TColor MintCream = ColorBuilder.FromRGBA(245, 255, 250, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFE4E1. - /// - public static readonly TColor MistyRose = ColorBuilder.FromRGBA(255, 228, 225, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFE4B5. - /// - public static readonly TColor Moccasin = ColorBuilder.FromRGBA(255, 228, 181, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFDEAD. - /// - public static readonly TColor NavajoWhite = ColorBuilder.FromRGBA(255, 222, 173, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #000080. - /// - public static readonly TColor Navy = ColorBuilder.FromRGBA(0, 0, 128, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FDF5E6. - /// - public static readonly TColor OldLace = ColorBuilder.FromRGBA(253, 245, 230, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #808000. - /// - public static readonly TColor Olive = ColorBuilder.FromRGBA(128, 128, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #6B8E23. - /// - public static readonly TColor OliveDrab = ColorBuilder.FromRGBA(107, 142, 35, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFA500. - /// - public static readonly TColor Orange = ColorBuilder.FromRGBA(255, 165, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF4500. - /// - public static readonly TColor OrangeRed = ColorBuilder.FromRGBA(255, 69, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #DA70D6. - /// - public static readonly TColor Orchid = ColorBuilder.FromRGBA(218, 112, 214, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #EEE8AA. - /// - public static readonly TColor PaleGoldenrod = ColorBuilder.FromRGBA(238, 232, 170, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #98FB98. - /// - public static readonly TColor PaleGreen = ColorBuilder.FromRGBA(152, 251, 152, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #AFEEEE. - /// - public static readonly TColor PaleTurquoise = ColorBuilder.FromRGBA(175, 238, 238, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #DB7093. - /// - public static readonly TColor PaleVioletRed = ColorBuilder.FromRGBA(219, 112, 147, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFEFD5. - /// - public static readonly TColor PapayaWhip = ColorBuilder.FromRGBA(255, 239, 213, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFDAB9. - /// - public static readonly TColor PeachPuff = ColorBuilder.FromRGBA(255, 218, 185, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #CD853F. - /// - public static readonly TColor Peru = ColorBuilder.FromRGBA(205, 133, 63, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFC0CB. - /// - public static readonly TColor Pink = ColorBuilder.FromRGBA(255, 192, 203, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #DDA0DD. - /// - public static readonly TColor Plum = ColorBuilder.FromRGBA(221, 160, 221, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #B0E0E6. - /// - public static readonly TColor PowderBlue = ColorBuilder.FromRGBA(176, 224, 230, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #800080. - /// - public static readonly TColor Purple = ColorBuilder.FromRGBA(128, 0, 128, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #663399. - /// - public static readonly TColor RebeccaPurple = ColorBuilder.FromRGBA(102, 51, 153, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF0000. - /// - public static readonly TColor Red = ColorBuilder.FromRGBA(255, 0, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #BC8F8F. - /// - public static readonly TColor RosyBrown = ColorBuilder.FromRGBA(188, 143, 143, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #4169E1. - /// - public static readonly TColor RoyalBlue = ColorBuilder.FromRGBA(65, 105, 225, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #8B4513. - /// - public static readonly TColor SaddleBrown = ColorBuilder.FromRGBA(139, 69, 19, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FA8072. - /// - public static readonly TColor Salmon = ColorBuilder.FromRGBA(250, 128, 114, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F4A460. - /// - public static readonly TColor SandyBrown = ColorBuilder.FromRGBA(244, 164, 96, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #2E8B57. - /// - public static readonly TColor SeaGreen = ColorBuilder.FromRGBA(46, 139, 87, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFF5EE. - /// - public static readonly TColor SeaShell = ColorBuilder.FromRGBA(255, 245, 238, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #A0522D. - /// - public static readonly TColor Sienna = ColorBuilder.FromRGBA(160, 82, 45, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #C0C0C0. - /// - public static readonly TColor Silver = ColorBuilder.FromRGBA(192, 192, 192, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #87CEEB. - /// - public static readonly TColor SkyBlue = ColorBuilder.FromRGBA(135, 206, 235, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #6A5ACD. - /// - public static readonly TColor SlateBlue = ColorBuilder.FromRGBA(106, 90, 205, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #708090. - /// - public static readonly TColor SlateGray = ColorBuilder.FromRGBA(112, 128, 144, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFAFA. - /// - public static readonly TColor Snow = ColorBuilder.FromRGBA(255, 250, 250, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #00FF7F. - /// - public static readonly TColor SpringGreen = ColorBuilder.FromRGBA(0, 255, 127, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #4682B4. - /// - public static readonly TColor SteelBlue = ColorBuilder.FromRGBA(70, 130, 180, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #D2B48C. - /// - public static readonly TColor Tan = ColorBuilder.FromRGBA(210, 180, 140, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #008080. - /// - public static readonly TColor Teal = ColorBuilder.FromRGBA(0, 128, 128, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #D8BFD8. - /// - public static readonly TColor Thistle = ColorBuilder.FromRGBA(216, 191, 216, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FF6347. - /// - public static readonly TColor Tomato = ColorBuilder.FromRGBA(255, 99, 71, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFFFF. - /// - public static readonly TColor Transparent = ColorBuilder.FromRGBA(255, 255, 255, 0); - - /// - /// Represents a matching the W3C definition that has an hex value of #40E0D0. - /// - public static readonly TColor Turquoise = ColorBuilder.FromRGBA(64, 224, 208, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #EE82EE. - /// - public static readonly TColor Violet = ColorBuilder.FromRGBA(238, 130, 238, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F5DEB3. - /// - public static readonly TColor Wheat = ColorBuilder.FromRGBA(245, 222, 179, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFFFF. - /// - public static readonly TColor White = ColorBuilder.FromRGBA(255, 255, 255, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #F5F5F5. - /// - public static readonly TColor WhiteSmoke = ColorBuilder.FromRGBA(245, 245, 245, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFF00. - /// - public static readonly TColor Yellow = ColorBuilder.FromRGBA(255, 255, 0, 255); - - /// - /// Represents a matching the W3C definition that has an hex value of #9ACD32. - /// - public static readonly TColor YellowGreen = ColorBuilder.FromRGBA(154, 205, 50, 255); - } -} \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs deleted file mode 100644 index 7b6169f9c6..0000000000 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs +++ /dev/null @@ -1,256 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System.Numerics; - using System.Runtime.CompilerServices; - - /// - /// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations - /// for pixel buffers of type . - /// - /// The pixel format. - public unsafe class BulkPixelOperations - where TColor : struct, IPixel - { - /// - /// The size of in bytes - /// - private static readonly int ColorSize = Unsafe.SizeOf(); - - /// - /// Gets the global instance for the pixel type - /// - public static BulkPixelOperations Instance { get; } = default(TColor).CreateBulkOperations(); - - /// - /// Bulk version of - /// - /// The to the source vectors. - /// The to the destination colors. - /// The number of pixels to convert. - internal virtual void PackFromVector4( - BufferSpan sourceVectors, - BufferSpan destColors, - int count) - { - Vector4* sp = (Vector4*)sourceVectors.PointerAtOffset; - byte* dp = (byte*)destColors; - - for (int i = 0; i < count; i++) - { - Vector4 v = Unsafe.Read(sp); - TColor c = default(TColor); - c.PackFromVector4(v); - Unsafe.Write(dp, c); - - sp++; - dp += ColorSize; - } - } - - /// - /// Bulk version of . - /// - /// The to the source colors. - /// The to the destination vectors. - /// The number of pixels to convert. - internal virtual void ToVector4( - BufferSpan sourceColors, - BufferSpan destVectors, - int count) - { - byte* sp = (byte*)sourceColors; - Vector4* dp = (Vector4*)destVectors.PointerAtOffset; - - for (int i = 0; i < count; i++) - { - TColor c = Unsafe.Read(sp); - *dp = c.ToVector4(); - sp += ColorSize; - dp++; - } - } - - /// - /// Bulk version of that converts data in . - /// - /// The to the source bytes. - /// The to the destination colors. - /// The number of pixels to convert. - internal virtual void PackFromXyzBytes( - BufferSpan sourceBytes, - BufferSpan destColors, - int count) - { - byte* sp = (byte*)sourceBytes; - byte* dp = (byte*)destColors.PointerAtOffset; - - for (int i = 0; i < count; i++) - { - TColor c = default(TColor); - c.PackFromBytes(sp[0], sp[1], sp[2], 255); - Unsafe.Write(dp, c); - sp += 3; - dp += ColorSize; - } - } - - /// - /// Bulk version of . - /// - /// The to the source colors. - /// The to the destination bytes. - /// The number of pixels to convert. - internal virtual void ToXyzBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) - { - byte* sp = (byte*)sourceColors; - byte[] dest = destBytes.Array; - - for (int i = destBytes.Start; i < destBytes.Start + (count * 3); i += 3) - { - TColor c = Unsafe.Read(sp); - c.ToXyzBytes(dest, i); - sp += ColorSize; - } - } - - /// - /// Bulk version of that converts data in . - /// - /// The to the source bytes. - /// The to the destination colors. - /// The number of pixels to convert. - internal virtual void PackFromXyzwBytes( - BufferSpan sourceBytes, - BufferSpan destColors, - int count) - { - byte* sp = (byte*)sourceBytes; - byte* dp = (byte*)destColors.PointerAtOffset; - - for (int i = 0; i < count; i++) - { - TColor c = default(TColor); - c.PackFromBytes(sp[0], sp[1], sp[2], sp[3]); - Unsafe.Write(dp, c); - sp += 4; - dp += ColorSize; - } - } - - /// - /// Bulk version of . - /// - /// The to the source colors. - /// The to the destination bytes. - /// The number of pixels to convert. - internal virtual void ToXyzwBytes( - BufferSpan sourceColors, - BufferSpan destBytes, - int count) - { - byte* sp = (byte*)sourceColors; - byte[] dest = destBytes.Array; - - for (int i = destBytes.Start; i < destBytes.Start + (count * 4); i += 4) - { - TColor c = Unsafe.Read(sp); - c.ToXyzwBytes(dest, i); - sp += ColorSize; - } - } - - /// - /// Bulk version of that converts data in . - /// - /// The to the source bytes. - /// The to the destination colors. - /// The number of pixels to convert. - internal virtual void PackFromZyxBytes( - BufferSpan sourceBytes, - BufferSpan destColors, - int count) - { - byte* sp = (byte*)sourceBytes; - byte* dp = (byte*)destColors.PointerAtOffset; - - for (int i = 0; i < count; i++) - { - TColor c = default(TColor); - c.PackFromBytes(sp[2], sp[1], sp[0], 255); - Unsafe.Write(dp, c); - sp += 3; - dp += ColorSize; - } - } - - /// - /// Bulk version of . - /// - /// The to the source colors. - /// The to the destination bytes. - /// The number of pixels to convert. - internal virtual void ToZyxBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) - { - byte* sp = (byte*)sourceColors; - byte[] dest = destBytes.Array; - - for (int i = destBytes.Start; i < destBytes.Start + (count * 3); i += 3) - { - TColor c = Unsafe.Read(sp); - c.ToZyxBytes(dest, i); - sp += ColorSize; - } - } - - /// - /// Bulk version of that converts data in . - /// - /// The to the source bytes. - /// The to the destination colors. - /// The number of pixels to convert. - internal virtual void PackFromZyxwBytes( - BufferSpan sourceBytes, - BufferSpan destColors, - int count) - { - byte* sp = (byte*)sourceBytes; - byte* dp = (byte*)destColors.PointerAtOffset; - - for (int i = 0; i < count; i++) - { - TColor c = default(TColor); - c.PackFromBytes(sp[2], sp[1], sp[0], sp[3]); - Unsafe.Write(dp, c); - sp += 4; - dp += ColorSize; - } - } - - /// - /// Bulk version of . - /// - /// The to the source colors. - /// The to the destination bytes. - /// The number of pixels to convert. - internal virtual void ToZyxwBytes( - BufferSpan sourceColors, - BufferSpan destBytes, - int count) - { - byte* sp = (byte*)sourceColors; - byte[] dest = destBytes.Array; - - for (int i = destBytes.Start; i < destBytes.Start + (count * 4); i += 4) - { - TColor c = Unsafe.Read(sp); - c.ToZyxwBytes(dest, i); - sp += ColorSize; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/README.md b/src/ImageSharp/Colors/PackedPixel/README.md deleted file mode 100644 index 61500de68d..0000000000 --- a/src/ImageSharp/Colors/PackedPixel/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Pixel formats adapted and extended from: - -https://github.com/MonoGame/MonoGame \ No newline at end of file diff --git a/src/ImageSharp/Colors/Spaces/Bgra32.cs b/src/ImageSharp/Colors/Spaces/Bgra32.cs index cbd1d61194..b1f72033d3 100644 --- a/src/ImageSharp/Colors/Spaces/Bgra32.cs +++ b/src/ImageSharp/Colors/Spaces/Bgra32.cs @@ -8,6 +8,7 @@ namespace ImageSharp.Colors.Spaces using System; using System.ComponentModel; using System.Numerics; + using ImageSharp.PixelFormats; /// /// Represents an BGRA (blue, green, red, alpha) color. @@ -79,16 +80,16 @@ namespace ImageSharp.Colors.Spaces public bool IsEmpty => this.Equals(Empty); /// - /// Allows the implicit conversion of an instance of to a + /// Allows the implicit conversion of an instance of to a /// . /// /// - /// The instance of to convert. + /// The instance of to convert. /// /// /// An instance of . /// - public static implicit operator Bgra32(Color color) + public static implicit operator Bgra32(Rgba32 color) { return new Bgra32(color.B, color.G, color.R, color.A); } diff --git a/src/ImageSharp/Colors/Spaces/CieLab.cs b/src/ImageSharp/Colors/Spaces/CieLab.cs index 921158174c..c1e5cba5a0 100644 --- a/src/ImageSharp/Colors/Spaces/CieLab.cs +++ b/src/ImageSharp/Colors/Spaces/CieLab.cs @@ -8,6 +8,7 @@ namespace ImageSharp.Colors.Spaces using System; using System.ComponentModel; using System.Numerics; + using ImageSharp.PixelFormats; /// /// Represents an CIE LAB 1976 color. @@ -72,16 +73,16 @@ namespace ImageSharp.Colors.Spaces public bool IsEmpty => this.Equals(Empty); /// - /// Allows the implicit conversion of an instance of to a + /// Allows the implicit conversion of an instance of to a /// . /// /// - /// The instance of to convert. + /// The instance of to convert. /// /// /// An instance of . /// - public static implicit operator CieLab(Color color) + public static implicit operator CieLab(Rgba32 color) { // First convert to CIE XYZ Vector4 vector = color.ToVector4().Expand(); diff --git a/src/ImageSharp/Colors/Spaces/CieXyz.cs b/src/ImageSharp/Colors/Spaces/CieXyz.cs index 5bd1eac634..9c6c9bf60f 100644 --- a/src/ImageSharp/Colors/Spaces/CieXyz.cs +++ b/src/ImageSharp/Colors/Spaces/CieXyz.cs @@ -8,6 +8,7 @@ namespace ImageSharp.Colors.Spaces using System; using System.ComponentModel; using System.Numerics; + using ImageSharp.PixelFormats; /// /// Represents an CIE 1931 color @@ -63,16 +64,16 @@ namespace ImageSharp.Colors.Spaces public bool IsEmpty => this.Equals(Empty); /// - /// Allows the implicit conversion of an instance of to a + /// Allows the implicit conversion of an instance of to a /// . /// /// - /// The instance of to convert. + /// The instance of to convert. /// /// /// An instance of . /// - public static implicit operator CieXyz(Color color) + public static implicit operator CieXyz(Rgba32 color) { Vector4 vector = color.ToVector4().Expand(); diff --git a/src/ImageSharp/Colors/Spaces/Cmyk.cs b/src/ImageSharp/Colors/Spaces/Cmyk.cs index c81a55c0bb..4ca9f018c6 100644 --- a/src/ImageSharp/Colors/Spaces/Cmyk.cs +++ b/src/ImageSharp/Colors/Spaces/Cmyk.cs @@ -8,6 +8,7 @@ namespace ImageSharp.Colors.Spaces using System; using System.ComponentModel; using System.Numerics; + using ImageSharp.PixelFormats; /// /// Represents an CMYK (cyan, magenta, yellow, keyline) color. @@ -78,7 +79,7 @@ namespace ImageSharp.Colors.Spaces public bool IsEmpty => this.Equals(Empty); /// - /// Allows the implicit conversion of an instance of to a + /// Allows the implicit conversion of an instance of to a /// . /// /// @@ -87,7 +88,7 @@ namespace ImageSharp.Colors.Spaces /// /// An instance of . /// - public static implicit operator Cmyk(Color color) + public static implicit operator Cmyk(Rgba32 color) { float c = 1f - (color.R / 255F); float m = 1f - (color.G / 255F); diff --git a/src/ImageSharp/Colors/Spaces/Hsl.cs b/src/ImageSharp/Colors/Spaces/Hsl.cs index 1d655ec326..de706c3506 100644 --- a/src/ImageSharp/Colors/Spaces/Hsl.cs +++ b/src/ImageSharp/Colors/Spaces/Hsl.cs @@ -8,6 +8,7 @@ namespace ImageSharp.Colors.Spaces using System; using System.ComponentModel; using System.Numerics; + using ImageSharp.PixelFormats; /// /// Represents a Hsl (hue, saturation, lightness) color. @@ -70,14 +71,14 @@ namespace ImageSharp.Colors.Spaces public bool IsEmpty => this.Equals(Empty); /// - /// Allows the implicit conversion of an instance of to a + /// Allows the implicit conversion of an instance of to a /// . /// - /// The instance of to convert. + /// The instance of to convert. /// /// An instance of . /// - public static implicit operator Hsl(Color color) + public static implicit operator Hsl(Rgba32 color) { float r = color.R / 255F; float g = color.G / 255F; diff --git a/src/ImageSharp/Colors/Spaces/Hsv.cs b/src/ImageSharp/Colors/Spaces/Hsv.cs index e171c95282..2b3d79afe9 100644 --- a/src/ImageSharp/Colors/Spaces/Hsv.cs +++ b/src/ImageSharp/Colors/Spaces/Hsv.cs @@ -8,6 +8,7 @@ namespace ImageSharp.Colors.Spaces using System; using System.ComponentModel; using System.Numerics; + using ImageSharp.PixelFormats; /// /// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness). @@ -70,14 +71,14 @@ namespace ImageSharp.Colors.Spaces public bool IsEmpty => this.Equals(Empty); /// - /// Allows the implicit conversion of an instance of to a + /// Allows the implicit conversion of an instance of to a /// . /// - /// The instance of to convert. + /// The instance of to convert. /// /// An instance of . /// - public static implicit operator Hsv(Color color) + public static implicit operator Hsv(Rgba32 color) { float r = color.R / 255F; float g = color.G / 255F; diff --git a/src/ImageSharp/Colors/Spaces/IAlmostEquatable.cs b/src/ImageSharp/Colors/Spaces/IAlmostEquatable.cs index a2183d396c..04ea91cbad 100644 --- a/src/ImageSharp/Colors/Spaces/IAlmostEquatable.cs +++ b/src/ImageSharp/Colors/Spaces/IAlmostEquatable.cs @@ -11,9 +11,9 @@ namespace ImageSharp.Colors.Spaces /// Defines a generalized method that a value type or class implements to create /// a type-specific method for determining approximate equality of instances. /// - /// The type of objects to compare. + /// The type of objects to compare. /// The object specifying the type to specify precision with. - public interface IAlmostEquatable + public interface IAlmostEquatable where TPrecision : struct, IComparable { /// @@ -25,6 +25,6 @@ namespace ImageSharp.Colors.Spaces /// /// true if the current object is equal to the other parameter; otherwise, false. /// - bool AlmostEquals(TColor other, TPrecision precision); + bool AlmostEquals(TPixel other, TPrecision precision); } } diff --git a/src/ImageSharp/Colors/Spaces/YCbCr.cs b/src/ImageSharp/Colors/Spaces/YCbCr.cs index ef9f4462b1..06696af9ea 100644 --- a/src/ImageSharp/Colors/Spaces/YCbCr.cs +++ b/src/ImageSharp/Colors/Spaces/YCbCr.cs @@ -8,6 +8,7 @@ namespace ImageSharp.Colors.Spaces using System; using System.ComponentModel; using System.Numerics; + using ImageSharp.PixelFormats; /// /// Represents an YCbCr (luminance, blue chroma, red chroma) color conforming to the full range standard used in digital imaging systems. @@ -72,16 +73,16 @@ namespace ImageSharp.Colors.Spaces public bool IsEmpty => this.Equals(Empty); /// - /// Allows the implicit conversion of an instance of to a + /// Allows the implicit conversion of an instance of to a /// . /// /// - /// The instance of to convert. + /// The instance of to convert. /// /// /// An instance of . /// - public static implicit operator YCbCr(Color color) + public static implicit operator YCbCr(Rgba32 color) { byte r = color.R; byte g = color.G; diff --git a/src/ImageSharp/Colors/Vector4BlendTransforms.cs b/src/ImageSharp/Colors/Vector4BlendTransforms.cs deleted file mode 100644 index a7e2e0e919..0000000000 --- a/src/ImageSharp/Colors/Vector4BlendTransforms.cs +++ /dev/null @@ -1,292 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System.Numerics; - - /// - /// Transform algorithms that match the equations defined in the W3C Compositing and Blending Level 1 specification. - /// - /// - public class Vector4BlendTransforms - { - /// - /// The blending formula simply selects the source vector. - /// - /// The backdrop vector. - /// The source vector. - /// - /// The . - /// - public static Vector4 Normal(Vector4 backdrop, Vector4 source) - { - return new Vector4(source.X, source.Y, source.Z, source.W); - } - - /// - /// Blends two vectors by multiplication. - /// - /// The backdrop vector. - /// The source vector. - /// - /// The . - /// - public static Vector4 Multiply(Vector4 backdrop, Vector4 source) - { - Vector4 multiply = backdrop * source; - multiply.W = backdrop.W; - return multiply; - } - - /// - /// Multiplies the complements of the backdrop and source vector values, then complements the result. - /// - /// The backdrop vector. - /// The source vector. - /// - /// The . - /// - public static Vector4 Screen(Vector4 backdrop, Vector4 source) - { - Vector4 subtract = backdrop + source - (backdrop * source); - subtract.W = backdrop.W; - return subtract; - } - - /// - /// Multiplies or screens the colors, depending on the source vector value. - /// - /// The backdrop vector. - /// The source vector. - /// - /// The . - /// - public static Vector4 HardLight(Vector4 backdrop, Vector4 source) - { - return new Vector4(BlendOverlay(source.X, backdrop.X), BlendOverlay(source.Y, backdrop.Y), BlendOverlay(source.Z, backdrop.Z), backdrop.W); - } - - /// - /// Multiplies or screens the vectors, depending on the backdrop vector value. - /// - /// The backdrop vector. - /// The source vector. - /// - /// The . - /// - public static Vector4 Overlay(Vector4 backdrop, Vector4 source) - { - return new Vector4(BlendOverlay(backdrop.X, source.X), BlendOverlay(backdrop.Y, source.Y), BlendOverlay(backdrop.Z, source.Z), backdrop.W); - } - - /// - /// Selects the minimum of the backdrop and source vectors. - /// - /// The backdrop vector. - /// The source vector. - /// - /// The . - /// - public static Vector4 Darken(Vector4 backdrop, Vector4 source) - { - Vector4 result = Vector4.Min(backdrop, source); - result.W = backdrop.W; - return result; - } - - /// - /// Selects the max of the backdrop and source vector. - /// - /// The backdrop vector. - /// The source vector. - /// - /// The . - /// - public static Vector4 Lighten(Vector4 backdrop, Vector4 source) - { - Vector4 result = Vector4.Max(backdrop, source); - result.W = backdrop.W; - return result; - } - - /// - /// Selects the maximum or minimum of the vectors, depending on the source vector value. - /// - /// The backdrop vector. - /// The source vector. - /// - /// The . - /// - public static Vector4 SoftLight(Vector4 backdrop, Vector4 source) - { - return new Vector4(BlendSoftLight(backdrop.X, source.X), BlendSoftLight(backdrop.Y, source.Y), BlendSoftLight(backdrop.Z, source.Z), backdrop.W); - } - - /// - /// Increases the backdrop vector to reflect the source vector. - /// - /// The backdrop vector. - /// The source vector. - /// - /// The . - /// - public static Vector4 Dodge(Vector4 backdrop, Vector4 source) - { - return new Vector4(BlendDodge(backdrop.X, source.X), BlendDodge(backdrop.Y, source.Y), BlendDodge(backdrop.Z, source.Z), backdrop.W); - } - - /// - /// Decreases the backdrop vector to reflect the source vector. - /// - /// The backdrop vector. - /// The source vector. - /// - /// The . - /// - public static Vector4 Burn(Vector4 backdrop, Vector4 source) - { - return new Vector4(BlendBurn(backdrop.X, source.X), BlendBurn(backdrop.Y, source.Y), BlendBurn(backdrop.Z, source.Z), backdrop.W); - } - - /// - /// Subtracts the minimum of the two constituent vectors from the maximum vector. - /// - /// The backdrop vector. - /// The source vector. - /// - /// The . - /// - public static Vector4 Difference(Vector4 backdrop, Vector4 source) - { - Vector4 result = Vector4.Abs(backdrop - source); - result.W = backdrop.W; - return result; - } - - /// - /// Produces an effect similar to that of the mode but lower in magnitude. - /// - /// The backdrop vector. - /// The source vector. - /// - /// The . - /// - public static Vector4 Exclusion(Vector4 backdrop, Vector4 source) - { - return new Vector4(BlendExclusion(backdrop.X, source.X), BlendExclusion(backdrop.Y, source.Y), BlendExclusion(backdrop.Z, source.Z), backdrop.W); - } - - /// - /// Linearly interpolates from one vector to another based on the given weighting. - /// The two vectors are premultiplied before operating. - /// - /// The backdrop vector. - /// The source vector. - /// - /// A value between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. - /// - /// - /// The - /// - public static Vector4 PremultipliedLerp(Vector4 backdrop, Vector4 source, float amount) - { - amount = amount.Clamp(0, 1); - - // Santize on zero alpha - if (MathF.Abs(backdrop.W) < Constants.Epsilon) - { - source.W *= amount; - return source; - } - - if (MathF.Abs(source.W) < Constants.Epsilon) - { - return backdrop; - } - - // Premultiply the source vector. - // Oddly premultiplying the background vector creates dark outlines when pixels - // Have low alpha values. - source = new Vector4(source.X, source.Y, source.Z, 1) * (source.W * amount); - - // This should be implementing the following formula - // https://en.wikipedia.org/wiki/Alpha_compositing - // Vout = Vs + Vb (1 - Vsa) - // Aout = Vsa + Vsb (1 - Vsa) - Vector3 inverseW = new Vector3(1 - source.W); - Vector3 xyzB = new Vector3(backdrop.X, backdrop.Y, backdrop.Z); - Vector3 xyzS = new Vector3(source.X, source.Y, source.Z); - - return new Vector4(xyzS + (xyzB * inverseW), source.W + (backdrop.W * (1 - source.W))); - } - - /// - /// Multiplies or screens the backdrop component, depending on the component value. - /// - /// The backdrop component. - /// The source component. - /// - /// The . - /// - private static float BlendOverlay(float b, float s) - { - return b <= .5F ? (2F * b * s) : (1F - (2F * (1F - b) * (1F - s))); - } - - /// - /// Darkens or lightens the backdrop component, depending on the source component value. - /// - /// The backdrop component. - /// The source component. - /// - /// The . - /// - private static float BlendSoftLight(float b, float s) - { - return s <= .5F ? ((2F * b * s) + (b * b * (1F - (2F * s)))) : (MathF.Sqrt(b) * ((2F * s) - 1F)) + (2F * b * (1F - s)); - } - - /// - /// Brightens the backdrop component to reflect the source component. - /// - /// The backdrop component. - /// The source component. - /// - /// The . - /// - private static float BlendDodge(float b, float s) - { - return MathF.Abs(s - 1F) < Constants.Epsilon ? s : MathF.Min(b / (1F - s), 1F); - } - - /// - /// Darkens the backdrop component to reflect the source component. - /// - /// The backdrop component. - /// The source component. - /// - /// The . - /// - private static float BlendBurn(float b, float s) - { - return MathF.Abs(s) < Constants.Epsilon ? s : MathF.Max(1F - ((1F - b) / s), 0F); - } - - /// - /// Darkens the backdrop component to reflect the source component. - /// - /// The backdrop component. - /// The source component. - /// - /// The . - /// - private static float BlendExclusion(float b, float s) - { - return b + s - (2F * b * s); - } - } -} diff --git a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs index 9f3aa405ed..fac33da140 100644 --- a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs +++ b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs @@ -5,14 +5,14 @@ namespace ImageSharp { - using System; using System.Numerics; using System.Runtime.CompilerServices; + using ImageSharp.PixelFormats; /// /// Extension methods for the struct. /// - public static class Vector4Extensions + internal static class Vector4Extensions { /// /// Compresses a linear color signal to its sRGB equivalent. @@ -21,6 +21,7 @@ namespace ImageSharp /// /// The whose signal to compress. /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Compress(this Vector4 linear) { // TODO: Is there a faster way to do this? @@ -32,8 +33,9 @@ namespace ImageSharp /// /// /// - /// The whose signal to expand. + /// The whose signal to expand. /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Expand(this Vector4 gamma) { // TODO: Is there a faster way to do this? diff --git a/src/ImageSharp/Common/Helpers/DebugGuard.cs b/src/ImageSharp/Common/Helpers/DebugGuard.cs index c1fa461913..f6941fc6fc 100644 --- a/src/ImageSharp/Common/Helpers/DebugGuard.cs +++ b/src/ImageSharp/Common/Helpers/DebugGuard.cs @@ -159,5 +159,45 @@ namespace ImageSharp throw new ArgumentException(message, parameterName); } } + + /// + /// Verifies, that the target span is of same size than the 'other' span. + /// + /// The element type of the spans + /// The target span. + /// The 'other' span to compare 'target' to. + /// The name of the parameter that is to be checked. + /// + /// is true + /// + [Conditional("DEBUG")] + public static void MustBeSameSized(Span target, Span other, string parameterName) + where T : struct + { + if (target.Length != other.Length) + { + throw new ArgumentException("Span-s must be the same size!", parameterName); + } + } + + /// + /// Verifies, that the `target` span has the length of 'minSpan', or longer. + /// + /// The element type of the spans + /// The target span. + /// The 'minSpan' span to compare 'target' to. + /// The name of the parameter that is to be checked. + /// + /// is true + /// + [Conditional("DEBUG")] + public static void MustBeSizedAtLeast(Span target, Span minSpan, string parameterName) + where T : struct + { + if (target.Length < minSpan.Length) + { + throw new ArgumentException($"Span-s must be at least of length {minSpan.Length}!", parameterName); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs index cf307e9365..a4b392fcf1 100644 --- a/src/ImageSharp/Common/Helpers/Guard.cs +++ b/src/ImageSharp/Common/Helpers/Guard.cs @@ -230,5 +230,24 @@ namespace ImageSharp throw new ArgumentException(message, parameterName); } } + + /// + /// Verifies, that the `target` span has the length of 'minSpan', or longer. + /// + /// The element type of the spans + /// The target span. + /// The minimum length. + /// The name of the parameter that is to be checked. + /// + /// is true + /// + public static void MustBeSizedAtLeast(Span target, int minLength, string parameterName) + where T : struct + { + if (target.Length < minLength) + { + throw new ArgumentException($"Span-s must be at least of length {minLength}!", parameterName); + } + } } } diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs index 224b267e40..0bfcec3616 100644 --- a/src/ImageSharp/Common/Helpers/ImageMaths.cs +++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs @@ -10,6 +10,8 @@ namespace ImageSharp using System.Numerics; using System.Runtime.CompilerServices; + using ImageSharp.PixelFormats; + /// /// Provides common mathematical methods. /// @@ -100,25 +102,6 @@ namespace ImageSharp return 0F; } - /// - /// Gets the result of a sine cardinal function for the given value. - /// - /// The value to calculate the result for. - /// - /// The . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float SinC(float x) - { - if (MathF.Abs(x) > Constants.Epsilon) - { - x *= MathF.PI; - return Clean(MathF.Sin(x) / x); - } - - return 1.0f; - } - /// /// Returns the given degrees converted to radians. /// @@ -175,22 +158,22 @@ namespace ImageSharp /// Finds the bounding rectangle based on the first instance of any color component other /// than the given one. /// - /// The pixel format. - /// The to search within. + /// The pixel format. + /// The to search within. /// The color component value to remove. /// The channel to test against. /// /// The . /// - public static Rectangle GetFilteredBoundingRectangle(ImageBase bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B) - where TColor : struct, IPixel + public static Rectangle GetFilteredBoundingRectangle(ImageBase bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B) + where TPixel : struct, IPixel { int width = bitmap.Width; int height = bitmap.Height; Point topLeft = default(Point); Point bottomRight = default(Point); - Func, int, int, float, bool> delegateFunc; + Func, int, int, float, bool> delegateFunc; // Determine which channel to check against switch (channel) @@ -212,7 +195,7 @@ namespace ImageSharp break; } - Func, int> getMinY = pixels => + int GetMinY(PixelAccessor pixels) { for (int y = 0; y < height; y++) { @@ -226,9 +209,9 @@ namespace ImageSharp } return 0; - }; + } - Func, int> getMaxY = pixels => + int GetMaxY(PixelAccessor pixels) { for (int y = height - 1; y > -1; y--) { @@ -242,9 +225,9 @@ namespace ImageSharp } return height; - }; + } - Func, int> getMinX = pixels => + int GetMinX(PixelAccessor pixels) { for (int x = 0; x < width; x++) { @@ -258,9 +241,9 @@ namespace ImageSharp } return 0; - }; + } - Func, int> getMaxX = pixels => + int GetMaxX(PixelAccessor pixels) { for (int x = width - 1; x > -1; x--) { @@ -274,35 +257,17 @@ namespace ImageSharp } return height; - }; - - using (PixelAccessor bitmapPixels = bitmap.Lock()) - { - topLeft.Y = getMinY(bitmapPixels); - topLeft.X = getMinX(bitmapPixels); - bottomRight.Y = (getMaxY(bitmapPixels) + 1).Clamp(0, height); - bottomRight.X = (getMaxX(bitmapPixels) + 1).Clamp(0, width); } - return GetBoundingRectangle(topLeft, bottomRight); - } - - /// - /// Ensures that any passed double is correctly rounded to zero - /// - /// The value to clean. - /// - /// The - /// . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static float Clean(float x) - { - if (MathF.Abs(x) < Constants.Epsilon) + using (PixelAccessor bitmapPixels = bitmap.Lock()) { - return 0F; + topLeft.Y = GetMinY(bitmapPixels); + topLeft.X = GetMinX(bitmapPixels); + bottomRight.Y = (GetMaxY(bitmapPixels) + 1).Clamp(0, height); + bottomRight.X = (GetMaxX(bitmapPixels) + 1).Clamp(0, width); } - return x; + return GetBoundingRectangle(topLeft, bottomRight); } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Helpers/MathF.cs b/src/ImageSharp/Common/Helpers/MathF.cs index 2ee700789c..1877fe8afe 100644 --- a/src/ImageSharp/Common/Helpers/MathF.cs +++ b/src/ImageSharp/Common/Helpers/MathF.cs @@ -126,6 +126,26 @@ namespace ImageSharp return (float)Math.Sin(f); } + /// + /// Returns the result of a normalized sine cardinal function for the given value. + /// SinC(x) = sin(pi*x)/(pi*x). + /// + /// A single-precision floating-point number to calculate the result for. + /// + /// The sine cardinal of . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float SinC(float f) + { + if (Abs(f) > Constants.Epsilon) + { + f *= PI; + return Clean(Sin(f) / f); + } + + return 1F; + } + /// Returns the square root of a specified number. /// The number whose square root is to be found. /// @@ -140,5 +160,23 @@ namespace ImageSharp { return (float)Math.Sqrt(f); } + + /// + /// Ensures that any passed float is correctly rounded to zero + /// + /// The value to clean. + /// + /// The + /// . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float Clean(float x) + { + if (Abs(x) < Constants.Epsilon) + { + return 0F; + } + + return x; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/BufferSpan.cs b/src/ImageSharp/Common/Memory/BufferSpan.cs deleted file mode 100644 index 42a6fbc6be..0000000000 --- a/src/ImageSharp/Common/Memory/BufferSpan.cs +++ /dev/null @@ -1,129 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - /// - /// Utility methods for - /// - internal static class BufferSpan - { - /// - /// It's worth to use Marshal.Copy() or Buffer.BlockCopy() over this size. - /// - private const int ByteCountThreshold = 1024; - - /// - /// Copy 'count' number of elements of the same type from 'source' to 'dest' - /// - /// The element type. - /// The input - /// The destination . - /// The number of elements to copy - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Copy(BufferSpan source, BufferSpan destination, int count) - where T : struct - { - CopyImpl(source, destination, count); - } - - /// - /// Copy 'countInSource' elements of from 'source' into the raw byte buffer 'destination'. - /// - /// The element type. - /// The source buffer of elements to copy from. - /// The destination buffer. - /// The number of elements to copy from 'source' - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Copy(BufferSpan source, BufferSpan destination, int countInSource) - where T : struct - { - CopyImpl(source, destination, countInSource); - } - - /// - /// Copy 'countInDest' number of elements into 'dest' from a raw byte buffer defined by 'source'. - /// - /// The element type. - /// The raw source buffer to copy from"/> - /// The destination buffer"/> - /// The number of elements to copy. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Copy(BufferSpan source, BufferSpan destination, int countInDest) - where T : struct - { - int byteCount = SizeOf(countInDest); - - if (byteCount > (int)ByteCountThreshold) - { - Marshal.Copy(source.Array, source.Start, destination.PointerAtOffset, byteCount); - } - else - { - Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)byteCount); - } - } - - /// - /// Gets the size of `count` elements in bytes. - /// - /// The element type. - /// The count of the elements - /// The size in bytes as int - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int SizeOf(int count) - where T : struct => Unsafe.SizeOf() * count; - - /// - /// Gets the size of `count` elements in bytes as UInt32 - /// - /// The element type. - /// The count of the elements - /// The size in bytes as UInt32 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint USizeOf(int count) - where T : struct - => (uint)SizeOf(count); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe void CopyImpl(BufferSpan source, BufferSpan destination, int count) - where T : struct - where TDest : struct - { - int byteCount = SizeOf(count); - - if (byteCount > ByteCountThreshold) - { - if (Unsafe.SizeOf() == sizeof(long)) - { - Marshal.Copy(Unsafe.As(source.Array), source.Start, destination.PointerAtOffset, count); - return; - } - else if (Unsafe.SizeOf() == sizeof(int)) - { - Marshal.Copy(Unsafe.As(source.Array), source.Start, destination.PointerAtOffset, count); - return; - } - else if (Unsafe.SizeOf() == sizeof(short)) - { - Marshal.Copy(Unsafe.As(source.Array), source.Start, destination.PointerAtOffset, count); - return; - } - else if (Unsafe.SizeOf() == sizeof(byte)) - { - Marshal.Copy(Unsafe.As(source.Array), source.Start, destination.PointerAtOffset, count); - return; - } - } - - Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)byteCount); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/BufferSpan{T}.cs b/src/ImageSharp/Common/Memory/BufferSpan{T}.cs deleted file mode 100644 index 8ef88814cd..0000000000 --- a/src/ImageSharp/Common/Memory/BufferSpan{T}.cs +++ /dev/null @@ -1,237 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Diagnostics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - /// - /// Represents a contiguous region of a pinned managed array. - /// The array is usually owned by a instance. - /// - /// - /// is very similar to corefx System.Span<T>, and we try to maintain a compatible API. - /// There are several differences though: - /// - It's not possible to use it with stack objects or pointers to unmanaged memory, only with managed arrays. - /// - It's possible to retrieve a reference to the array () so we can pass it to API-s like - /// - It's possible to retrieve the pinned pointer. This enables optimized (unchecked) unsafe operations. - /// - There is no bounds checking for performance reasons, only in debug mode. This makes an unsafe type! - /// - /// The type of elements of the array - internal unsafe struct BufferSpan - where T : struct - { - /// - /// Initializes a new instance of the struct from a pinned array and an start. - /// - /// The pinned array - /// Pointer to the beginning of the array - /// The index at which to begin the span. - /// The length - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BufferSpan(T[] array, void* pointerToArray, int start, int length) - { - GuardArrayAndPointer(array, pointerToArray); - - DebugGuard.MustBeLessThanOrEqualTo(start, array.Length, nameof(start)); - DebugGuard.MustBeLessThanOrEqualTo(length, array.Length - start, nameof(length)); - - this.Array = array; - this.Length = length; - this.Start = start; - this.PointerAtOffset = (IntPtr)pointerToArray + (Unsafe.SizeOf() * start); - } - - /// - /// Initializes a new instance of the struct from a pinned array and an start. - /// - /// The pinned array - /// Pointer to the beginning of the array - /// The index at which to begin the span. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BufferSpan(T[] array, void* pointerToArray, int start) - { - GuardArrayAndPointer(array, pointerToArray); - DebugGuard.MustBeLessThanOrEqualTo(start, array.Length, nameof(start)); - - this.Array = array; - this.Length = array.Length - start; - this.Start = start; - this.PointerAtOffset = (IntPtr)pointerToArray + (Unsafe.SizeOf() * start); - } - - /// - /// Initializes a new instance of the struct from a pinned array. - /// - /// The pinned array - /// Pointer to the start of 'array' - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BufferSpan(T[] array, void* pointerToArray) - { - GuardArrayAndPointer(array, pointerToArray); - - this.Array = array; - this.Start = 0; - this.Length = array.Length; - this.PointerAtOffset = (IntPtr)pointerToArray; - } - - /// - /// Gets the backing array - /// - public T[] Array { get; private set; } - - /// - /// Gets the length of the - /// - public int Length { get; private set; } - - /// - /// Gets the start inside - /// - public int Start { get; private set; } - - /// - /// Gets the start inside in bytes. - /// - public int ByteOffset => this.Start * Unsafe.SizeOf(); - - /// - /// Gets the pointer to the offseted array position - /// - public IntPtr PointerAtOffset { get; private set; } - - /// - /// Returns a reference to specified element of the span. - /// - /// The index - /// The reference to the specified element - public ref T this[int index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - DebugGuard.MustBeLessThan(index, this.Length, nameof(index)); - - byte* ptr = (byte*)this.PointerAtOffset + BufferSpan.SizeOf(index); - return ref Unsafe.AsRef(ptr); - } - } - - /// - /// Convertes instance to a raw 'void*' pointer - /// - /// The to convert - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator void*(BufferSpan bufferSpan) - { - return (void*)bufferSpan.PointerAtOffset; - } - - /// - /// Converts instance to a raw 'byte*' pointer - /// - /// The to convert - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator byte*(BufferSpan bufferSpan) - { - return (byte*)bufferSpan.PointerAtOffset; - } - - /// - /// Converts generic to a of bytes - /// setting it's and to correct values. - /// - /// The to convert - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator BufferSpan(BufferSpan source) - { - BufferSpan result = default(BufferSpan); - result.Array = Unsafe.As(source.Array); - result.Start = source.Start * Unsafe.SizeOf(); - result.PointerAtOffset = source.PointerAtOffset; - return result; - } - - /// - /// Forms a slice out of the given BufferSpan, beginning at 'start'. - /// - /// TThe index at which to begin this slice. - /// The offseted (sliced) BufferSpan - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BufferSpan Slice(int start) - { - DebugGuard.MustBeLessThan(start, this.Length, nameof(start)); - - BufferSpan result = default(BufferSpan); - result.Array = this.Array; - result.Start = this.Start + start; - result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf() * start); - result.Length = this.Length - start; - return result; - } - - /// - /// Forms a slice out of the given BufferSpan, beginning at 'start'. - /// - /// The index at which to begin this slice. - /// The desired length for the slice (exclusive). - /// The sliced BufferSpan - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BufferSpan Slice(int start, int length) - { - DebugGuard.MustBeLessThanOrEqualTo(start, this.Length, nameof(start)); - DebugGuard.MustBeLessThanOrEqualTo(length, this.Length - start, nameof(length)); - - BufferSpan result = default(BufferSpan); - result.Array = this.Array; - result.Start = this.Start + start; - result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf() * start); - result.Length = length; - return result; - } - - /// - /// Clears `count` elements from the beginning of the span. - /// - /// The number of elements to clear - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Clear(int count) - { - DebugGuard.MustBeLessThanOrEqualTo(count, this.Length, nameof(count)); - - if (count < 256) - { - Unsafe.InitBlock((void*)this.PointerAtOffset, 0, BufferSpan.USizeOf(count)); - } - else - { - System.Array.Clear(this.Array, this.Start, count); - } - } - - /// - /// Clears the the span - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Clear() - { - this.Clear(this.Length); - } - - [Conditional("DEBUG")] - private static void GuardArrayAndPointer(T[] array, void* pointerToArray) - { - DebugGuard.NotNull(array, nameof(array)); - DebugGuard.IsFalse( - pointerToArray == (void*)0, - nameof(pointerToArray), - "pointerToArray should not be null pointer!"); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs index b94b872552..629944ea15 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Atkinson.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Dithering { + using ImageSharp.Memory; + /// /// Applies error diffusion based dithering using the Atkinson image dithering algorithm. /// diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs index 894b6e236e..02d41c369e 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Burks.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Dithering { + using ImageSharp.Memory; + /// /// Applies error diffusion based dithering using the Burks image dithering algorithm. /// diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs index cde146f1e5..7a5fabdb3a 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuser.cs @@ -8,6 +8,9 @@ namespace ImageSharp.Dithering using System.Numerics; using System.Runtime.CompilerServices; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// The base class for performing error diffusion based dithering. /// @@ -68,11 +71,22 @@ namespace ImageSharp.Dithering /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dither(PixelAccessor pixels, TColor source, TColor transformed, int x, int y, int width, int height) - where TColor : struct, IPixel + public void Dither(PixelAccessor pixels, TPixel source, TPixel transformed, int x, int y, int width, int height) + where TPixel : struct, IPixel + { + this.Dither(pixels, source, transformed, x, y, width, height, true); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dither(PixelAccessor pixels, TPixel source, TPixel transformed, int x, int y, int width, int height, bool replacePixel) + where TPixel : struct, IPixel { - // Assign the transformed pixel to the array. - pixels[x, y] = transformed; + if (replacePixel) + { + // Assign the transformed pixel to the array. + pixels[x, y] = transformed; + } // Calculate the error Vector4 error = source.ToVector4() - transformed.ToVector4(); @@ -102,7 +116,7 @@ namespace ImageSharp.Dithering Vector4 result = ((error * coefficientVector) / this.divisorVector) + offsetColor; result.W = offsetColor.W; - TColor packed = default(TColor); + TPixel packed = default(TPixel); packed.PackFromVector4(result); pixels[matrixX, matrixY] = packed; } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs index f7a93667fe..6165da6344 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/FloydSteinberg.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Dithering { + using ImageSharp.Memory; + /// /// Applies error diffusion based dithering using the Floyd–Steinberg image dithering algorithm. /// diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs index 18079b1fb2..f49e7e62d2 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/IErrorDiffuser.cs @@ -5,7 +5,7 @@ namespace ImageSharp.Dithering { - using System; + using ImageSharp.PixelFormats; /// /// Encapsulates properties and methods required to perfom diffused error dithering on an image. @@ -22,8 +22,26 @@ namespace ImageSharp.Dithering /// The row index. /// The image width. /// The image height. - /// The pixel format. - void Dither(PixelAccessor pixels, TColor source, TColor transformed, int x, int y, int width, int height) - where TColor : struct, IPixel; + /// The pixel format. + void Dither(PixelAccessor pixels, TPixel source, TPixel transformed, int x, int y, int width, int height) + where TPixel : struct, IPixel; + + /// + /// Transforms the image applying the dither matrix. This method alters the input pixels array + /// + /// The pixel accessor + /// The source pixel + /// The transformed pixel + /// The column index. + /// The row index. + /// The image width. + /// The image height. + /// + /// Whether to replace the pixel at the given coordinates with the transformed value. + /// Generally this would be true for standard two-color dithering but when used in conjunction with color quantization this should be false. + /// + /// The pixel format. + void Dither(PixelAccessor pixels, TPixel source, TPixel transformed, int x, int y, int width, int height, bool replacePixel) + where TPixel : struct, IPixel; } } diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs index 60fef81216..6daeab32f7 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/JarvisJudiceNinke.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Dithering { + using ImageSharp.Memory; + /// /// Applies error diffusion based dithering using the JarvisJudiceNinke image dithering algorithm. /// diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs index 4325438e08..0c0944d0eb 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra2.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Dithering { + using ImageSharp.Memory; + /// /// Applies error diffusion based dithering using the Sierra2 image dithering algorithm. /// diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs index 25ea70d0a6..2e22208462 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Sierra3.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Dithering { + using ImageSharp.Memory; + /// /// Applies error diffusion based dithering using the Sierra3 image dithering algorithm. /// diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs index c7b1d214f1..fe4c933a9f 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/SierraLite.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Dithering { + using ImageSharp.Memory; + /// /// Applies error diffusion based dithering using the SierraLite image dithering algorithm. /// diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs b/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs index 93258c3508..b04c164814 100644 --- a/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs +++ b/src/ImageSharp/Dithering/ErrorDiffusion/Stucki.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Dithering { + using ImageSharp.Memory; + /// /// Applies error diffusion based dithering using the Stucki image dithering algorithm. /// diff --git a/src/ImageSharp/Dithering/Ordered/Bayer.cs b/src/ImageSharp/Dithering/Ordered/Bayer.cs index 3792c3c023..ded17d1e10 100644 --- a/src/ImageSharp/Dithering/Ordered/Bayer.cs +++ b/src/ImageSharp/Dithering/Ordered/Bayer.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Dithering.Ordered { + using ImageSharp.Memory; + /// /// Applies error diffusion based dithering using the 4x4 Bayer dithering matrix. /// diff --git a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs index 5c98973747..3f7cf49885 100644 --- a/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs +++ b/src/ImageSharp/Dithering/Ordered/IOrderedDither.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Dithering { + using ImageSharp.PixelFormats; + /// /// Encapsulates properties and methods required to perfom ordered dithering on an image. /// @@ -23,8 +25,8 @@ namespace ImageSharp.Dithering /// The row index. /// The image width. /// The image height. - /// The pixel format. - void Dither(PixelAccessor pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height) - where TColor : struct, IPixel; + /// The pixel format. + void Dither(PixelAccessor pixels, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y, int width, int height) + where TPixel : struct, IPixel; } } \ No newline at end of file diff --git a/src/ImageSharp/Dithering/Ordered/Ordered.cs b/src/ImageSharp/Dithering/Ordered/Ordered.cs index ae75b87f21..1fd39eb8ba 100644 --- a/src/ImageSharp/Dithering/Ordered/Ordered.cs +++ b/src/ImageSharp/Dithering/Ordered/Ordered.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Dithering.Ordered { + using ImageSharp.Memory; + /// /// Applies error diffusion based dithering using the 4x4 ordered dithering matrix. /// diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDither4x4.cs b/src/ImageSharp/Dithering/Ordered/OrderedDither4x4.cs index c2b55d98eb..48d6c3f6a4 100644 --- a/src/ImageSharp/Dithering/Ordered/OrderedDither4x4.cs +++ b/src/ImageSharp/Dithering/Ordered/OrderedDither4x4.cs @@ -5,6 +5,9 @@ namespace ImageSharp.Dithering.Ordered { + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// The base class for performing ordered ditheroing using a 4x4 matrix. /// @@ -25,8 +28,8 @@ namespace ImageSharp.Dithering.Ordered } /// - public void Dither(PixelAccessor pixels, TColor source, TColor upper, TColor lower, byte[] bytes, int index, int x, int y, int width, int height) - where TColor : struct, IPixel + public void Dither(PixelAccessor pixels, TPixel source, TPixel upper, TPixel lower, byte[] bytes, int index, int x, int y, int width, int height) + where TPixel : struct, IPixel { // TODO: This doesn't really cut it for me. // I'd rather be using float but we need to add some sort of movalization vector methods to all IPixel implementations diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index da5d246372..9090e9a8cd 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -8,6 +8,8 @@ namespace ImageSharp.Formats using System; using System.IO; + using ImageSharp.PixelFormats; + /// /// Image decoder for generating an image out of a Windows bitmap stream. /// @@ -26,13 +28,13 @@ namespace ImageSharp.Formats public class BmpDecoder : IImageDecoder { /// - public Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) + public Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) - where TColor : struct, IPixel + where TPixel : struct, IPixel { Guard.NotNull(stream, "stream"); - return new BmpDecoderCore(configuration).Decode(stream); + return new BmpDecoderCore(configuration).Decode(stream); } } } diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 18e4e858b8..dd91aa11d9 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Formats using System; using System.IO; + using ImageSharp.PixelFormats; + /// /// Performs the bmp decoding operation. /// @@ -58,15 +60,15 @@ namespace ImageSharp.Formats /// Decodes the image from the specified this._stream and sets /// the data to image. /// - /// The pixel format. + /// The pixel format. /// The stream, where the image should be /// decoded from. Cannot be null (Nothing in Visual Basic). /// /// is null. /// /// The decoded image. - public Image Decode(Stream stream) - where TColor : struct, IPixel + public Image Decode(Stream stream) + where TPixel : struct, IPixel { this.currentStream = stream; @@ -118,15 +120,15 @@ namespace ImageSharp.Formats this.currentStream.Read(palette, 0, colorMapSize); } - if (this.infoHeader.Width > Image.MaxWidth || this.infoHeader.Height > Image.MaxHeight) + if (this.infoHeader.Width > Image.MaxWidth || this.infoHeader.Height > Image.MaxHeight) { throw new ArgumentOutOfRangeException( $"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is " - + $"bigger then the max allowed size '{Image.MaxWidth}x{Image.MaxHeight}'"); + + $"bigger then the max allowed size '{Image.MaxWidth}x{Image.MaxHeight}'"); } - Image image = Image.Create(this.infoHeader.Width, this.infoHeader.Height, this.configuration); - using (PixelAccessor pixels = image.Lock()) + Image image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height); + using (PixelAccessor pixels = image.Lock()) { switch (this.infoHeader.Compression) { @@ -213,15 +215,15 @@ namespace ImageSharp.Formats /// /// Reads the color palette from the stream. /// - /// The pixel format. - /// The to assign the palette to. + /// The pixel format. + /// The to assign the palette to. /// The containing the colors. /// The width of the bitmap. /// The height of the bitmap. /// The number of bits per pixel. /// Whether the bitmap is inverted. - private void ReadRgbPalette(PixelAccessor pixels, byte[] colors, int width, int height, int bits, bool inverted) - where TColor : struct, IPixel + private void ReadRgbPalette(PixelAccessor pixels, byte[] colors, int width, int height, int bits, bool inverted) + where TPixel : struct, IPixel { // Pixels per byte (bits per pixel) int ppb = 8 / bits; @@ -239,7 +241,7 @@ namespace ImageSharp.Formats } byte[] row = new byte[arrayWidth + padding]; - TColor color = default(TColor); + TPixel color = default(TPixel); for (int y = 0; y < height; y++) { @@ -270,21 +272,21 @@ namespace ImageSharp.Formats /// /// Reads the 16 bit color palette from the stream /// - /// The pixel format. - /// The to assign the palette to. + /// The pixel format. + /// The to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. - private void ReadRgb16(PixelAccessor pixels, int width, int height, bool inverted) - where TColor : struct, IPixel + private void ReadRgb16(PixelAccessor pixels, int width, int height, bool inverted) + where TPixel : struct, IPixel { // We divide here as we will store the colors in our floating point format. const int ScaleR = 8; // 256/32 const int ScaleG = 4; // 256/64 const int ComponentCount = 2; - TColor color = default(TColor); - using (PixelArea row = new PixelArea(width, ComponentOrder.Xyz)) + TPixel color = default(TPixel); + using (PixelArea row = new PixelArea(width, ComponentOrder.Xyz)) { for (int y = 0; y < height; y++) { @@ -312,16 +314,16 @@ namespace ImageSharp.Formats /// /// Reads the 24 bit color palette from the stream /// - /// The pixel format. - /// The to assign the palette to. + /// The pixel format. + /// The to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. - private void ReadRgb24(PixelAccessor pixels, int width, int height, bool inverted) - where TColor : struct, IPixel + private void ReadRgb24(PixelAccessor pixels, int width, int height, bool inverted) + where TPixel : struct, IPixel { int padding = CalculatePadding(width, 3); - using (PixelArea row = new PixelArea(width, ComponentOrder.Zyx, padding)) + using (PixelArea row = new PixelArea(width, ComponentOrder.Zyx, padding)) { for (int y = 0; y < height; y++) { @@ -336,16 +338,16 @@ namespace ImageSharp.Formats /// /// Reads the 32 bit color palette from the stream /// - /// The pixel format. - /// The to assign the palette to. + /// The pixel format. + /// The to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. - private void ReadRgb32(PixelAccessor pixels, int width, int height, bool inverted) - where TColor : struct, IPixel + private void ReadRgb32(PixelAccessor pixels, int width, int height, bool inverted) + where TPixel : struct, IPixel { int padding = CalculatePadding(width, 4); - using (PixelArea row = new PixelArea(width, ComponentOrder.Zyxw, padding)) + using (PixelArea row = new PixelArea(width, ComponentOrder.Zyxw, padding)) { for (int y = 0; y < height; y++) { diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs index d0a3550f6f..dc2bc0e972 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs @@ -8,6 +8,8 @@ namespace ImageSharp.Formats using System; using System.IO; + using ImageSharp.PixelFormats; + /// /// Image encoder for writing an image to a stream as a Windows bitmap. /// @@ -15,8 +17,8 @@ namespace ImageSharp.Formats public class BmpEncoder : IImageEncoder { /// - public void Encode(Image image, Stream stream, IEncoderOptions options) - where TColor : struct, IPixel + public void Encode(Image image, Stream stream, IEncoderOptions options) + where TPixel : struct, IPixel { IBmpEncoderOptions bmpOptions = BmpEncoderOptions.Create(options); @@ -24,14 +26,14 @@ namespace ImageSharp.Formats } /// - /// Encodes the image to the specified stream from the . + /// Encodes the image to the specified stream from the . /// - /// The pixel format. - /// The to encode from. + /// The pixel format. + /// The to encode from. /// The to encode the image data to. /// The options for the encoder. - public void Encode(Image image, Stream stream, IBmpEncoderOptions options) - where TColor : struct, IPixel + public void Encode(Image image, Stream stream, IBmpEncoderOptions options) + where TPixel : struct, IPixel { BmpEncoderCore encoder = new BmpEncoderCore(options); encoder.Encode(image, stream); diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index df62fb6f40..617edde8eb 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -8,6 +8,8 @@ namespace ImageSharp.Formats using System; using System.IO; + using ImageSharp.PixelFormats; + using IO; /// @@ -35,13 +37,13 @@ namespace ImageSharp.Formats } /// - /// Encodes the image to the specified stream from the . + /// Encodes the image to the specified stream from the . /// - /// The pixel format. - /// The to encode from. + /// The pixel format. + /// The to encode from. /// The to encode the image data to. - public void Encode(ImageBase image, Stream stream) - where TColor : struct, IPixel + public void Encode(ImageBase image, Stream stream) + where TPixel : struct, IPixel { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); @@ -124,15 +126,15 @@ namespace ImageSharp.Formats /// /// Writes the pixel data to the binary stream. /// - /// The pixel format. + /// The pixel format. /// The containing the stream to write to. /// - /// The containing pixel data. + /// The containing pixel data. /// - private void WriteImage(EndianBinaryWriter writer, ImageBase image) - where TColor : struct, IPixel + private void WriteImage(EndianBinaryWriter writer, ImageBase image) + where TPixel : struct, IPixel { - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { switch (this.options.BitsPerPixel) { @@ -150,13 +152,13 @@ namespace ImageSharp.Formats /// /// Writes the 32bit color palette to the stream. /// - /// The pixel format. + /// The pixel format. /// The containing the stream to write to. - /// The containing pixel data. - private void Write32Bit(EndianBinaryWriter writer, PixelAccessor pixels) - where TColor : struct, IPixel + /// The containing pixel data. + private void Write32Bit(EndianBinaryWriter writer, PixelAccessor pixels) + where TPixel : struct, IPixel { - using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyxw, this.padding)) + using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyxw, this.padding)) { for (int y = pixels.Height - 1; y >= 0; y--) { @@ -169,13 +171,13 @@ namespace ImageSharp.Formats /// /// Writes the 24bit color palette to the stream. /// - /// The pixel format. + /// The pixel format. /// The containing the stream to write to. - /// The containing pixel data. - private void Write24Bit(EndianBinaryWriter writer, PixelAccessor pixels) - where TColor : struct, IPixel + /// The containing pixel data. + private void Write24Bit(EndianBinaryWriter writer, PixelAccessor pixels) + where TPixel : struct, IPixel { - using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyx, this.padding)) + using (PixelArea row = new PixelArea(pixels.Width, ComponentOrder.Zyx, this.padding)) { for (int y = pixels.Height - 1; y >= 0; y--) { diff --git a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs index 5b92b90d63..aba24f9997 100644 --- a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs @@ -10,23 +10,25 @@ namespace ImageSharp using Formats; + using ImageSharp.PixelFormats; + /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Saves the image to the given stream with the bmp format. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. /// - /// The . + /// The . /// - public static Image SaveAsBmp(this Image source, Stream stream) - where TColor : struct, IPixel + public static Image SaveAsBmp(this Image source, Stream stream) + where TPixel : struct, IPixel => source.Save(stream, new BmpEncoder()); } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 2eb89de8ff..88aaccf6a4 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -8,33 +8,35 @@ namespace ImageSharp.Formats using System; using System.IO; + using ImageSharp.PixelFormats; + /// /// Decoder for generating an image out of a gif encoded stream. /// public class GifDecoder : IImageDecoder { /// - public Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) + public Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) - where TColor : struct, IPixel + where TPixel : struct, IPixel { IGifDecoderOptions gifOptions = GifDecoderOptions.Create(options); - return this.Decode(configuration, stream, gifOptions); + return this.Decode(configuration, stream, gifOptions); } /// - /// Decodes the image from the specified stream to the . + /// Decodes the image from the specified stream to the . /// - /// The pixel format. + /// The pixel format. /// The configuration. /// The containing image data. /// The options for the decoder. /// The image thats been decoded. - public Image Decode(Configuration configuration, Stream stream, IGifDecoderOptions options) - where TColor : struct, IPixel + public Image Decode(Configuration configuration, Stream stream, IGifDecoderOptions options) + where TPixel : struct, IPixel { - return new GifDecoderCore(options, configuration).Decode(stream); + return new GifDecoderCore(options, configuration).Decode(stream); } } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 4c119ca737..272e4d0750 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -8,14 +8,17 @@ namespace ImageSharp.Formats using System; using System.Buffers; using System.IO; + using System.Runtime.CompilerServices; using System.Text; + using ImageSharp.PixelFormats; + /// /// Performs the gif decoding operation. /// - /// The pixel format. - internal class GifDecoderCore - where TColor : struct, IPixel + /// The pixel format. + internal class GifDecoderCore + where TPixel : struct, IPixel { /// /// The temp buffer used to reduce allocations. @@ -50,7 +53,7 @@ namespace ImageSharp.Formats /// /// The previous frame. /// - private ImageFrame previousFrame; + private ImageFrame previousFrame; /// /// The area to restore. @@ -75,10 +78,10 @@ namespace ImageSharp.Formats /// /// The image to decode the information to. /// - private Image image; + private Image image; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The decoder options. /// The configuration. @@ -93,7 +96,7 @@ namespace ImageSharp.Formats /// /// The stream containing image data. /// The decoded image - public Image Decode(Stream stream) + public Image Decode(Stream stream) { try { @@ -227,10 +230,10 @@ namespace ImageSharp.Formats } /* // No point doing this as the max width/height is always int.Max and that always bigger than the max size of a gif which is stored in a short. - if (this.logicalScreenDescriptor.Width > Image.MaxWidth || this.logicalScreenDescriptor.Height > Image.MaxHeight) + if (this.logicalScreenDescriptor.Width > Image.MaxWidth || this.logicalScreenDescriptor.Height > Image.MaxHeight) { throw new ArgumentOutOfRangeException( - $"The input gif '{this.logicalScreenDescriptor.Width}x{this.logicalScreenDescriptor.Height}' is bigger then the max allowed size '{Image.MaxWidth}x{Image.MaxHeight}'"); + $"The input gif '{this.logicalScreenDescriptor.Width}x{this.logicalScreenDescriptor.Height}' is bigger then the max allowed size '{Image.MaxWidth}x{Image.MaxHeight}'"); } */ } @@ -330,6 +333,7 @@ namespace ImageSharp.Formats /// /// The . /// The pixel array to write to. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ReadFrameIndices(GifImageDescriptor imageDescriptor, byte[] indices) { int dataSize = this.currentStream.ReadByte(); @@ -351,20 +355,20 @@ namespace ImageSharp.Formats int imageWidth = this.logicalScreenDescriptor.Width; int imageHeight = this.logicalScreenDescriptor.Height; - ImageFrame previousFrame = null; + ImageFrame previousFrame = null; - ImageFrame currentFrame = null; + ImageFrame currentFrame = null; - ImageBase image; + ImageBase image; if (this.previousFrame == null) { this.metaData.Quality = colorTableLength / 3; // This initializes the image to become fully transparent because the alpha channel is zero. - this.image = Image.Create(imageWidth, imageHeight, this.metaData, this.configuration); + this.image = new Image(this.configuration, imageWidth, imageHeight, this.metaData); - this.SetFrameDelay(this.metaData); + this.SetFrameMetaData(this.metaData); image = this.image; } @@ -378,7 +382,7 @@ namespace ImageSharp.Formats currentFrame = this.previousFrame.Clone(); - this.SetFrameDelay(currentFrame.MetaData); + this.SetFrameMetaData(currentFrame.MetaData); image = currentFrame; @@ -392,7 +396,7 @@ namespace ImageSharp.Formats int interlaceIncrement = 8; // The interlacing line increment int interlaceY = 0; // The current interlaced line - using (PixelAccessor pixelAccessor = image.Lock()) + using (PixelAccessor pixelAccessor = image.Lock()) { for (int y = descriptor.Top; y < descriptor.Top + descriptor.Height; y++) { @@ -441,7 +445,7 @@ namespace ImageSharp.Formats { int indexOffset = index * 3; - TColor pixel = default(TColor); + TPixel pixel = default(TPixel); pixel.PackFromBytes(colorTable[indexOffset], colorTable[indexOffset + 1], colorTable[indexOffset + 2], 255); pixelAccessor[x, writeY] = pixel; } @@ -470,7 +474,7 @@ namespace ImageSharp.Formats /// Restores the current frame area to the background. /// /// The frame. - private void RestoreToBackground(ImageBase frame) + private void RestoreToBackground(ImageBase frame) { if (this.restoreArea == null) { @@ -481,16 +485,16 @@ namespace ImageSharp.Formats if (this.restoreArea.Value.Width == this.image.Width && this.restoreArea.Value.Height == this.image.Height) { - using (PixelAccessor pixelAccessor = frame.Lock()) + using (PixelAccessor pixelAccessor = frame.Lock()) { pixelAccessor.Reset(); } } else { - using (PixelArea emptyRow = new PixelArea(this.restoreArea.Value.Width, ComponentOrder.Xyzw)) + using (PixelArea emptyRow = new PixelArea(this.restoreArea.Value.Width, ComponentOrder.Xyzw)) { - using (PixelAccessor pixelAccessor = frame.Lock()) + using (PixelAccessor pixelAccessor = frame.Lock()) { for (int y = this.restoreArea.Value.Top; y < this.restoreArea.Value.Top + this.restoreArea.Value.Height; y++) { @@ -504,14 +508,20 @@ namespace ImageSharp.Formats } /// - /// Sets the frame delay in the metadata. + /// Sets the frames metadata. /// /// The meta data. - private void SetFrameDelay(IMetaData metaData) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetFrameMetaData(IMetaData metaData) { - if (this.graphicsControlExtension != null && this.graphicsControlExtension.DelayTime > 0) + if (this.graphicsControlExtension != null) { - metaData.FrameDelay = this.graphicsControlExtension.DelayTime; + if (this.graphicsControlExtension.DelayTime > 0) + { + metaData.FrameDelay = this.graphicsControlExtension.DelayTime; + } + + metaData.DisposalMethod = this.graphicsControlExtension.DisposalMethod; } } } diff --git a/src/ImageSharp/Formats/Gif/GifEncoder.cs b/src/ImageSharp/Formats/Gif/GifEncoder.cs index cc8516ed9d..b5cadd834e 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoder.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoder.cs @@ -8,14 +8,16 @@ namespace ImageSharp.Formats using System; using System.IO; + using ImageSharp.PixelFormats; + /// /// Image encoder for writing image data to a stream in gif format. /// public class GifEncoder : IImageEncoder { /// - public void Encode(Image image, Stream stream, IEncoderOptions options) - where TColor : struct, IPixel + public void Encode(Image image, Stream stream, IEncoderOptions options) + where TPixel : struct, IPixel { IGifEncoderOptions gifOptions = GifEncoderOptions.Create(options); @@ -23,14 +25,14 @@ namespace ImageSharp.Formats } /// - /// Encodes the image to the specified stream from the . + /// Encodes the image to the specified stream from the . /// - /// The pixel format. - /// The to encode from. + /// The pixel format. + /// The to encode from. /// The to encode the image data to. /// The options for the encoder. - public void Encode(Image image, Stream stream, IGifEncoderOptions options) - where TColor : struct, IPixel + public void Encode(Image image, Stream stream, IGifEncoderOptions options) + where TPixel : struct, IPixel { GifEncoderCore encoder = new GifEncoderCore(options); encoder.Encode(image, stream); diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 38cbba8500..5dc1a150d0 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -10,6 +10,8 @@ namespace ImageSharp.Formats using System.IO; using System.Linq; + using ImageSharp.PixelFormats; + using IO; using Quantizers; @@ -33,6 +35,11 @@ namespace ImageSharp.Formats /// private int bitDepth; + /// + /// Whether the current image has multiple frames. + /// + private bool hasFrames; + /// /// Initializes a new instance of the class. /// @@ -48,18 +55,18 @@ namespace ImageSharp.Formats public IQuantizer Quantizer { get; set; } /// - /// Encodes the image to the specified stream from the . + /// Encodes the image to the specified stream from the . /// - /// The pixel format. - /// The to encode from. + /// The pixel format. + /// The to encode from. /// The to encode the image data to. - public void Encode(Image image, Stream stream) - where TColor : struct, IPixel + public void Encode(Image image, Stream stream) + where TPixel : struct, IPixel { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - this.Quantizer = this.options.Quantizer ?? new OctreeQuantizer(); + this.Quantizer = this.options.Quantizer ?? new OctreeQuantizer(); // Do not use IDisposable pattern here as we want to preserve the stream. EndianBinaryWriter writer = new EndianBinaryWriter(Endianness.LittleEndian, stream); @@ -72,7 +79,13 @@ namespace ImageSharp.Formats this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quality); // Quantize the image returning a palette. - QuantizedImage quantized = ((IQuantizer)this.Quantizer).Quantize(image, quality); + this.hasFrames = image.Frames.Any(); + + // Dithering when animating gifs is a bad idea as we introduce pixel tearing across frames. + IQuantizer ditheredQuantizer = (IQuantizer)this.Quantizer; + ditheredQuantizer.Dither = !this.hasFrames; + + QuantizedImage quantized = ditheredQuantizer.Quantize(image, quality); int index = this.GetTransparentIndex(quantized); @@ -90,15 +103,15 @@ namespace ImageSharp.Formats this.WriteImageData(quantized, writer); // Write additional frames. - if (image.Frames.Any()) + if (this.hasFrames) { this.WriteApplicationExtension(writer, image.MetaData.RepeatCount, image.Frames.Count); // ReSharper disable once ForCanBeConvertedToForeach for (int i = 0; i < image.Frames.Count; i++) { - ImageFrame frame = image.Frames[i]; - QuantizedImage quantizedFrame = ((IQuantizer)this.Quantizer).Quantize(frame, quality); + ImageFrame frame = image.Frames[i]; + QuantizedImage quantizedFrame = ditheredQuantizer.Quantize(frame, quality); this.WriteGraphicalControlExtension(frame, writer, this.GetTransparentIndex(quantizedFrame)); this.WriteImageDescriptor(frame, writer); @@ -117,12 +130,12 @@ namespace ImageSharp.Formats /// /// The quantized. /// - /// The pixel format. + /// The pixel format. /// /// The . /// - private int GetTransparentIndex(QuantizedImage quantized) - where TColor : struct, IPixel + private int GetTransparentIndex(QuantizedImage quantized) + where TPixel : struct, IPixel { // Find the lowest alpha value and make it the transparent index. int index = 255; @@ -167,18 +180,18 @@ namespace ImageSharp.Formats /// /// Writes the logical screen descriptor to the stream. /// - /// The pixel format. + /// The pixel format. /// The image to encode. /// The writer to write to the stream with. /// The transparency index to set the default background index to. - private void WriteLogicalScreenDescriptor(Image image, EndianBinaryWriter writer, int tranparencyIndex) - where TColor : struct, IPixel + private void WriteLogicalScreenDescriptor(Image image, EndianBinaryWriter writer, int tranparencyIndex) + where TPixel : struct, IPixel { GifLogicalScreenDescriptor descriptor = new GifLogicalScreenDescriptor { Width = (short)image.Width, Height = (short)image.Height, - GlobalColorTableFlag = false, // Always false for now. + GlobalColorTableFlag = false, // TODO: Always false for now. GlobalColorTableSize = this.bitDepth - 1, BackgroundColorIndex = (byte)tranparencyIndex }; @@ -222,8 +235,7 @@ namespace ImageSharp.Formats writer.Write((byte)1); // Data sub-block index (always 1) // 0 means loop indefinitely. Count is set as play n + 1 times. - repeatCount = (ushort)(Math.Max((ushort)0, repeatCount) - 1); - + repeatCount = (ushort)Math.Max(0, repeatCount - 1); writer.Write(repeatCount); // Repeat count for images. writer.Write(GifConstants.Terminator); // Terminator @@ -233,11 +245,11 @@ namespace ImageSharp.Formats /// /// Writes the image comments to the stream. /// - /// The pixel format. - /// The to be encoded. + /// The pixel format. + /// The to be encoded. /// The stream to write to. - private void WriteComments(Image image, EndianBinaryWriter writer) - where TColor : struct, IPixel + private void WriteComments(Image image, EndianBinaryWriter writer) + where TPixel : struct, IPixel { if (this.options.IgnoreMetadata == true) { @@ -266,12 +278,12 @@ namespace ImageSharp.Formats /// /// Writes the graphics control extension to the stream. /// - /// The pixel format. - /// The to encode. + /// The pixel format. + /// The to encode. /// The stream to write to. /// The index of the color in the color palette to make transparent. - private void WriteGraphicalControlExtension(Image image, EndianBinaryWriter writer, int transparencyIndex) - where TColor : struct, IPixel + private void WriteGraphicalControlExtension(Image image, EndianBinaryWriter writer, int transparencyIndex) + where TPixel : struct, IPixel { this.WriteGraphicalControlExtension(image, image.MetaData, writer, transparencyIndex); } @@ -279,12 +291,12 @@ namespace ImageSharp.Formats /// /// Writes the graphics control extension to the stream. /// - /// The pixel format. - /// The to encode. + /// The pixel format. + /// The to encode. /// The stream to write to. /// The index of the color in the color palette to make transparent. - private void WriteGraphicalControlExtension(ImageFrame imageFrame, EndianBinaryWriter writer, int transparencyIndex) - where TColor : struct, IPixel + private void WriteGraphicalControlExtension(ImageFrame imageFrame, EndianBinaryWriter writer, int transparencyIndex) + where TPixel : struct, IPixel { this.WriteGraphicalControlExtension(imageFrame, imageFrame.MetaData, writer, transparencyIndex); } @@ -292,24 +304,18 @@ namespace ImageSharp.Formats /// /// Writes the graphics control extension to the stream. /// - /// The pixel format. - /// The to encode. + /// The pixel format. + /// The to encode. /// The metadata of the image or frame. /// The stream to write to. /// The index of the color in the color palette to make transparent. - private void WriteGraphicalControlExtension(ImageBase image, IMetaData metaData, EndianBinaryWriter writer, int transparencyIndex) - where TColor : struct, IPixel + private void WriteGraphicalControlExtension(ImageBase image, IMetaData metaData, EndianBinaryWriter writer, int transparencyIndex) + where TPixel : struct, IPixel { - // TODO: Check transparency logic. - bool hasTransparent = transparencyIndex < 255; - DisposalMethod disposalMethod = hasTransparent - ? DisposalMethod.RestoreToBackground - : DisposalMethod.Unspecified; - GifGraphicsControlExtension extension = new GifGraphicsControlExtension() { - DisposalMethod = disposalMethod, - TransparencyFlag = hasTransparent, + DisposalMethod = metaData.DisposalMethod, + TransparencyFlag = transparencyIndex < 255, TransparencyIndex = transparencyIndex, DelayTime = metaData.FrameDelay }; @@ -336,11 +342,11 @@ namespace ImageSharp.Formats /// /// Writes the image descriptor to the stream. /// - /// The pixel format. - /// The to be encoded. + /// The pixel format. + /// The to be encoded. /// The stream to write to. - private void WriteImageDescriptor(ImageBase image, EndianBinaryWriter writer) - where TColor : struct, IPixel + private void WriteImageDescriptor(ImageBase image, EndianBinaryWriter writer) + where TPixel : struct, IPixel { writer.Write(GifConstants.ImageDescriptorLabel); // 2c @@ -362,11 +368,11 @@ namespace ImageSharp.Formats /// /// Writes the color table to the stream. /// - /// The pixel format. - /// The to encode. + /// The pixel format. + /// The to encode. /// The writer to write to the stream with. - private void WriteColorTable(QuantizedImage image, EndianBinaryWriter writer) - where TColor : struct, IPixel + private void WriteColorTable(QuantizedImage image, EndianBinaryWriter writer) + where TPixel : struct, IPixel { // Grab the palette and write it to the stream. int pixelCount = image.Palette.Length; @@ -397,11 +403,11 @@ namespace ImageSharp.Formats /// /// Writes the image pixel data to the stream. /// - /// The pixel format. - /// The containing indexed pixels. + /// The pixel format. + /// The containing indexed pixels. /// The stream to write to. - private void WriteImageData(QuantizedImage image, EndianBinaryWriter writer) - where TColor : struct, IPixel + private void WriteImageData(QuantizedImage image, EndianBinaryWriter writer) + where TPixel : struct, IPixel { using (LzwEncoder encoder = new LzwEncoder(image.Pixels, (byte)this.bitDepth)) { diff --git a/src/ImageSharp/Formats/Gif/ImageExtensions.cs b/src/ImageSharp/Formats/Gif/ImageExtensions.cs index 1ba03ed351..d64203f6ce 100644 --- a/src/ImageSharp/Formats/Gif/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Gif/ImageExtensions.cs @@ -10,23 +10,25 @@ namespace ImageSharp using Formats; + using ImageSharp.PixelFormats; + /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Saves the image to the given stream with the gif format. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. /// - /// The . + /// The . /// - public static Image SaveAsGif(this Image source, Stream stream) - where TColor : struct, IPixel + public static Image SaveAsGif(this Image source, Stream stream) + where TPixel : struct, IPixel { return SaveAsGif(source, stream, null); } @@ -34,16 +36,16 @@ namespace ImageSharp /// /// Saves the image to the given stream with the gif format. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. /// - /// The . + /// The . /// - public static Image SaveAsGif(this Image source, Stream stream, IGifEncoderOptions options) - where TColor : struct, IPixel + public static Image SaveAsGif(this Image source, Stream stream, IGifEncoderOptions options) + where TPixel : struct, IPixel { GifEncoder encoder = new GifEncoder(); encoder.Encode(source, stream, options); diff --git a/src/ImageSharp/Formats/Gif/spec-gif89a.txt b/src/ImageSharp/Formats/Gif/spec-gif89a.txt new file mode 100644 index 0000000000..64a07299b1 --- /dev/null +++ b/src/ImageSharp/Formats/Gif/spec-gif89a.txt @@ -0,0 +1,2476 @@ + + + + + Cover Sheet for the GIF89a Specification + + + DEFERRED CLEAR CODE IN LZW COMPRESSION + + There has been confusion about where clear codes can be found in the + data stream. As the specification says, they may appear at anytime. There + is not a requirement to send a clear code when the string table is full. + + It is the encoder's decision as to when the table should be cleared. When + the table is full, the encoder can chose to use the table as is, making no + changes to it until the encoder chooses to clear it. The encoder during + this time sends out codes that are of the maximum Code Size. + + As we can see from the above, when the decoder's table is full, it must + not change the table until a clear code is received. The Code Size is that + of the maximum Code Size. Processing other than this is done normally. + + Because of a large base of decoders that do not handle the decompression in + this manner, we ask developers of GIF encoding software to NOT implement + this feature until at least January 1991 and later if they see that their + particular market is not ready for it. This will give developers of GIF + decoding software time to implement this feature and to get it into the + hands of their clients before the decoders start "breaking" on the new + GIF's. It is not required that encoders change their software to take + advantage of the deferred clear code, but it is for decoders. + + APPLICATION EXTENSION BLOCK - APPLICATION IDENTIFIER + + There will be a Courtesy Directory file located on CompuServe in the PICS + forum. This directory will contain Application Identifiers for Application + Extension Blocks that have been used by developers of GIF applications. + This file is intended to help keep developers that wish to create + Application Extension Blocks from using the same Application Identifiers. + This is not an official directory; it is for voluntary participation only + and does not guarantee that someone will not use the same identifier. + + E-Mail can be sent to Larry Wood (forum manager of PICS) indicating the + request for inclusion in this file with an identifier. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GRAPHICS INTERCHANGE FORMAT(sm) + + Version 89a + + (c)1987,1988,1989,1990 + + Copyright + CompuServe Incorporated + Columbus, Ohio + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +CompuServe Incorporated Graphics Interchange Format +Document Date : 31 July 1990 Programming Reference + + + + + + + + + + + Table of Contents + +Disclaimer................................................................. 1 + +Foreword................................................................... 1 + +Licensing.................................................................. 1 + +About the Document......................................................... 2 + +General Description........................................................ 2 + +Version Numbers............................................................ 2 + +The Encoder................................................................ 3 + +The Decoder................................................................ 3 + +Compliance................................................................. 3 + +About Recommendations...................................................... 4 + +About Color Tables......................................................... 4 + +Blocks, Extensions and Scope............................................... 4 + +Block Sizes................................................................ 5 + +Using GIF as an embedded protocol.......................................... 5 + +Data Sub-blocks............................................................ 5 + +Block Terminator........................................................... 6 + +Header..................................................................... 7 + +Logical Screen Descriptor.................................................. 8 + +Global Color Table......................................................... 10 + +Image Descriptor........................................................... 11 + +Local Color Table.......................................................... 13 + +Table Based Image Data..................................................... 14 + +Graphic Control Extension.................................................. 15 + +Comment Extension.......................................................... 17 + +Plain Text Extension....................................................... 18 + +Application Extension...................................................... 21 + +Trailer.................................................................... 23 + + + + + + + + + + + +Quick Reference Table...................................................... 24 + +GIF Grammar................................................................ 25 + +Glossary................................................................... 27 + +Conventions................................................................ 28 + +Interlaced Images.......................................................... 29 + +Variable-Length-Code LZW Compression....................................... 30 + +On-line Capabilities Dialogue.............................................. 33 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + +1. Disclaimer. + +The information provided herein is subject to change without notice. In no +event will CompuServe Incorporated be liable for damages, including any loss of +revenue, loss of profits or other incidental or consequential damages arising +out of the use or inability to use the information; CompuServe Incorporated +makes no claim as to the suitability of the information. + + +2. Foreword. + +This document defines the Graphics Interchange Format(sm). The specification +given here defines version 89a, which is an extension of version 87a. + +The Graphics Interchange Format(sm) as specified here should be considered +complete; any deviation from it should be considered invalid, including but not +limited to, the use of reserved or undefined fields within control or data +blocks, the inclusion of extraneous data within or between blocks, the use of +methods or algorithms not specifically listed as part of the format, etc. In +general, any and all deviations, extensions or modifications not specified in +this document should be considered to be in violation of the format and should +be avoided. + + +3. Licensing. + +The Graphics Interchange Format(c) is the copyright property of CompuServe +Incorporated. Only CompuServe Incorporated is authorized to define, redefine, +enhance, alter, modify or change in any way the definition of the format. + +CompuServe Incorporated hereby grants a limited, non-exclusive, royalty-free +license for the use of the Graphics Interchange Format(sm) in computer +software; computer software utilizing GIF(sm) must acknowledge ownership of the +Graphics Interchange Format and its Service Mark by CompuServe Incorporated, in +User and Technical Documentation. Computer software utilizing GIF, which is +distributed or may be distributed without User or Technical Documentation must +display to the screen or printer a message acknowledging ownership of the +Graphics Interchange Format and the Service Mark by CompuServe Incorporated; in +this case, the acknowledgement may be displayed in an opening screen or leading +banner, or a closing screen or trailing banner. A message such as the following +may be used: + + "The Graphics Interchange Format(c) is the Copyright property of + CompuServe Incorporated. GIF(sm) is a Service Mark property of + CompuServe Incorporated." + +For further information, please contact : + + CompuServe Incorporated + Graphics Technology Department + 5000 Arlington Center Boulevard + Columbus, Ohio 43220 + U. S. A. + +CompuServe Incorporated maintains a mailing list with all those individuals and +organizations who wish to receive copies of this document when it is corrected + + + + + + + + 2 + + +or revised. This service is offered free of charge; please provide us with your +mailing address. + + +4. About the Document. + +This document describes in detail the definition of the Graphics Interchange +Format. This document is intended as a programming reference; it is +recommended that the entire document be read carefully before programming, +because of the interdependence of the various parts. There is an individual +section for each of the Format blocks. Within each section, the sub-section +labeled Required Version refers to the version number that an encoder will have +to use if the corresponding block is used in the Data Stream. Within each +section, a diagram describes the individual fields in the block; the diagrams +are drawn vertically; top bytes in the diagram appear first in the Data Stream. +Bits within a byte are drawn most significant on the left end. Multi-byte +numeric fields are ordered Least Significant Byte first. Numeric constants are +represented as Hexadecimal numbers, preceded by "0x". Bit fields within a byte +are described in order from most significant bits to least significant bits. + + +5. General Description. + +The Graphics Interchange Format(sm) defines a protocol intended for the on-line +transmission and interchange of raster graphic data in a way that is +independent of the hardware used in their creation or display. + +The Graphics Interchange Format is defined in terms of blocks and sub-blocks +which contain relevant parameters and data used in the reproduction of a +graphic. A GIF Data Stream is a sequence of protocol blocks and sub-blocks +representing a collection of graphics. In general, the graphics in a Data +Stream are assumed to be related to some degree, and to share some control +information; it is recommended that encoders attempt to group together related +graphics in order to minimize hardware changes during processing and to +minimize control information overhead. For the same reason, unrelated graphics +or graphics which require resetting hardware parameters should be encoded +separately to the extent possible. + +A Data Stream may originate locally, as when read from a file, or it may +originate remotely, as when transmitted over a data communications line. The +Format is defined with the assumption that an error-free Transport Level +Protocol is used for communications; the Format makes no provisions for +error-detection and error-correction. + +The GIF Data Stream must be interpreted in context, that is, the application +program must rely on information external to the Data Stream to invoke the +decoder process. + + +6. Version Numbers. + +The version number in the Header of a Data Stream is intended to identify the +minimum set of capabilities required of a decoder in order to fully process the +Data Stream. An encoder should use the earliest possible version number that +includes all the blocks used in the Data Stream. Within each block section in +this document, there is an entry labeled Required Version which specifies the + + + + + + + + 3 + + +earliest version number that includes the corresponding block. The encoder +should make every attempt to use the earliest version number covering all the +blocks in the Data Stream; the unnecessary use of later version numbers will +hinder processing by some decoders. + + +7. The Encoder. + +The Encoder is the program used to create a GIF Data Stream. From raster data +and other information, the encoder produces the necessary control and data +blocks needed for reproducing the original graphics. + +The encoder has the following primary responsibilities. + + - Include in the Data Stream all the necessary information to + reproduce the graphics. + + - Insure that a Data Stream is labeled with the earliest possible + Version Number that will cover the definition of all the blocks in + it; this is to ensure that the largest number of decoders can + process the Data Stream. + + - Ensure encoding of the graphics in such a way that the decoding + process is optimized. Avoid redundant information as much as + possible. + + - To the extent possible, avoid grouping graphics which might + require resetting hardware parameters during the decoding process. + + - Set to zero (off) each of the bits of each and every field + designated as reserved. Note that some fields in the Logical Screen + Descriptor and the Image Descriptor were reserved under Version + 87a, but are used under version 89a. + + +8. The Decoder. + +The Decoder is the program used to process a GIF Data Stream. It processes the +Data Stream sequentially, parsing the various blocks and sub-blocks, using the +control information to set hardware and process parameters and interpreting the +data to render the graphics. + +The decoder has the following primary responsibilities. + + - Process each graphic in the Data Stream in sequence, without + delays other than those specified in the control information. + + - Set its hardware parameters to fit, as closely as possible, the + control information contained in the Data Stream. + + +9. Compliance. + +An encoder or a decoder is said to comply with a given version of the Graphics +Interchange Format if and only if it fully conforms with and correctly +implements the definition of the standard associated with that version. An + + + + + + + + 4 + + +encoder or a decoder may be compliant with a given version number and not +compliant with some subsequent version. + + +10. About Recommendations. + +Each block section in this document contains an entry labeled Recommendation; +this section lists a set of recommendations intended to guide and organize the +use of the particular blocks. Such recommendations are geared towards making +the functions of encoders and decoders more efficient, as well as making +optimal use of the communications bandwidth. It is advised that these +recommendations be followed. + + +11. About Color Tables. + +The GIF format utilizes color tables to render raster-based graphics. A color +table can have one of two different scopes: global or local. A Global Color +Table is used by all those graphics in the Data Stream which do not have a +Local Color Table associated with them. The scope of the Global Color Table is +the entire Data Stream. A Local Color Table is always associated with the +graphic that immediately follows it; the scope of a Local Color Table is +limited to that single graphic. A Local Color Table supersedes a Global Color +Table, that is, if a Data Stream contains a Global Color Table, and an image +has a Local Color Table associated with it, the decoder must save the Global +Color Table, use the Local Color Table to render the image, and then restore +the Global Color Table. Both types of color tables are optional, making it +possible for a Data Stream to contain numerous graphics without a color table +at all. For this reason, it is recommended that the decoder save the last +Global Color Table used until another Global Color Table is encountered. In +this way, a Data Stream which does not contain either a Global Color Table or +a Local Color Table may be processed using the last Global Color Table saved. +If a Global Color Table from a previous Stream is used, that table becomes the +Global Color Table of the present Stream. This is intended to reduce the +overhead incurred by color tables. In particular, it is recommended that an +encoder use only one Global Color Table if all the images in related Data +Streams can be rendered with the same table. If no color table is available at +all, the decoder is free to use a system color table or a table of its own. In +that case, the decoder may use a color table with as many colors as its +hardware is able to support; it is recommended that such a table have black and +white as its first two entries, so that monochrome images can be rendered +adequately. + +The Definition of the GIF Format allows for a Data Stream to contain only the +Header, the Logical Screen Descriptor, a Global Color Table and the GIF +Trailer. Such a Data Stream would be used to load a decoder with a Global Color +Table, in preparation for subsequent Data Streams without a color table at all. + + +12. Blocks, Extensions and Scope. + +Blocks can be classified into three groups : Control, Graphic-Rendering and +Special Purpose. Control blocks, such as the Header, the Logical Screen +Descriptor, the Graphic Control Extension and the Trailer, contain information +used to control the process of the Data Stream or information used in setting +hardware parameters. Graphic-Rendering blocks such as the Image Descriptor and + + + + + + + + 5 + + +the Plain Text Extension contain information and data used to render a graphic +on the display device. Special Purpose blocks such as the Comment Extension and +the Application Extension are neither used to control the process of the Data +Stream nor do they contain information or data used to render a graphic on the +display device. With the exception of the Logical Screen Descriptor and the +Global Color Table, whose scope is the entire Data Stream, all other Control +blocks have a limited scope, restricted to the Graphic-Rendering block that +follows them. Special Purpose blocks do not delimit the scope of any Control +blocks; Special Purpose blocks are transparent to the decoding process. +Graphic-Rendering blocks and extensions are used as scope delimiters for +Control blocks and extensions. The labels used to identify labeled blocks fall +into three ranges : 0x00-0x7F (0-127) are the Graphic Rendering blocks, +excluding the Trailer (0x3B); 0x80-0xF9 (128-249) are the Control blocks; +0xFA-0xFF (250-255) are the Special Purpose blocks. These ranges are defined so +that decoders can handle block scope by appropriately identifying block labels, +even when the block itself cannot be processed. + + +13. Block Sizes. + +The Block Size field in a block, counts the number of bytes remaining in the +block, not counting the Block Size field itself, and not counting the Block +Terminator, if one is to follow. Blocks other than Data Blocks are intended to +be of fixed length; the Block Size field is provided in order to facilitate +skipping them, not to allow their size to change in the future. Data blocks +and sub-blocks are of variable length to accommodate the amount of data. + + +14. Using GIF as an embedded protocol. + +As an embedded protocol, GIF may be part of larger application protocols, +within which GIF is used to render graphics. In such a case, the application +protocol could define a block within which the GIF Data Stream would be +contained. The application program would then invoke a GIF decoder upon +encountering a block of type GIF. This approach is recommended in favor of +using Application Extensions, which become overhead for all other applications +that do not process them. Because a GIF Data Stream must be processed in +context, the application must rely on some means of identifying the GIF Data +Stream outside of the Stream itself. + + +15. Data Sub-blocks. + + a. Description. Data Sub-blocks are units containing data. They do not + have a label, these blocks are processed in the context of control + blocks, wherever data blocks are specified in the format. The first byte + of the Data sub-block indicates the number of data bytes to follow. A + data sub-block may contain from 0 to 255 data bytes. The size of the + block does not account for the size byte itself, therefore, the empty + sub-block is one whose size field contains 0x00. + + b. Required Version. 87a. + + + + + + + + + + + + 6 + + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Block Size Byte + +---------------+ + 1 | | + +- -+ + 2 | | + +- -+ + 3 | | + +- -+ + | | Data Values Byte + +- -+ + up | | + +- . . . . -+ + to | | + +- -+ + | | + +- -+ +255 | | + +---------------+ + + i) Block Size - Number of bytes in the Data Sub-block; the size + must be within 0 and 255 bytes, inclusive. + + ii) Data Values - Any 8-bit value. There must be exactly as many + Data Values as specified by the Block Size field. + + d. Extensions and Scope. This type of block always occurs as part of a + larger unit. It does not have a scope of itself. + + e. Recommendation. None. + + +16. Block Terminator. + + a. Description. This zero-length Data Sub-block is used to terminate a + sequence of Data Sub-blocks. It contains a single byte in the position of + the Block Size field and does not contain data. + + b. Required Version. 87a. + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Block Size Byte + +---------------+ + + i) Block Size - Number of bytes in the Data Sub-block; this field + contains the fixed value 0x00. + + ii) Data Values - This block does not contain any data. + + + + + + + + + + 7 + + + d. Extensions and Scope. This block terminates the immediately preceding + sequence of Data Sub-blocks. This block cannot be modified by any + extension. + + e. Recommendation. None. + + +17. Header. + + a. Description. The Header identifies the GIF Data Stream in context. The + Signature field marks the beginning of the Data Stream, and the Version + field identifies the set of capabilities required of a decoder to fully + process the Data Stream. This block is REQUIRED; exactly one Header must + be present per Data Stream. + + b. Required Version. Not applicable. This block is not subject to a + version number. This block must appear at the beginning of every Data + Stream. + + c. Syntax. + + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Signature 3 Bytes + +- -+ + 1 | | + +- -+ + 2 | | + +---------------+ + 3 | | Version 3 Bytes + +- -+ + 4 | | + +- -+ + 5 | | + +---------------+ + + i) Signature - Identifies the GIF Data Stream. This field contains + the fixed value 'GIF'. + + ii) Version - Version number used to format the data stream. + Identifies the minimum set of capabilities necessary to a decoder + to fully process the contents of the Data Stream. + + Version Numbers as of 10 July 1990 : "87a" - May 1987 + "89a" - July 1989 + + Version numbers are ordered numerically increasing on the first two + digits starting with 87 (87,88,...,99,00,...,85,86) and + alphabetically increasing on the third character (a,...,z). + + iii) Extensions and Scope. The scope of this block is the entire + Data Stream. This block cannot be modified by any extension. + + + + + + + + + + + 8 + + + d. Recommendations. + + i) Signature - This field identifies the beginning of the GIF Data + Stream; it is not intended to provide a unique signature for the + identification of the data. It is recommended that the GIF Data + Stream be identified externally by the application. (Refer to + Appendix G for on-line identification of the GIF Data Stream.) + + ii) Version - ENCODER : An encoder should use the earliest possible + version number that defines all the blocks used in the Data Stream. + When two or more Data Streams are combined, the latest of the + individual version numbers should be used for the resulting Data + Stream. DECODER : A decoder should attempt to process the data + stream to the best of its ability; if it encounters a version + number which it is not capable of processing fully, it should + nevertheless, attempt to process the data stream to the best of its + ability, perhaps after warning the user that the data may be + incomplete. + + +18. Logical Screen Descriptor. + + a. Description. The Logical Screen Descriptor contains the parameters + necessary to define the area of the display device within which the + images will be rendered. The coordinates in this block are given with + respect to the top-left corner of the virtual screen; they do not + necessarily refer to absolute coordinates on the display device. This + implies that they could refer to window coordinates in a window-based + environment or printer coordinates when a printer is used. + + This block is REQUIRED; exactly one Logical Screen Descriptor must be + present per Data Stream. + + b. Required Version. Not applicable. This block is not subject to a + version number. This block must appear immediately after the Header. + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Logical Screen Width Unsigned + +- -+ + 1 | | + +---------------+ + 2 | | Logical Screen Height Unsigned + +- -+ + 3 | | + +---------------+ + 4 | | | | | See below + +---------------+ + 5 | | Background Color Index Byte + +---------------+ + 6 | | Pixel Aspect Ratio Byte + +---------------+ + + + + + + + + + + 9 + + + = Global Color Table Flag 1 Bit + Color Resolution 3 Bits + Sort Flag 1 Bit + Size of Global Color Table 3 Bits + + i) Logical Screen Width - Width, in pixels, of the Logical Screen + where the images will be rendered in the displaying device. + + ii) Logical Screen Height - Height, in pixels, of the Logical + Screen where the images will be rendered in the displaying device. + + iii) Global Color Table Flag - Flag indicating the presence of a + Global Color Table; if the flag is set, the Global Color Table will + immediately follow the Logical Screen Descriptor. This flag also + selects the interpretation of the Background Color Index; if the + flag is set, the value of the Background Color Index field should + be used as the table index of the background color. (This field is + the most significant bit of the byte.) + + Values : 0 - No Global Color Table follows, the Background + Color Index field is meaningless. + 1 - A Global Color Table will immediately follow, the + Background Color Index field is meaningful. + + iv) Color Resolution - Number of bits per primary color available + to the original image, minus 1. This value represents the size of + the entire palette from which the colors in the graphic were + selected, not the number of colors actually used in the graphic. + For example, if the value in this field is 3, then the palette of + the original image had 4 bits per primary color available to create + the image. This value should be set to indicate the richness of + the original palette, even if not every color from the whole + palette is available on the source machine. + + v) Sort Flag - Indicates whether the Global Color Table is sorted. + If the flag is set, the Global Color Table is sorted, in order of + decreasing importance. Typically, the order would be decreasing + frequency, with most frequent color first. This assists a decoder, + with fewer available colors, in choosing the best subset of colors; + the decoder may use an initial segment of the table to render the + graphic. + + Values : 0 - Not ordered. + 1 - Ordered by decreasing importance, most + important color first. + + vi) Size of Global Color Table - If the Global Color Table Flag is + set to 1, the value in this field is used to calculate the number + of bytes contained in the Global Color Table. To determine that + actual size of the color table, raise 2 to [the value of the field + + 1]. Even if there is no Global Color Table specified, set this + field according to the above formula so that decoders can choose + the best graphics mode to display the stream in. (This field is + made up of the 3 least significant bits of the byte.) + + vii) Background Color Index - Index into the Global Color Table for + + + + + + + + 10 + + + the Background Color. The Background Color is the color used for + those pixels on the screen that are not covered by an image. If the + Global Color Table Flag is set to (zero), this field should be zero + and should be ignored. + + viii) Pixel Aspect Ratio - Factor used to compute an approximation + of the aspect ratio of the pixel in the original image. If the + value of the field is not 0, this approximation of the aspect ratio + is computed based on the formula: + + Aspect Ratio = (Pixel Aspect Ratio + 15) / 64 + + The Pixel Aspect Ratio is defined to be the quotient of the pixel's + width over its height. The value range in this field allows + specification of the widest pixel of 4:1 to the tallest pixel of + 1:4 in increments of 1/64th. + + Values : 0 - No aspect ratio information is given. + 1..255 - Value used in the computation. + + d. Extensions and Scope. The scope of this block is the entire Data + Stream. This block cannot be modified by any extension. + + e. Recommendations. None. + + +19. Global Color Table. + + a. Description. This block contains a color table, which is a sequence of + bytes representing red-green-blue color triplets. The Global Color Table + is used by images without a Local Color Table and by Plain Text + Extensions. Its presence is marked by the Global Color Table Flag being + set to 1 in the Logical Screen Descriptor; if present, it immediately + follows the Logical Screen Descriptor and contains a number of bytes + equal to + 3 x 2^(Size of Global Color Table+1). + + This block is OPTIONAL; at most one Global Color Table may be present + per Data Stream. + + b. Required Version. 87a + + + + + + + + + + + + + + + + + + + + + + + 11 + + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +===============+ + 0 | | Red 0 Byte + +- -+ + 1 | | Green 0 Byte + +- -+ + 2 | | Blue 0 Byte + +- -+ + 3 | | Red 1 Byte + +- -+ + | | Green 1 Byte + +- -+ + up | | + +- . . . . -+ ... + to | | + +- -+ + | | Green 255 Byte + +- -+ +767 | | Blue 255 Byte + +===============+ + + + d. Extensions and Scope. The scope of this block is the entire Data + Stream. This block cannot be modified by any extension. + + e. Recommendation. None. + + +20. Image Descriptor. + + a. Description. Each image in the Data Stream is composed of an Image + Descriptor, an optional Local Color Table, and the image data. Each + image must fit within the boundaries of the Logical Screen, as defined + in the Logical Screen Descriptor. + + The Image Descriptor contains the parameters necessary to process a table + based image. The coordinates given in this block refer to coordinates + within the Logical Screen, and are given in pixels. This block is a + Graphic-Rendering Block, optionally preceded by one or more Control + blocks such as the Graphic Control Extension, and may be optionally + followed by a Local Color Table; the Image Descriptor is always followed + by the image data. + + This block is REQUIRED for an image. Exactly one Image Descriptor must + be present per image in the Data Stream. An unlimited number of images + may be present per Data Stream. + + b. Required Version. 87a. + + + + + + + + + + + + + + 12 + + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Image Separator Byte + +---------------+ + 1 | | Image Left Position Unsigned + +- -+ + 2 | | + +---------------+ + 3 | | Image Top Position Unsigned + +- -+ + 4 | | + +---------------+ + 5 | | Image Width Unsigned + +- -+ + 6 | | + +---------------+ + 7 | | Image Height Unsigned + +- -+ + 8 | | + +---------------+ + 9 | | | | | | See below + +---------------+ + + = Local Color Table Flag 1 Bit + Interlace Flag 1 Bit + Sort Flag 1 Bit + Reserved 2 Bits + Size of Local Color Table 3 Bits + + i) Image Separator - Identifies the beginning of an Image + Descriptor. This field contains the fixed value 0x2C. + + ii) Image Left Position - Column number, in pixels, of the left edge + of the image, with respect to the left edge of the Logical Screen. + Leftmost column of the Logical Screen is 0. + + iii) Image Top Position - Row number, in pixels, of the top edge of + the image with respect to the top edge of the Logical Screen. Top + row of the Logical Screen is 0. + + iv) Image Width - Width of the image in pixels. + + v) Image Height - Height of the image in pixels. + + vi) Local Color Table Flag - Indicates the presence of a Local Color + Table immediately following this Image Descriptor. (This field is + the most significant bit of the byte.) + + + Values : 0 - Local Color Table is not present. Use + Global Color Table if available. + 1 - Local Color Table present, and to follow + immediately after this Image Descriptor. + + + + + + + + + 13 + + + vii) Interlace Flag - Indicates if the image is interlaced. An image + is interlaced in a four-pass interlace pattern; see Appendix E for + details. + + Values : 0 - Image is not interlaced. + 1 - Image is interlaced. + + viii) Sort Flag - Indicates whether the Local Color Table is + sorted. If the flag is set, the Local Color Table is sorted, in + order of decreasing importance. Typically, the order would be + decreasing frequency, with most frequent color first. This assists + a decoder, with fewer available colors, in choosing the best subset + of colors; the decoder may use an initial segment of the table to + render the graphic. + + Values : 0 - Not ordered. + 1 - Ordered by decreasing importance, most + important color first. + + ix) Size of Local Color Table - If the Local Color Table Flag is + set to 1, the value in this field is used to calculate the number + of bytes contained in the Local Color Table. To determine that + actual size of the color table, raise 2 to the value of the field + + 1. This value should be 0 if there is no Local Color Table + specified. (This field is made up of the 3 least significant bits + of the byte.) + + d. Extensions and Scope. The scope of this block is the Table-based Image + Data Block that follows it. This block may be modified by the Graphic + Control Extension. + + e. Recommendation. None. + + +21. Local Color Table. + + a. Description. This block contains a color table, which is a sequence of + bytes representing red-green-blue color triplets. The Local Color Table + is used by the image that immediately follows. Its presence is marked by + the Local Color Table Flag being set to 1 in the Image Descriptor; if + present, the Local Color Table immediately follows the Image Descriptor + and contains a number of bytes equal to + 3x2^(Size of Local Color Table+1). + If present, this color table temporarily becomes the active color table + and the following image should be processed using it. This block is + OPTIONAL; at most one Local Color Table may be present per Image + Descriptor and its scope is the single image associated with the Image + Descriptor that precedes it. + + b. Required Version. 87a. + + + + + + + + + + + + + + 14 + + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +===============+ + 0 | | Red 0 Byte + +- -+ + 1 | | Green 0 Byte + +- -+ + 2 | | Blue 0 Byte + +- -+ + 3 | | Red 1 Byte + +- -+ + | | Green 1 Byte + +- -+ + up | | + +- . . . . -+ ... + to | | + +- -+ + | | Green 255 Byte + +- -+ +767 | | Blue 255 Byte + +===============+ + + + d. Extensions and Scope. The scope of this block is the Table-based Image + Data Block that immediately follows it. This block cannot be modified by + any extension. + + e. Recommendations. None. + + +22. Table Based Image Data. + + a. Description. The image data for a table based image consists of a + sequence of sub-blocks, of size at most 255 bytes each, containing an + index into the active color table, for each pixel in the image. Pixel + indices are in order of left to right and from top to bottom. Each index + must be within the range of the size of the active color table, starting + at 0. The sequence of indices is encoded using the LZW Algorithm with + variable-length code, as described in Appendix F + + b. Required Version. 87a. + + c. Syntax. The image data format is as follows: + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + | | LZW Minimum Code Size Byte + +---------------+ + + +===============+ + | | + / / Image Data Data Sub-blocks + | | + +===============+ + + + + + + + + + 15 + + + i) LZW Minimum Code Size. This byte determines the initial number + of bits used for LZW codes in the image data, as described in + Appendix F. + + d. Extensions and Scope. This block has no scope, it contains raster + data. Extensions intended to modify a Table-based image must appear + before the corresponding Image Descriptor. + + e. Recommendations. None. + + +23. Graphic Control Extension. + + a. Description. The Graphic Control Extension contains parameters used + when processing a graphic rendering block. The scope of this extension is + the first graphic rendering block to follow. The extension contains only + one data sub-block. + + This block is OPTIONAL; at most one Graphic Control Extension may precede + a graphic rendering block. This is the only limit to the number of + Graphic Control Extensions that may be contained in a Data Stream. + + b. Required Version. 89a. + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Extension Introducer Byte + +---------------+ + 1 | | Graphic Control Label Byte + +---------------+ + + +---------------+ + 0 | | Block Size Byte + +---------------+ + 1 | | | | | See below + +---------------+ + 2 | | Delay Time Unsigned + +- -+ + 3 | | + +---------------+ + 4 | | Transparent Color Index Byte + +---------------+ + + +---------------+ + 0 | | Block Terminator Byte + +---------------+ + + + = Reserved 3 Bits + Disposal Method 3 Bits + User Input Flag 1 Bit + Transparent Color Flag 1 Bit + + i) Extension Introducer - Identifies the beginning of an extension + + + + + + + + 16 + + + block. This field contains the fixed value 0x21. + + ii) Graphic Control Label - Identifies the current block as a + Graphic Control Extension. This field contains the fixed value + 0xF9. + + iii) Block Size - Number of bytes in the block, after the Block + Size field and up to but not including the Block Terminator. This + field contains the fixed value 4. + + iv) Disposal Method - Indicates the way in which the graphic is to + be treated after being displayed. + + Values : 0 - No disposal specified. The decoder is + not required to take any action. + 1 - Do not dispose. The graphic is to be left + in place. + 2 - Restore to background color. The area used by the + graphic must be restored to the background color. + 3 - Restore to previous. The decoder is required to + restore the area overwritten by the graphic with + what was there prior to rendering the graphic. + 4-7 - To be defined. + + v) User Input Flag - Indicates whether or not user input is + expected before continuing. If the flag is set, processing will + continue when user input is entered. The nature of the User input + is determined by the application (Carriage Return, Mouse Button + Click, etc.). + + Values : 0 - User input is not expected. + 1 - User input is expected. + + When a Delay Time is used and the User Input Flag is set, + processing will continue when user input is received or when the + delay time expires, whichever occurs first. + + vi) Transparency Flag - Indicates whether a transparency index is + given in the Transparent Index field. (This field is the least + significant bit of the byte.) + + Values : 0 - Transparent Index is not given. + 1 - Transparent Index is given. + + vii) Delay Time - If not 0, this field specifies the number of + hundredths (1/100) of a second to wait before continuing with the + processing of the Data Stream. The clock starts ticking immediately + after the graphic is rendered. This field may be used in + conjunction with the User Input Flag field. + + viii) Transparency Index - The Transparency Index is such that when + encountered, the corresponding pixel of the display device is not + modified and processing goes on to the next pixel. The index is + present if and only if the Transparency Flag is set to 1. + + ix) Block Terminator - This zero-length data block marks the end of + + + + + + + + 17 + + the Graphic Control Extension. + + d. Extensions and Scope. The scope of this Extension is the graphic + rendering block that follows it; it is possible for other extensions to + be present between this block and its target. This block can modify the + Image Descriptor Block and the Plain Text Extension. + + e. Recommendations. + + i) Disposal Method - The mode Restore To Previous is intended to be + used in small sections of the graphic; the use of this mode imposes + severe demands on the decoder to store the section of the graphic + that needs to be saved. For this reason, this mode should be used + sparingly. This mode is not intended to save an entire graphic or + large areas of a graphic; when this is the case, the encoder should + make every attempt to make the sections of the graphic to be + restored be separate graphics in the data stream. In the case where + a decoder is not capable of saving an area of a graphic marked as + Restore To Previous, it is recommended that a decoder restore to + the background color. + + ii) User Input Flag - When the flag is set, indicating that user + input is expected, the decoder may sound the bell (0x07) to alert + the user that input is being expected. In the absence of a + specified Delay Time, the decoder should wait for user input + indefinitely. It is recommended that the encoder not set the User + Input Flag without a Delay Time specified. + + +24. Comment Extension. + + a. Description. The Comment Extension contains textual information which + is not part of the actual graphics in the GIF Data Stream. It is suitable + for including comments about the graphics, credits, descriptions or any + other type of non-control and non-graphic data. The Comment Extension + may be ignored by the decoder, or it may be saved for later processing; + under no circumstances should a Comment Extension disrupt or interfere + with the processing of the Data Stream. + + This block is OPTIONAL; any number of them may appear in the Data Stream. + + b. Required Version. 89a. + + + + + + + + + + + + + + + + + + + + + + + 18 + + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Extension Introducer Byte + +---------------+ + 1 | | Comment Label Byte + +---------------+ + + +===============+ + | | + N | | Comment Data Data Sub-blocks + | | + +===============+ + + +---------------+ + 0 | | Block Terminator Byte + +---------------+ + + i) Extension Introducer - Identifies the beginning of an extension + block. This field contains the fixed value 0x21. + + ii) Comment Label - Identifies the block as a Comment Extension. + This field contains the fixed value 0xFE. + + iii) Comment Data - Sequence of sub-blocks, each of size at most + 255 bytes and at least 1 byte, with the size in a byte preceding + the data. The end of the sequence is marked by the Block + Terminator. + + iv) Block Terminator - This zero-length data block marks the end of + the Comment Extension. + + d. Extensions and Scope. This block does not have scope. This block + cannot be modified by any extension. + + e. Recommendations. + + i) Data - This block is intended for humans. It should contain + text using the 7-bit ASCII character set. This block should + not be used to store control information for custom processing. + + ii) Position - This block may appear at any point in the Data + Stream at which a block can begin; however, it is recommended that + Comment Extensions do not interfere with Control or Data blocks; + they should be located at the beginning or at the end of the Data + Stream to the extent possible. + + +25. Plain Text Extension. + + a. Description. The Plain Text Extension contains textual data and the + parameters necessary to render that data as a graphic, in a simple form. + The textual data will be encoded with the 7-bit printable ASCII + characters. Text data are rendered using a grid of character cells + + + + + + + + + 19 + + + defined by the parameters in the block fields. Each character is rendered + in an individual cell. The textual data in this block is to be rendered + as mono-spaced characters, one character per cell, with a best fitting + font and size. For further information, see the section on + Recommendations below. The data characters are taken sequentially from + the data portion of the block and rendered within a cell, starting with + the upper left cell in the grid and proceeding from left to right and + from top to bottom. Text data is rendered until the end of data is + reached or the character grid is filled. The Character Grid contains an + integral number of cells; in the case that the cell dimensions do not + allow for an integral number, fractional cells must be discarded; an + encoder must be careful to specify the grid dimensions accurately so that + this does not happen. This block requires a Global Color Table to be + available; the colors used by this block reference the Global Color Table + in the Stream if there is one, or the Global Color Table from a previous + Stream, if one was saved. This block is a graphic rendering block, + therefore it may be modified by a Graphic Control Extension. This block + is OPTIONAL; any number of them may appear in the Data Stream. + + b. Required Version. 89a. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 20 + + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Extension Introducer Byte + +---------------+ + 1 | | Plain Text Label Byte + +---------------+ + + +---------------+ + 0 | | Block Size Byte + +---------------+ + 1 | | Text Grid Left Position Unsigned + +- -+ + 2 | | + +---------------+ + 3 | | Text Grid Top Position Unsigned + +- -+ + 4 | | + +---------------+ + 5 | | Text Grid Width Unsigned + +- -+ + 6 | | + +---------------+ + 7 | | Text Grid Height Unsigned + +- -+ + 8 | | + +---------------+ + 9 | | Character Cell Width Byte + +---------------+ + 10 | | Character Cell Height Byte + +---------------+ + 11 | | Text Foreground Color Index Byte + +---------------+ + 12 | | Text Background Color Index Byte + +---------------+ + + +===============+ + | | + N | | Plain Text Data Data Sub-blocks + | | + +===============+ + + +---------------+ + 0 | | Block Terminator Byte + +---------------+ + + i) Extension Introducer - Identifies the beginning of an extension + block. This field contains the fixed value 0x21. + + ii) Plain Text Label - Identifies the current block as a Plain Text + Extension. This field contains the fixed value 0x01. + + iii) Block Size - Number of bytes in the extension, after the Block + Size field and up to but not including the beginning of the data + portion. This field contains the fixed value 12. + + + + + + + + 21 + + + iv) Text Grid Left Position - Column number, in pixels, of the left + edge of the text grid, with respect to the left edge of the Logical + Screen. + + v) Text Grid Top Position - Row number, in pixels, of the top edge + of the text grid, with respect to the top edge of the Logical + Screen. + + vi) Image Grid Width - Width of the text grid in pixels. + + vii) Image Grid Height - Height of the text grid in pixels. + + viii) Character Cell Width - Width, in pixels, of each cell in the + grid. + + ix) Character Cell Height - Height, in pixels, of each cell in the + grid. + + x) Text Foreground Color Index - Index into the Global Color Table + to be used to render the text foreground. + + xi) Text Background Color Index - Index into the Global Color Table + to be used to render the text background. + + xii) Plain Text Data - Sequence of sub-blocks, each of size at most + 255 bytes and at least 1 byte, with the size in a byte preceding + the data. The end of the sequence is marked by the Block + Terminator. + + xiii) Block Terminator - This zero-length data block marks the end + of the Plain Text Data Blocks. + + d. Extensions and Scope. The scope of this block is the Plain Text Data + Block contained in it. This block may be modified by the Graphic Control + Extension. + + e. Recommendations. The data in the Plain Text Extension is assumed to be + preformatted. The selection of font and size is left to the discretion of + the decoder. If characters less than 0x20 or greater than 0xf7 are + encountered, it is recommended that the decoder display a Space character + (0x20). The encoder should use grid and cell dimensions such that an + integral number of cells fit in the grid both horizontally as well as + vertically. For broadest compatibility, character cell dimensions should + be around 8x8 or 8x16 (width x height); consider an image for unusual + sized text. + + +26. Application Extension. + + a. Description. The Application Extension contains application-specific + information; it conforms with the extension block syntax, as described + below, and its block label is 0xFF. + + b. Required Version. 89a. + + + + + + + + + + 22 + + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Extension Introducer Byte + +---------------+ + 1 | | Extension Label Byte + +---------------+ + + +---------------+ + 0 | | Block Size Byte + +---------------+ + 1 | | + +- -+ + 2 | | + +- -+ + 3 | | Application Identifier 8 Bytes + +- -+ + 4 | | + +- -+ + 5 | | + +- -+ + 6 | | + +- -+ + 7 | | + +- -+ + 8 | | + +---------------+ + 9 | | + +- -+ + 10 | | Appl. Authentication Code 3 Bytes + +- -+ + 11 | | + +---------------+ + + +===============+ + | | + | | Application Data Data Sub-blocks + | | + | | + +===============+ + + +---------------+ + 0 | | Block Terminator Byte + +---------------+ + + i) Extension Introducer - Defines this block as an extension. This + field contains the fixed value 0x21. + + ii) Application Extension Label - Identifies the block as an + Application Extension. This field contains the fixed value 0xFF. + + iii) Block Size - Number of bytes in this extension block, + following the Block Size field, up to but not including the + beginning of the Application Data. This field contains the fixed + value 11. + + + + + + + + 23 + + + iv) Application Identifier - Sequence of eight printable ASCII + characters used to identify the application owning the Application + Extension. + + v) Application Authentication Code - Sequence of three bytes used + to authenticate the Application Identifier. An Application program + may use an algorithm to compute a binary code that uniquely + identifies it as the application owning the Application Extension. + + + d. Extensions and Scope. This block does not have scope. This block + cannot be modified by any extension. + + e. Recommendation. None. + + +27. Trailer. + + a. Description. This block is a single-field block indicating the end of + the GIF Data Stream. It contains the fixed value 0x3B. + + b. Required Version. 87a. + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | GIF Trailer Byte + +---------------+ + + d. Extensions and Scope. This block does not have scope, it terminates + the GIF Data Stream. This block may not be modified by any extension. + + e. Recommendations. None. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 24 + + +Appendix +A. Quick Reference Table. + +Block Name Required Label Ext. Vers. +Application Extension Opt. (*) 0xFF (255) yes 89a +Comment Extension Opt. (*) 0xFE (254) yes 89a +Global Color Table Opt. (1) none no 87a +Graphic Control Extension Opt. (*) 0xF9 (249) yes 89a +Header Req. (1) none no N/A +Image Descriptor Opt. (*) 0x2C (044) no 87a (89a) +Local Color Table Opt. (*) none no 87a +Logical Screen Descriptor Req. (1) none no 87a (89a) +Plain Text Extension Opt. (*) 0x01 (001) yes 89a +Trailer Req. (1) 0x3B (059) no 87a + +Unlabeled Blocks +Header Req. (1) none no N/A +Logical Screen Descriptor Req. (1) none no 87a (89a) +Global Color Table Opt. (1) none no 87a +Local Color Table Opt. (*) none no 87a + +Graphic-Rendering Blocks +Plain Text Extension Opt. (*) 0x01 (001) yes 89a +Image Descriptor Opt. (*) 0x2C (044) no 87a (89a) + +Control Blocks +Graphic Control Extension Opt. (*) 0xF9 (249) yes 89a + +Special Purpose Blocks +Trailer Req. (1) 0x3B (059) no 87a +Comment Extension Opt. (*) 0xFE (254) yes 89a +Application Extension Opt. (*) 0xFF (255) yes 89a + +legend: (1) if present, at most one occurrence + (*) zero or more occurrences + (+) one or more occurrences + +Notes : The Header is not subject to Version Numbers. +(89a) The Logical Screen Descriptor and the Image Descriptor retained their +syntax from version 87a to version 89a, but some fields reserved under version +87a are used under version 89a. + + + + + + + + + + + + + + + + + + + + + + + 25 + + +Appendix +B. GIF Grammar. + +A Grammar is a form of notation to represent the sequence in which certain +objects form larger objects. A grammar is also used to represent the number of +objects that can occur at a given position. The grammar given here represents +the sequence of blocks that form the GIF Data Stream. A grammar is given by +listing its rules. Each rule consists of the left-hand side, followed by some +form of equals sign, followed by the right-hand side. In a rule, the +right-hand side describes how the left-hand side is defined. The right-hand +side consists of a sequence of entities, with the possible presence of special +symbols. The following legend defines the symbols used in this grammar for GIF. + +Legend: <> grammar word + ::= defines symbol + * zero or more occurrences + + one or more occurrences + | alternate element + [] optional element + +Example: + + ::= Header * Trailer + +This rule defines the entity as follows. It must begin with a +Header. The Header is followed by an entity called Logical Screen, which is +defined below by another rule. The Logical Screen is followed by the entity +Data, which is also defined below by another rule. Finally, the entity Data is +followed by the Trailer. Since there is no rule defining the Header or the +Trailer, this means that these blocks are defined in the document. The entity +Data has a special symbol (*) following it which means that, at this position, +the entity Data may be repeated any number of times, including 0 times. For +further reading on this subject, refer to a standard text on Programming +Languages. + + +The Grammar. + + ::= Header * Trailer + + ::= Logical Screen Descriptor [Global Color Table] + + ::= | + + + ::= [Graphic Control Extension] + + ::= | + Plain Text Extension + + ::= Image Descriptor [Local Color Table] Image Data + + ::= Application Extension | + Comment Extension + + + + + + + + + + 26 + + +NOTE : The grammar indicates that it is possible for a GIF Data Stream to +contain the Header, the Logical Screen Descriptor, a Global Color Table and the +GIF Trailer. This special case is used to load a GIF decoder with a Global +Color Table, in preparation for subsequent Data Streams without color tables at +all. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 27 + + +Appendix +C. Glossary. + +Active Color Table - Color table used to render the next graphic. If the next +graphic is an image which has a Local Color Table associated with it, the +active color table becomes the Local Color Table associated with that image. +If the next graphic is an image without a Local Color Table, or a Plain Text +Extension, the active color table is the Global Color Table associated with the +Data Stream, if there is one; if there is no Global Color Table in the Data +Stream, the active color table is a color table saved from a previous Data +Stream, or one supplied by the decoder. + +Block - Collection of bytes forming a protocol unit. In general, the term +includes labeled and unlabeled blocks, as well as Extensions. + +Data Stream - The GIF Data Stream is composed of blocks and sub-blocks +representing images and graphics, together with control information to render +them on a display device. All control and data blocks in the Data Stream must +follow the Header and must precede the Trailer. + +Decoder - A program capable of processing a GIF Data Stream to render the +images and graphics contained in it. + +Encoder - A program capable of capturing and formatting image and graphic +raster data, following the definitions of the Graphics Interchange Format. + +Extension - A protocol block labeled by the Extension Introducer 0x21. + +Extension Introducer - Label (0x21) defining an Extension. + +Graphic - Data which can be rendered on the screen by virtue of some algorithm. +The term graphic is more general than the term image; in addition to images, +the term graphic also includes data such as text, which is rendered using +character bit-maps. + +Image - Data representing a picture or a drawing; an image is represented by an +array of pixels called the raster of the image. + +Raster - Array of pixel values representing an image. + + + + + + + + + + + + + + + + + + + + + + + + + 28 + + +Appendix +D. Conventions. + +Animation - The Graphics Interchange Format is not intended as a platform for +animation, even though it can be done in a limited way. + +Byte Ordering - Unless otherwise stated, multi-byte numeric fields are ordered +with the Least Significant Byte first. + +Color Indices - Color indices always refer to the active color table, either +the Global Color Table or the Local Color Table. + +Color Order - Unless otherwise stated, all triple-component RGB color values +are specified in Red-Green-Blue order. + +Color Tables - Both color tables, the Global and the Local, are optional; if +present, the Global Color Table is to be used with every image in the Data +Stream for which a Local Color Table is not given; if present, a Local Color +Table overrides the Global Color Table. However, if neither color table is +present, the application program is free to use an arbitrary color table. If +the graphics in several Data Streams are related and all use the same color +table, an encoder could place the color table as the Global Color Table in the +first Data Stream and leave subsequent Data Streams without a Global Color +Table or any Local Color Tables; in this way, the overhead for the table is +eliminated. It is recommended that the decoder save the previous Global Color +Table to be used with the Data Stream that follows, in case it does not contain +either a Global Color Table or any Local Color Tables. In general, this allows +the application program to use past color tables, significantly reducing +transmission overhead. + +Extension Blocks - Extensions are defined using the Extension Introducer code +to mark the beginning of the block, followed by a block label, identifying the +type of extension. Extension Codes are numbers in the range from 0x00 to 0xFF, +inclusive. Special purpose extensions are transparent to the decoder and may be +omitted when transmitting the Data Stream on-line. The GIF capabilities +dialogue makes the provision for the receiver to request the transmission of +all blocks; the default state in this regard is no transmission of Special +purpose blocks. + +Reserved Fields - All Reserved Fields are expected to have each bit set to zero +(off). + + + + + + + + + + + + + + + + + + + + + + + 29 + + +Appendix +E. Interlaced Images. + +The rows of an Interlaced images are arranged in the following order: + + Group 1 : Every 8th. row, starting with row 0. (Pass 1) + Group 2 : Every 8th. row, starting with row 4. (Pass 2) + Group 3 : Every 4th. row, starting with row 2. (Pass 3) + Group 4 : Every 2nd. row, starting with row 1. (Pass 4) + +The Following example illustrates how the rows of an interlaced image are +ordered. + + Row Number Interlace Pass + + 0 ----------------------------------------- 1 + 1 ----------------------------------------- 4 + 2 ----------------------------------------- 3 + 3 ----------------------------------------- 4 + 4 ----------------------------------------- 2 + 5 ----------------------------------------- 4 + 6 ----------------------------------------- 3 + 7 ----------------------------------------- 4 + 8 ----------------------------------------- 1 + 9 ----------------------------------------- 4 + 10 ----------------------------------------- 3 + 11 ----------------------------------------- 4 + 12 ----------------------------------------- 2 + 13 ----------------------------------------- 4 + 14 ----------------------------------------- 3 + 15 ----------------------------------------- 4 + 16 ----------------------------------------- 1 + 17 ----------------------------------------- 4 + 18 ----------------------------------------- 3 + 19 ----------------------------------------- 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 30 + + +Appendix +F. Variable-Length-Code LZW Compression. + +The Variable-Length-Code LZW Compression is a variation of the Lempel-Ziv +Compression algorithm in which variable-length codes are used to replace +patterns detected in the original data. The algorithm uses a code or +translation table constructed from the patterns encountered in the original +data; each new pattern is entered into the table and its index is used to +replace it in the compressed stream. + +The compressor takes the data from the input stream and builds a code or +translation table with the patterns as it encounters them; each new pattern is +entered into the code table and its index is added to the output stream; when a +pattern is encountered which had been detected since the last code table +refresh, its index from the code table is put on the output stream, thus +achieving the data compression. The expander takes input from the compressed +data stream and builds the code or translation table from it; as the compressed +data stream is processed, codes are used to index into the code table and the +corresponding data is put on the decompressed output stream, thus achieving +data decompression. The details of the algorithm are explained below. The +Variable-Length-Code aspect of the algorithm is based on an initial code size +(LZW-initial code size), which specifies the initial number of bits used for +the compression codes. When the number of patterns detected by the compressor +in the input stream exceeds the number of patterns encodable with the current +number of bits, the number of bits per LZW code is increased by one. + +The Raster Data stream that represents the actual output image can be +represented as: + + 7 6 5 4 3 2 1 0 + +---------------+ + | LZW code size | + +---------------+ + + +---------------+ ----+ + | block size | | + +---------------+ | + | | +-- Repeated as many + | data bytes | | times as necessary. + | | | + +---------------+ ----+ + + . . . . . . ------- The code that terminates the LZW + compressed data must appear before + Block Terminator. + +---------------+ + |0 0 0 0 0 0 0 0| Block Terminator + +---------------+ + +The conversion of the image from a series of pixel values to a transmitted or +stored character stream involves several steps. In brief these steps are: + +1. Establish the Code Size - Define the number of bits needed to represent the +actual data. + +2. Compress the Data - Compress the series of image pixels to a series of + + + + + + + + 31 + + +compression codes. + +3. Build a Series of Bytes - Take the set of compression codes and convert to a +string of 8-bit bytes. + +4. Package the Bytes - Package sets of bytes into blocks preceded by character +counts and output. + +ESTABLISH CODE SIZE + +The first byte of the Compressed Data stream is a value indicating the minimum +number of bits required to represent the set of actual pixel values. Normally +this will be the same as the number of color bits. Because of some algorithmic +constraints however, black & white images which have one color bit must be +indicated as having a code size of 2. +This code size value also implies that the compression codes must start out one +bit longer. + +COMPRESSION + +The LZW algorithm converts a series of data values into a series of codes which +may be raw values or a code designating a series of values. Using text +characters as an analogy, the output code consists of a character or a code +representing a string of characters. + +The LZW algorithm used in GIF matches algorithmically with the standard LZW +algorithm with the following differences: + +1. A special Clear code is defined which resets all compression/decompression +parameters and tables to a start-up state. The value of this code is 2**. For example if the code size indicated was 4 (image was 4 bits/pixel) +the Clear code value would be 16 (10000 binary). The Clear code can appear at +any point in the image data stream and therefore requires the LZW algorithm to +process succeeding codes as if a new data stream was starting. Encoders should +output a Clear code as the first code of each image data stream. + +2. An End of Information code is defined that explicitly indicates the end of +the image data stream. LZW processing terminates when this code is encountered. +It must be the last code output by the encoder for an image. The value of this +code is +1. + +3. The first available compression code value is +2. + +4. The output codes are of variable length, starting at +1 bits per +code, up to 12 bits per code. This defines a maximum code value of 4095 +(0xFFF). Whenever the LZW code value would exceed the current code length, the +code length is increased by one. The packing/unpacking of these codes must then +be altered to reflect the new code length. + +BUILD 8-BIT BYTES + +Because the LZW compression used for GIF creates a series of variable length +codes, of between 3 and 12 bits each, these codes must be reformed into a +series of 8-bit bytes that will be the characters actually stored or +transmitted. This provides additional compression of the image. The codes are +formed into a stream of bits as if they were packed right to left and then + + + + + + + + 32 + + +picked off 8 bits at a time to be output. + +Assuming a character array of 8 bits per character and using 5 bit codes to be +packed, an example layout would be similar to: + + + +---------------+ + 0 | | bbbaaaaa + +---------------+ + 1 | | dcccccbb + +---------------+ + 2 | | eeeedddd + +---------------+ + 3 | | ggfffffe + +---------------+ + 4 | | hhhhhggg + +---------------+ + . . . + +---------------+ + N | | + +---------------+ + + +Note that the physical packing arrangement will change as the number of bits +per compression code change but the concept remains the same. + +PACKAGE THE BYTES + +Once the bytes have been created, they are grouped into blocks for output by +preceding each block of 0 to 255 bytes with a character count byte. A block +with a zero byte count terminates the Raster Data stream for a given image. +These blocks are what are actually output for the GIF image. This block format +has the side effect of allowing a decoding program the ability to read past the +actual image data if necessary by reading block counts and then skipping over +the data. + + + +FURTHER READING + +[1] Ziv, J. and Lempel, A. : "A Universal Algorithm for Sequential Data +Compression", IEEE Transactions on Information Theory, May 1977. +[2] Welch, T. : "A Technique for High-Performance Data Compression", Computer, +June 1984. +[3] Nelson, M.R. : "LZW Data Compression", Dr. Dobb's Journal, October 1989. + + + + + + + + + + + + + + + + + + + 33 + + +Appendix +G. On-line Capabilities Dialogue. + +NOTE : This section is currently (10 July 1990) under revision; the information +provided here should be used as general guidelines. Code written based on this +information should be designed in a flexible way to accommodate any changes +resulting from the revisions. + +The following sequences are defined for use in mediating control between a GIF +sender and GIF receiver over an interactive communications line. These +sequences do not apply to applications that involve downloading of static GIF +files and are not considered part of a GIF file. + +GIF CAPABILITIES ENQUIRY + +The GIF Capabilities Enquiry sequence is issued from a host and requests an +interactive GIF decoder to return a response message that defines the graphics +parameters for the decoder. This involves returning information about available +screen sizes, number of bits/color supported and the amount of color detail +supported. The escape sequence for the GIF Capabilities Enquiry is defined as: + +ESC[>0g 0x1B 0x5B 0x3E 0x30 0x67 + +GIF CAPABILITIES RESPONSE + +The GIF Capabilities Response message is returned by an interactive GIF decoder +and defines the decoder's display capabilities for all graphics modes that are +supported by the software. Note that this can also include graphics printers as +well as a monitor screen. The general format of this message is: + +#version;protocol{;dev, width, height, color-bits, color-res}... + + +'#' GIF Capabilities Response identifier character. +version GIF format version number; initially '87a'. +protocol='0' No end-to-end protocol supported by decoder Transfer as direct + 8-bit data stream. +protocol='1' Can use CIS B+ error correction protocol to transfer GIF data + interactively from the host directly to the display. +dev = '0' Screen parameter set follows. +dev = '1' Printer parameter set follows. +width Maximum supported display width in pixels. +height Maximum supported display height in pixels. +color-bits Number of bits per pixel supported. The number of supported + colors is therefore 2**color-bits. +color-res Number of bits per color component supported in the hardware + color palette. If color-res is '0' then no hardware palette + table is available. + +Note that all values in the GIF Capabilities Response are returned as ASCII +decimal numbers and the message is terminated by a Carriage Return character. + +The following GIF Capabilities Response message describes three standard IBM PC +Enhanced Graphics Adapter configurations with no printer; the GIF data stream + + + + + + + + + + 34 + + +can be processed within an error correcting protocol: + +#87a;1;0,320,200,4,0;0,640,200,2,2;0,640,350,4,2 + +ENTER GIF GRAPHICS MODE + +Two sequences are currently defined to invoke an interactive GIF decoder into +action. The only difference between them is that different output media are +selected. These sequences are: + +ESC[>1g Display GIF image on screen + + 0x1B 0x5B 0x3E 0x31 0x67 + +ESC[>2g Display image directly to an attached graphics printer. The image may +optionally be displayed on the screen as well. + + 0x1B 0x5B 0x3E 0x32 0x67 + +Note that the 'g' character terminating each sequence is in lowercase. + +INTERACTIVE ENVIRONMENT + +The assumed environment for the transmission of GIF image data from an +interactive application is a full 8-bit data stream from host to micro. All +256 character codes must be transferrable. The establishing of an 8-bit data +path for communications will normally be taken care of by the host application +programs. It is however up to the receiving communications programs supporting +GIF to be able to receive and pass on all 256 8-bit codes to the GIF decoder +software. +. diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index c85fbef101..4fd25df13e 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -8,20 +8,22 @@ namespace ImageSharp.Formats using System; using System.IO; + using ImageSharp.PixelFormats; + /// /// Encapsulates properties and methods required for decoding an image from a stream. /// public interface IImageDecoder { /// - /// Decodes the image from the specified stream to the . + /// Decodes the image from the specified stream to the . /// - /// The pixel format. + /// The pixel format. /// The configuration for the image. /// The containing image data. /// The options for the decoder. /// The decoded image - Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) - where TColor : struct, IPixel; + Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) + where TPixel : struct, IPixel; } } diff --git a/src/ImageSharp/Formats/IImageEncoder.cs b/src/ImageSharp/Formats/IImageEncoder.cs index 918f0d273c..a28511c173 100644 --- a/src/ImageSharp/Formats/IImageEncoder.cs +++ b/src/ImageSharp/Formats/IImageEncoder.cs @@ -8,19 +8,21 @@ namespace ImageSharp.Formats using System; using System.IO; + using ImageSharp.PixelFormats; + /// /// Encapsulates properties and methods required for encoding an image to a stream. /// public interface IImageEncoder { /// - /// Encodes the image to the specified stream from the . + /// Encodes the image to the specified stream from the . /// - /// The pixel format. - /// The to encode from. + /// The pixel format. + /// The to encode from. /// The to encode the image data to. /// The options for the encoder. - void Encode(Image image, Stream stream, IEncoderOptions options) - where TColor : struct, IPixel; + void Encode(Image image, Stream stream, IEncoderOptions options) + where TPixel : struct, IPixel; } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index 2b8c15ab3c..56466d7a0f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -235,7 +235,7 @@ namespace ImageSharp.Formats.Jpg /// /// Vector to multiply by [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void MultiplyAllInplace(Vector4 scaleVec) + public void MultiplyAllInplace(float scaleVec) { this.V0L *= scaleVec; this.V0R *= scaleVec; diff --git a/src/ImageSharp/Formats/Jpeg/Components/DCT.cs b/src/ImageSharp/Formats/Jpeg/Components/DCT.cs index 186a23862f..5729fe46d6 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/DCT.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/DCT.cs @@ -15,31 +15,31 @@ namespace ImageSharp.Formats.Jpg internal static class DCT { #pragma warning disable SA1310 // FieldNamesMustNotContainUnderscore - private static readonly Vector4 C_1_175876 = new Vector4(1.175876f); + private static readonly float C_1_175876 = 1.175876f; - private static readonly Vector4 C_1_961571 = new Vector4(-1.961571f); + private static readonly float C_1_961571 = -1.961571f; - private static readonly Vector4 C_0_390181 = new Vector4(-0.390181f); + private static readonly float C_0_390181 = -0.390181f; - private static readonly Vector4 C_0_899976 = new Vector4(-0.899976f); + private static readonly float C_0_899976 = -0.899976f; - private static readonly Vector4 C_2_562915 = new Vector4(-2.562915f); + private static readonly float C_2_562915 = -2.562915f; - private static readonly Vector4 C_0_298631 = new Vector4(0.298631f); + private static readonly float C_0_298631 = 0.298631f; - private static readonly Vector4 C_2_053120 = new Vector4(2.053120f); + private static readonly float C_2_053120 = 2.053120f; - private static readonly Vector4 C_3_072711 = new Vector4(3.072711f); + private static readonly float C_3_072711 = 3.072711f; - private static readonly Vector4 C_1_501321 = new Vector4(1.501321f); + private static readonly float C_1_501321 = 1.501321f; - private static readonly Vector4 C_0_541196 = new Vector4(0.541196f); + private static readonly float C_0_541196 = 0.541196f; - private static readonly Vector4 C_1_847759 = new Vector4(-1.847759f); + private static readonly float C_1_847759 = -1.847759f; - private static readonly Vector4 C_0_765367 = new Vector4(0.765367f); + private static readonly float C_0_765367 = 0.765367f; - private static readonly Vector4 C_0_125 = new Vector4(0.1250f); + private static readonly float C_0_125 = 0.1250f; #pragma warning restore SA1310 // FieldNamesMustNotContainUnderscore private static readonly Vector4 InvSqrt2 = new Vector4(0.707107f); @@ -216,26 +216,26 @@ namespace ImageSharp.Formats.Jpg d.V0L = c0 + c1; d.V4L = c0 - c1; - Vector4 w0 = new Vector4(0.541196f); - Vector4 w1 = new Vector4(1.306563f); + float w0 = 0.541196f; + float w1 = 1.306563f; d.V2L = (w0 * c2) + (w1 * c3); d.V6L = (w0 * c3) - (w1 * c2); - w0 = new Vector4(1.175876f); - w1 = new Vector4(0.785695f); + w0 = 1.175876f; + w1 = 0.785695f; c3 = (w0 * t4) + (w1 * t7); c0 = (w0 * t7) - (w1 * t4); - w0 = new Vector4(1.387040f); - w1 = new Vector4(0.275899f); + w0 = 1.387040f; + w1 = 0.275899f; c2 = (w0 * t5) + (w1 * t6); c1 = (w0 * t6) - (w1 * t5); d.V3L = c0 - c2; d.V5L = c3 - c1; - Vector4 invsqrt2 = new Vector4(0.707107f); + float invsqrt2 = 0.707107f; c0 = (c0 + c2) * invsqrt2; c3 = (c3 + c1) * invsqrt2; @@ -281,19 +281,19 @@ namespace ImageSharp.Formats.Jpg d.V0R = c0 + c1; d.V4R = c0 - c1; - Vector4 w0 = new Vector4(0.541196f); - Vector4 w1 = new Vector4(1.306563f); + float w0 = 0.541196f; + float w1 = 1.306563f; d.V2R = (w0 * c2) + (w1 * c3); d.V6R = (w0 * c3) - (w1 * c2); - w0 = new Vector4(1.175876f); - w1 = new Vector4(0.785695f); + w0 = 1.175876f; + w1 = 0.785695f; c3 = (w0 * t4) + (w1 * t7); c0 = (w0 * t7) - (w1 * t4); - w0 = new Vector4(1.387040f); - w1 = new Vector4(0.275899f); + w0 = 1.387040f; + w1 = 0.275899f; c2 = (w0 * t5) + (w1 * t6); c1 = (w0 * t6) - (w1 * t5); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecodedBlockArray.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecodedBlockArray.cs deleted file mode 100644 index 97a79dd61b..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/DecodedBlockArray.cs +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats.Jpg -{ - using System; - using System.Buffers; - - /// - /// Because has no information for rented arrays, - /// we need to store the count and the buffer separately when storing pooled arrays. - /// - internal struct DecodedBlockArray : IDisposable - { - /// - /// The used to pool data in . - /// Should always clean arrays when returning! - /// - private static readonly ArrayPool ArrayPool = ArrayPool.Create(); - - /// - /// Initializes a new instance of the struct. Rents a buffer. - /// - /// The number of valid -s - public DecodedBlockArray(int count) - { - this.Count = count; - this.Buffer = ArrayPool.Rent(count); - } - - /// - /// Gets the number of actual -s inside - /// - public int Count { get; } - - /// - /// Gets the rented buffer. - /// - public DecodedBlock[] Buffer { get; private set; } - - /// - /// Returns the rented buffer to the pool. - /// - public void Dispose() - { - if (this.Buffer != null) - { - ArrayPool.Return(this.Buffer, true); - this.Buffer = null; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockProcessor.cs index 0acee3a10b..71472c00fc 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockProcessor.cs @@ -8,8 +8,10 @@ namespace ImageSharp.Formats.Jpg using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + using ImageSharp.Memory; + /// - /// Encapsulates the implementation of processing "raw" -s into Jpeg image channels. + /// Encapsulates the implementation of processing "raw" -s into Jpeg image channels. /// [StructLayout(LayoutKind.Sequential)] internal unsafe struct JpegBlockProcessor @@ -47,10 +49,10 @@ namespace ImageSharp.Formats.Jpg /// The instance public void ProcessAllBlocks(JpegDecoderCore decoder) { - DecodedBlockArray blockArray = decoder.DecodedBlocks[this.componentIndex]; - for (int i = 0; i < blockArray.Count; i++) + Buffer blockArray = decoder.DecodedBlocks[this.componentIndex]; + for (int i = 0; i < blockArray.Length; i++) { - this.ProcessBlockColors(decoder, ref blockArray.Buffer[i]); + this.ProcessBlockColors(decoder, ref blockArray[i]); } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegPixelArea.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegPixelArea.cs index 728da8d02e..342ce299ce 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegPixelArea.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegPixelArea.cs @@ -4,9 +4,10 @@ // namespace ImageSharp.Formats.Jpg { - using System.Buffers; using System.Runtime.CompilerServices; + using ImageSharp.Memory; + /// /// Represents an area of a Jpeg subimage (channel) /// @@ -15,20 +16,30 @@ namespace ImageSharp.Formats.Jpg /// /// Initializes a new instance of the struct from existing data. /// - /// The pixel array - /// The stride + /// The pixel buffer + /// The stride /// The offset - public JpegPixelArea(byte[] pixels, int striede, int offset) + public JpegPixelArea(Buffer2D pixels, int stride, int offset) { - this.Stride = striede; + this.Stride = stride; this.Pixels = pixels; this.Offset = offset; } /// - /// Gets the pixels. + /// Initializes a new instance of the struct from existing buffer. + /// will be set to of and will be set to 0. + /// + /// The pixel buffer + public JpegPixelArea(Buffer2D pixels) + : this(pixels, pixels.Width, 0) + { + } + + /// + /// Gets the pixels buffer. /// - public byte[] Pixels { get; private set; } + public Buffer2D Pixels { get; private set; } /// /// Gets a value indicating whether the instance has been initalized. (Is not default(JpegPixelArea)) @@ -36,21 +47,19 @@ namespace ImageSharp.Formats.Jpg public bool IsInitialized => this.Pixels != null; /// - /// Gets or the stride. + /// Gets the stride. /// public int Stride { get; } /// - /// Gets or the offset. + /// Gets the offset. /// public int Offset { get; } /// /// Gets a of bytes to the pixel area /// - public MutableSpan Span => new MutableSpan(this.Pixels, this.Offset); - - private static ArrayPool BytePool => ArrayPool.Shared; + public MutableSpan Span => new MutableSpan(this.Pixels.Array, this.Offset); /// /// Returns the pixel at (x, y) @@ -67,35 +76,6 @@ namespace ImageSharp.Formats.Jpg } } - /// - /// Creates a new instance of the struct. - /// Pixel array will be taken from a pool, this instance will be the owner of it's pixel data, therefore - /// should be called when the instance is no longer needed. - /// - /// The width. - /// The height. - /// A with pooled data - public static JpegPixelArea CreatePooled(int width, int height) - { - int size = width * height; - byte[] pixels = BytePool.Rent(size); - return new JpegPixelArea(pixels, width, 0); - } - - /// - /// Returns to the pool - /// - public void ReturnPooled() - { - if (this.Pixels == null) - { - return; - } - - BytePool.Return(this.Pixels); - this.Pixels = null; - } - /// /// Gets the subarea that belongs to the Block8x8 defined by block indices /// @@ -129,7 +109,7 @@ namespace ImageSharp.Formats.Jpg public unsafe void LoadColorsFrom(Block8x8F* block, Block8x8F* temp) { // Level shift by +128, clip to [0, 255], and write to dst. - block->CopyColorsTo(new MutableSpan(this.Pixels, this.Offset), this.Stride, temp); + block->CopyColorsTo(new MutableSpan(this.Pixels.Array, this.Offset), this.Stride, temp); } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.cs index c6362a8713..7d2e6d4414 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegScanDecoder.cs @@ -9,6 +9,8 @@ namespace ImageSharp.Formats.Jpg using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + using ImageSharp.Memory; + /// /// Encapsulates the impementation of Jpeg SOS Huffman decoding. See JpegScanDecoder.md! /// @@ -171,7 +173,7 @@ namespace ImageSharp.Formats.Jpg // Take an existing block (required when progressive): int blockIndex = this.GetBlockIndex(decoder); - this.data.Block = decoder.DecodedBlocks[this.ComponentIndex].Buffer[blockIndex].Block; + this.data.Block = decoder.DecodedBlocks[this.ComponentIndex][blockIndex].Block; if (!decoder.InputProcessor.UnexpectedEndOfStreamReached) { @@ -179,8 +181,8 @@ namespace ImageSharp.Formats.Jpg } // Store the decoded block - DecodedBlockArray blocks = decoder.DecodedBlocks[this.ComponentIndex]; - blocks.Buffer[blockIndex].SaveBlock(this.bx, this.by, ref this.data.Block); + Buffer blocks = decoder.DecodedBlocks[this.ComponentIndex]; + blocks[blockIndex].SaveBlock(this.bx, this.by, ref this.data.Block); } // for j diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrImage.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrImage.cs index a5ca9796b6..89e327f0db 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrImage.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrImage.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Formats.Jpg using System; using System.Buffers; + using ImageSharp.Memory; + /// /// Represents an image made up of three color components (luminance, blue chroma, red chroma) /// @@ -17,17 +19,17 @@ namespace ImageSharp.Formats.Jpg /// /// Gets the luminance components channel as . /// - public JpegPixelArea YChannel; + public Buffer2D YChannel; /// /// Gets the blue chroma components channel as . /// - public JpegPixelArea CbChannel; + public Buffer2D CbChannel; /// /// Gets an offseted to the Cr channel /// - public JpegPixelArea CrChannel; + public Buffer2D CrChannel; #pragma warning restore SA1401 /// @@ -44,9 +46,9 @@ namespace ImageSharp.Formats.Jpg this.YStride = width; this.CStride = cSize.Width; - this.YChannel = JpegPixelArea.CreatePooled(width, height); - this.CbChannel = JpegPixelArea.CreatePooled(cSize.Width, cSize.Height); - this.CrChannel = JpegPixelArea.CreatePooled(cSize.Width, cSize.Height); + this.YChannel = Buffer2D.CreateClean(width, height); + this.CbChannel = Buffer2D.CreateClean(cSize.Width, cSize.Height); + this.CrChannel = Buffer2D.CreateClean(cSize.Width, cSize.Height); } /// @@ -106,9 +108,9 @@ namespace ImageSharp.Formats.Jpg /// public void Dispose() { - this.YChannel.ReturnPooled(); - this.CbChannel.ReturnPooled(); - this.CrChannel.ReturnPooled(); + this.YChannel.Dispose(); + this.CbChannel.Dispose(); + this.CrChannel.Dispose(); } /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrToRgbTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrToRgbTables.cs new file mode 100644 index 0000000000..27324b5f68 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrToRgbTables.cs @@ -0,0 +1,109 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Jpg +{ + using System.Runtime.CompilerServices; + using ImageSharp.PixelFormats; + + /// + /// Provides 8-bit lookup tables for converting from YCbCr to Rgb colorspace. + /// Methods to build the tables are based on libjpeg implementation. + /// + internal unsafe struct YCbCrToRgbTables + { + /// + /// The red red-chrominance table + /// + public fixed int CrRTable[256]; + + /// + /// The blue blue-chrominance table + /// + public fixed int CbBTable[256]; + + /// + /// The green red-chrominance table + /// + public fixed int CrGTable[256]; + + /// + /// The green blue-chrominance table + /// + public fixed int CbGTable[256]; + + // Speediest right-shift on some machines and gives us enough accuracy at 4 decimal places. + private const int ScaleBits = 16; + + private const int Half = 1 << (ScaleBits - 1); + + /// + /// Initializes the YCbCr tables + /// + /// The intialized + public static YCbCrToRgbTables Create() + { + YCbCrToRgbTables tables = default(YCbCrToRgbTables); + + for (int i = 0, x = -128; i <= 255; i++, x++) + { + // i is the actual input pixel value, in the range 0..255 + // The Cb or Cr value we are thinking of is x = i - 128 + // Cr=>R value is nearest int to 1.402 * x + tables.CrRTable[i] = RightShift((Fix(1.402F) * x) + Half); + + // Cb=>B value is nearest int to 1.772 * x + tables.CbBTable[i] = RightShift((Fix(1.772F) * x) + Half); + + // Cr=>G value is scaled-up -0.714136286 + tables.CrGTable[i] = (-Fix(0.714136286F)) * x; + + // Cb => G value is scaled - up - 0.344136286 * x + // We also add in Half so that need not do it in inner loop + tables.CbGTable[i] = ((-Fix(0.344136286F)) * x) + Half; + } + + return tables; + } + + /// + /// Optimized method to pack bytes to the image from the YCbCr color space. + /// + /// The pixel format. + /// The packed pixel. + /// The reference to the tables instance. + /// The y luminance component. + /// The cb chroma component. + /// The cr chroma component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Pack(ref TPixel packed, YCbCrToRgbTables* tables, byte y, byte cb, byte cr) + where TPixel : struct, IPixel + { + // float r = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero); + byte r = (byte)(y + tables->CrRTable[cr]).Clamp(0, 255); + + // float g = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero); + // The values for the G calculation are left scaled up, since we must add them together before rounding. + byte g = (byte)(y + RightShift(tables->CbGTable[cb] + tables->CrGTable[cr])).Clamp(0, 255); + + // float b = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero); + byte b = (byte)(y + tables->CbBTable[cb]).Clamp(0, 255); + + packed.PackFromBytes(r, g, b, byte.MaxValue); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Fix(float x) + { + return (int)((x * (1L << ScaleBits)) + 0.5F); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int RightShift(int x) + { + return x >> ScaleBits; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs new file mode 100644 index 0000000000..94173d3e43 --- /dev/null +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs @@ -0,0 +1,131 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Formats.Jpg +{ + using System.Runtime.CompilerServices; + + /// + /// Provides 8-bit lookup tables for converting from Rgb to YCbCr colorspace. + /// Methods to build the tables are based on libjpeg implementation. + /// + internal unsafe struct RgbToYCbCrTables + { + /// + /// The red luminance table + /// + public fixed int YRTable[256]; + + /// + /// The green luminance table + /// + public fixed int YGTable[256]; + + /// + /// The blue luminance table + /// + public fixed int YBTable[256]; + + /// + /// The red blue-chrominance table + /// + public fixed int CbRTable[256]; + + /// + /// The green blue-chrominance table + /// + public fixed int CbGTable[256]; + + /// + /// The blue blue-chrominance table + /// B=>Cb and R=>Cr are the same + /// + public fixed int CbBTable[256]; + + /// + /// The green red-chrominance table + /// + public fixed int CrGTable[256]; + + /// + /// The blue red-chrominance table + /// + public fixed int CrBTable[256]; + + // Speediest right-shift on some machines and gives us enough accuracy at 4 decimal places. + private const int ScaleBits = 16; + + private const int CBCrOffset = 128 << ScaleBits; + + private const int Half = 1 << (ScaleBits - 1); + + /// + /// Initializes the YCbCr tables + /// + /// The intialized + public static RgbToYCbCrTables Create() + { + RgbToYCbCrTables tables = default(RgbToYCbCrTables); + + for (int i = 0; i <= 255; i++) + { + // The values for the calculations are left scaled up since we must add them together before rounding. + tables.YRTable[i] = Fix(0.299F) * i; + tables.YGTable[i] = Fix(0.587F) * i; + tables.YBTable[i] = (Fix(0.114F) * i) + Half; + tables.CbRTable[i] = (-Fix(0.168735892F)) * i; + tables.CbGTable[i] = (-Fix(0.331264108F)) * i; + + // We use a rounding fudge - factor of 0.5 - epsilon for Cb and Cr. + // This ensures that the maximum output will round to 255 + // not 256, and thus that we don't have to range-limit. + // + // B=>Cb and R=>Cr tables are the same + tables.CbBTable[i] = (Fix(0.5F) * i) + CBCrOffset + Half - 1; + + tables.CrGTable[i] = (-Fix(0.418687589F)) * i; + tables.CrBTable[i] = (-Fix(0.081312411F)) * i; + } + + return tables; + } + + /// + /// Optimized method to allocates the correct y, cb, and cr values to the DCT blocks from the given r, g, b values. + /// + /// The The luminance block. + /// The red chroma block. + /// The blue chroma block. + /// The reference to the tables instance. + /// The current index. + /// The red value. + /// The green value. + /// The blue value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Allocate(ref float* yBlockRaw, ref float* cbBlockRaw, ref float* crBlockRaw, ref RgbToYCbCrTables* tables, int index, int r, int g, int b) + { + // float y = (0.299F * r) + (0.587F * g) + (0.114F * b); + yBlockRaw[index] = (tables->YRTable[r] + tables->YGTable[g] + tables->YBTable[b]) >> ScaleBits; + + // float cb = 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b)); + cbBlockRaw[index] = (tables->CbRTable[r] + tables->CbGTable[g] + tables->CbBTable[b]) >> ScaleBits; + + // float b = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero); + crBlockRaw[index] = (tables->CbBTable[r] + tables->CrGTable[g] + tables->CrBTable[b]) >> ScaleBits; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Fix(float x) + { + return (int)((x * (1L << ScaleBits)) + 0.5F); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int RightShift(int x) + { + return x >> ScaleBits; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs b/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs index 351275ebb7..420af6b742 100644 --- a/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs @@ -10,23 +10,25 @@ namespace ImageSharp using Formats; + using ImageSharp.PixelFormats; + /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Saves the image to the given stream with the jpeg format. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. /// - /// The . + /// The . /// - public static Image SaveAsJpeg(this Image source, Stream stream) - where TColor : struct, IPixel + public static Image SaveAsJpeg(this Image source, Stream stream) + where TPixel : struct, IPixel { return SaveAsJpeg(source, stream, null); } @@ -34,16 +36,16 @@ namespace ImageSharp /// /// Saves the image to the given stream with the jpeg format. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. /// - /// The . + /// The . /// - public static Image SaveAsJpeg(this Image source, Stream stream, IJpegEncoderOptions options) - where TColor : struct, IPixel + public static Image SaveAsJpeg(this Image source, Stream stream, IJpegEncoderOptions options) + where TPixel : struct, IPixel { JpegEncoder encoder = new JpegEncoder(); encoder.Encode(source, stream, options); diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index 0aac316035..56d025504d 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -8,20 +8,22 @@ namespace ImageSharp.Formats using System; using System.IO; + using ImageSharp.PixelFormats; + /// /// Image decoder for generating an image out of a jpg stream. /// public class JpegDecoder : IImageDecoder { /// - public Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) - where TColor : struct, IPixel + public Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) + where TPixel : struct, IPixel { Guard.NotNull(stream, "stream"); using (JpegDecoderCore decoder = new JpegDecoderCore(options, configuration)) { - return decoder.Decode(stream); + return decoder.Decode(stream); } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 33533aa12b..9d9ecacfe1 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -11,6 +11,8 @@ namespace ImageSharp.Formats using System.Threading.Tasks; using ImageSharp.Formats.Jpg; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; /// /// Performs the jpeg decoding operation. @@ -37,6 +39,11 @@ namespace ImageSharp.Formats public InputProcessor InputProcessor; #pragma warning restore SA401 + /// + /// Lookup tables for converting YCbCr to Rgb + /// + private static YCbCrToRgbTables yCbCrToRgbTables = YCbCrToRgbTables.Create(); + /// /// The decoder options. /// @@ -105,7 +112,7 @@ namespace ImageSharp.Formats this.QuantizationTables = new Block8x8F[MaxTq + 1]; this.Temp = new byte[2 * Block8x8F.ScalarCount]; this.ComponentArray = new Component[MaxComponents]; - this.DecodedBlocks = new DecodedBlockArray[MaxComponents]; + this.DecodedBlocks = new Buffer[MaxComponents]; } /// @@ -119,12 +126,12 @@ namespace ImageSharp.Formats public HuffmanTree[] HuffmanTrees { get; } /// - /// Gets the array of -s storing the "raw" frequency-domain decoded blocks. + /// Gets the array of -s storing the "raw" frequency-domain decoded blocks. /// We need to apply IDCT, dequantiazition and unzigging to transform them into color-space blocks. - /// This is done by . + /// This is done by . /// When ==true, we are touching these blocks multiple times - each time we process a Scan. /// - public DecodedBlockArray[] DecodedBlocks { get; } + public Buffer[] DecodedBlocks { get; } /// /// Gets the quantization tables, in zigzag order. @@ -186,16 +193,16 @@ namespace ImageSharp.Formats /// Decodes the image from the specified and sets /// the data to image. /// - /// The pixel format. + /// The pixel format. /// The stream, where the image should be. /// The decoded image. - public Image Decode(Stream stream) - where TColor : struct, IPixel + public Image Decode(Stream stream) + where TPixel : struct, IPixel { ImageMetaData metadata = new ImageMetaData(); this.ProcessStream(metadata, stream, false); - this.ProcessBlocksIntoJpegImageChannels(); - Image image = this.ConvertJpegPixelsToImagePixels(metadata); + this.ProcessBlocksIntoJpegImageChannels(); + Image image = this.ConvertJpegPixelsToImagePixels(metadata); return image; } @@ -210,15 +217,15 @@ namespace ImageSharp.Formats this.HuffmanTrees[i].Dispose(); } - foreach (DecodedBlockArray blockArray in this.DecodedBlocks) + foreach (Buffer blockArray in this.DecodedBlocks) { - blockArray.Dispose(); + blockArray?.Dispose(); } this.ycbcrImage?.Dispose(); this.InputProcessor.Dispose(); - this.grayImage.ReturnPooled(); - this.blackImage.ReturnPooled(); + this.grayImage.Pixels?.Dispose(); + this.blackImage.Pixels?.Dispose(); } /// @@ -237,11 +244,11 @@ namespace ImageSharp.Formats switch (compIndex) { case 0: - return this.ycbcrImage.YChannel; + return new JpegPixelArea(this.ycbcrImage.YChannel); case 1: - return this.ycbcrImage.CbChannel; + return new JpegPixelArea(this.ycbcrImage.CbChannel); case 2: - return this.ycbcrImage.CrChannel; + return new JpegPixelArea(this.ycbcrImage.CrChannel); case 3: return this.blackImage; default: @@ -250,35 +257,6 @@ namespace ImageSharp.Formats } } - /// - /// Optimized method to pack bytes to the image from the YCbCr color space. - /// This is faster than implicit casting as it avoids double packing. - /// - /// The pixel format. - /// The packed pixel. - /// The y luminance component. - /// The cb chroma component. - /// The cr chroma component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void PackYcbCr(ref TColor packed, byte y, byte cb, byte cr) - where TColor : struct, IPixel - { - int ccb = cb - 128; - int ccr = cr - 128; - - // Speed up the algorithm by removing floating point calculation - // Scale by 65536, add .5F and truncate value. We use bit shifting to divide the result - int r0 = 91881 * ccr; // (1.402F * 65536) + .5F - int g0 = 22554 * ccb; // (0.34414F * 65536) + .5F - int g1 = 46802 * ccr; // (0.71414F * 65536) + .5F - int b0 = 116130 * ccb; // (1.772F * 65536) + .5F - - byte r = (byte)(y + (r0 >> 16)).Clamp(0, 255); - byte g = (byte)(y - (g0 >> 16) - (g1 >> 16)).Clamp(0, 255); - byte b = (byte)(y + (b0 >> 16)).Clamp(0, 255); - packed.PackFromBytes(r, g, b, 255); - } - /// /// Read metadata from stream and read the blocks in the scans into . /// @@ -481,9 +459,9 @@ namespace ImageSharp.Formats /// are in a "raw" frequency-domain form. We need to apply IDCT, dequantization and unzigging to transform them into color-space blocks. /// We can copy these blocks into -s afterwards. /// - /// The pixel type - private void ProcessBlocksIntoJpegImageChannels() - where TColor : struct, IPixel + /// The pixel type + private void ProcessBlocksIntoJpegImageChannels() + where TPixel : struct, IPixel { Parallel.For( 0, @@ -497,15 +475,15 @@ namespace ImageSharp.Formats } /// - /// Convert the pixel data in and/or into pixels of + /// Convert the pixel data in and/or into pixels of /// - /// The pixel type + /// The pixel type /// The metadata for the image. /// The decoded image. - private Image ConvertJpegPixelsToImagePixels(ImageMetaData metadata) - where TColor : struct, IPixel + private Image ConvertJpegPixelsToImagePixels(ImageMetaData metadata) + where TPixel : struct, IPixel { - Image image = Image.Create(this.ImageWidth, this.ImageHeight, metadata, this.configuration); + Image image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, metadata); if (this.grayImage.IsInitialized) { @@ -561,10 +539,10 @@ namespace ImageSharp.Formats /// /// Assigns the horizontal and vertical resolution to the image if it has a JFIF header. /// - /// The pixel format. + /// The pixel format. /// The image to assign the resolution to. - private void AssignResolution(Image image) - where TColor : struct, IPixel + private void AssignResolution(Image image) + where TPixel : struct, IPixel { if (this.isJfif && this.horizontalResolution > 0 && this.verticalResolution > 0) { @@ -589,14 +567,14 @@ namespace ImageSharp.Formats /// /// Converts the image from the original CMYK image pixels. /// - /// The pixel format. + /// The pixel format. /// The image. - private void ConvertFromCmyk(Image image) - where TColor : struct, IPixel + private void ConvertFromCmyk(Image image) + where TPixel : struct, IPixel { int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { Parallel.For( 0, @@ -609,11 +587,11 @@ namespace ImageSharp.Formats for (int x = 0; x < image.Width; x++) { - byte cyan = this.ycbcrImage.YChannel.Pixels[yo + x]; - byte magenta = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)]; - byte yellow = this.ycbcrImage.CrChannel.Pixels[co + (x / scale)]; + byte cyan = this.ycbcrImage.YChannel[yo + x]; + byte magenta = this.ycbcrImage.CbChannel[co + (x / scale)]; + byte yellow = this.ycbcrImage.CrChannel[co + (x / scale)]; - TColor packed = default(TColor); + TPixel packed = default(TPixel); this.PackCmyk(ref packed, cyan, magenta, yellow, x, y); pixels[x, y] = packed; } @@ -626,12 +604,12 @@ namespace ImageSharp.Formats /// /// Converts the image from the original grayscale image pixels. /// - /// The pixel format. + /// The pixel format. /// The image. - private void ConvertFromGrayScale(Image image) - where TColor : struct, IPixel + private void ConvertFromGrayScale(Image image) + where TPixel : struct, IPixel { - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { Parallel.For( 0, @@ -644,7 +622,7 @@ namespace ImageSharp.Formats { byte rgb = this.grayImage.Pixels[yoff + x]; - TColor packed = default(TColor); + TPixel packed = default(TPixel); packed.PackFromBytes(rgb, rgb, rgb, 255); pixels[x, y] = packed; } @@ -657,14 +635,14 @@ namespace ImageSharp.Formats /// /// Converts the image from the original RBG image pixels. /// - /// The pixel format. + /// The pixel format. /// The image. - private void ConvertFromRGB(Image image) - where TColor : struct, IPixel + private void ConvertFromRGB(Image image) + where TPixel : struct, IPixel { int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { Parallel.For( 0, @@ -678,11 +656,11 @@ namespace ImageSharp.Formats for (int x = 0; x < image.Width; x++) { - byte red = this.ycbcrImage.YChannel.Pixels[yo + x]; - byte green = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)]; - byte blue = this.ycbcrImage.CrChannel.Pixels[co + (x / scale)]; + byte red = this.ycbcrImage.YChannel[yo + x]; + byte green = this.ycbcrImage.CbChannel[co + (x / scale)]; + byte blue = this.ycbcrImage.CrChannel[co + (x / scale)]; - TColor packed = default(TColor); + TPixel packed = default(TPixel); packed.PackFromBytes(red, green, blue, 255); pixels[x, y] = packed; } @@ -695,35 +673,43 @@ namespace ImageSharp.Formats /// /// Converts the image from the original YCbCr image pixels. /// - /// The pixel format. + /// The pixel format. /// The image. - private void ConvertFromYCbCr(Image image) - where TColor : struct, IPixel + private void ConvertFromYCbCr(Image image) + where TPixel : struct, IPixel { int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { Parallel.For( - 0, - image.Height, - image.Configuration.ParallelOptions, - y => - { - // TODO: Simplify + optimize + share duplicate code across converter methods - int yo = this.ycbcrImage.GetRowYOffset(y); - int co = this.ycbcrImage.GetRowCOffset(y); - - for (int x = 0; x < image.Width; x++) - { - byte yy = this.ycbcrImage.YChannel.Pixels[yo + x]; - byte cb = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)]; - byte cr = this.ycbcrImage.CrChannel.Pixels[co + (x / scale)]; - - TColor packed = default(TColor); - PackYcbCr(ref packed, yy, cb, cr); - pixels[x, y] = packed; - } - }); + 0, + image.Height, + image.Configuration.ParallelOptions, + y => + { + // TODO. This Parallel loop doesn't give us the boost it should. + ref byte ycRef = ref this.ycbcrImage.YChannel[0]; + ref byte cbRef = ref this.ycbcrImage.CbChannel[0]; + ref byte crRef = ref this.ycbcrImage.CrChannel[0]; + fixed (YCbCrToRgbTables* tables = &yCbCrToRgbTables) + { + // TODO: Simplify + optimize + share duplicate code across converter methods + int yo = this.ycbcrImage.GetRowYOffset(y); + int co = this.ycbcrImage.GetRowCOffset(y); + + for (int x = 0; x < image.Width; x++) + { + int cOff = co + (x / scale); + byte yy = Unsafe.Add(ref ycRef, yo + x); + byte cb = Unsafe.Add(ref cbRef, cOff); + byte cr = Unsafe.Add(ref crRef, cOff); + + TPixel packed = default(TPixel); + YCbCrToRgbTables.Pack(ref packed, tables, yy, cb, cr); + pixels[x, y] = packed; + } + } + }); } this.AssignResolution(image); @@ -732,14 +718,14 @@ namespace ImageSharp.Formats /// /// Converts the image from the original YCCK image pixels. /// - /// The pixel format. + /// The pixel format. /// The image. - private void ConvertFromYcck(Image image) - where TColor : struct, IPixel + private void ConvertFromYcck(Image image) + where TPixel : struct, IPixel { int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { Parallel.For( 0, @@ -752,11 +738,11 @@ namespace ImageSharp.Formats for (int x = 0; x < image.Width; x++) { - byte yy = this.ycbcrImage.YChannel.Pixels[yo + x]; - byte cb = this.ycbcrImage.CbChannel.Pixels[co + (x / scale)]; - byte cr = this.ycbcrImage.CrChannel.Pixels[co + (x / scale)]; + byte yy = this.ycbcrImage.YChannel[yo + x]; + byte cb = this.ycbcrImage.CbChannel[co + (x / scale)]; + byte cr = this.ycbcrImage.CrChannel[co + (x / scale)]; - TColor packed = default(TColor); + TPixel packed = default(TPixel); this.PackYcck(ref packed, yy, cb, cr, x, y); pixels[x, y] = packed; } @@ -802,7 +788,8 @@ namespace ImageSharp.Formats if (this.ComponentCount == 1) { - this.grayImage = JpegPixelArea.CreatePooled(8 * this.MCUCountX, 8 * this.MCUCountY); + Buffer2D buffer = Buffer2D.CreateClean(8 * this.MCUCountX, 8 * this.MCUCountY); + this.grayImage = new JpegPixelArea(buffer); } else { @@ -841,7 +828,8 @@ namespace ImageSharp.Formats int h3 = this.ComponentArray[3].HorizontalFactor; int v3 = this.ComponentArray[3].VerticalFactor; - this.blackImage = JpegPixelArea.CreatePooled(8 * h3 * this.MCUCountX, 8 * v3 * this.MCUCountY); + Buffer2D buffer = Buffer2D.CreateClean(8 * h3 * this.MCUCountX, 8 * v3 * this.MCUCountY); + this.blackImage = new JpegPixelArea(buffer); } } } @@ -850,15 +838,15 @@ namespace ImageSharp.Formats /// Optimized method to pack bytes to the image from the CMYK color space. /// This is faster than implicit casting as it avoids double packing. /// - /// The pixel format. + /// The pixel format. /// The packed pixel. /// The cyan component. /// The magenta component. /// The yellow component. /// The x-position within the image. /// The y-position within the image. - private void PackCmyk(ref TColor packed, byte c, byte m, byte y, int xx, int yy) - where TColor : struct, IPixel + private void PackCmyk(ref TPixel packed, byte c, byte m, byte y, int xx, int yy) + where TPixel : struct, IPixel { // Get keyline float keyline = (255 - this.blackImage[xx, yy]) / 255F; @@ -875,15 +863,15 @@ namespace ImageSharp.Formats /// Optimized method to pack bytes to the image from the YCCK color space. /// This is faster than implicit casting as it avoids double packing. /// - /// The pixel format. + /// The pixel format. /// The packed pixel. /// The y luminance component. /// The cb chroma component. /// The cr chroma component. /// The x-position within the image. /// The y-position within the image. - private void PackYcck(ref TColor packed, byte y, byte cb, byte cr, int xx, int yy) - where TColor : struct, IPixel + private void PackYcck(ref TPixel packed, byte y, byte cb, byte cr, int xx, int yy) + where TPixel : struct, IPixel { // Convert the YCbCr part of the YCbCrK to RGB, invert the RGB to get // CMY, and patch in the original K. The RGB to CMY inversion cancels @@ -1323,7 +1311,7 @@ namespace ImageSharp.Formats { int count = this.TotalMCUCount * this.ComponentArray[i].HorizontalFactor * this.ComponentArray[i].VerticalFactor; - this.DecodedBlocks[i] = new DecodedBlockArray(count); + this.DecodedBlocks[i] = Buffer.CreateClean(count); } } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs index 2f2823fa28..152fd2c64c 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoder.cs @@ -7,14 +7,16 @@ namespace ImageSharp.Formats { using System.IO; + using ImageSharp.PixelFormats; + /// /// Encoder for writing the data image to a stream in jpeg format. /// public class JpegEncoder : IImageEncoder { /// - public void Encode(Image image, Stream stream, IEncoderOptions options) - where TColor : struct, IPixel + public void Encode(Image image, Stream stream, IEncoderOptions options) + where TPixel : struct, IPixel { IJpegEncoderOptions gifOptions = JpegEncoderOptions.Create(options); @@ -22,14 +24,14 @@ namespace ImageSharp.Formats } /// - /// Encodes the image to the specified stream from the . + /// Encodes the image to the specified stream from the . /// - /// The pixel format. - /// The to encode from. + /// The pixel format. + /// The to encode from. /// The to encode the image data to. /// The options for the encoder. - public void Encode(Image image, Stream stream, IJpegEncoderOptions options) - where TColor : struct, IPixel + public void Encode(Image image, Stream stream, IJpegEncoderOptions options) + where TPixel : struct, IPixel { JpegEncoderCore encode = new JpegEncoderCore(options); encode.Encode(image, stream); diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index 66f400c017..fd70627294 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -5,13 +5,12 @@ namespace ImageSharp.Formats { - using System; using System.Buffers; using System.IO; using System.Runtime.CompilerServices; - using ImageSharp.Formats.Jpg; using ImageSharp.Formats.Jpg.Components; + using ImageSharp.PixelFormats; /// /// Image encoder for writing an image to a stream as a jpeg. @@ -102,10 +101,15 @@ namespace ImageSharp.Formats } }; + /// + /// Lookup tables for converting Rgb to YCbCr + /// + private static RgbToYCbCrTables rgbToYCbCrTables = RgbToYCbCrTables.Create(); + /// /// A scratch buffer to reduce allocations. /// - private readonly byte[] buffer = new byte[16]; + private readonly byte[] buffer = new byte[20]; /// /// A buffer for reducing the number of stream writes when emitting Huffman tables. 64 seems to be enough. @@ -165,11 +169,11 @@ namespace ImageSharp.Formats /// /// Encode writes the image to the jpeg baseline format with the given options. /// - /// The pixel format. + /// The pixel format. /// The image to write from. /// The stream to write to. - public void Encode(Image image, Stream stream) - where TColor : struct, IPixel + public void Encode(Image image, Stream stream) + where TPixel : struct, IPixel { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); @@ -225,7 +229,7 @@ namespace ImageSharp.Formats this.WriteDefineHuffmanTables(componentCount); // Write the image data. - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { this.WriteStartOfScan(pixels); } @@ -282,23 +286,25 @@ namespace ImageSharp.Formats /// /// Converts the 8x8 region of the image whose top-left corner is x,y to its YCbCr values. /// - /// The pixel format. + /// The pixel format. /// The pixel accessor. + /// The reference to the tables instance. /// The x-position within the image. /// The y-position within the image. /// The luminance block. /// The red chroma block. /// The blue chroma block. - /// Temporal provided by the caller - private static void ToYCbCr( - PixelAccessor pixels, + /// Temporal provided by the caller + private static void ToYCbCr( + PixelAccessor pixels, + RgbToYCbCrTables* tables, int x, int y, Block8x8F* yBlock, Block8x8F* cbBlock, Block8x8F* crBlock, - PixelArea rgbBytes) - where TColor : struct, IPixel + PixelArea rgbBytes) + where TPixel : struct, IPixel { float* yBlockRaw = (float*)yBlock; float* cbBlockRaw = (float*)cbBlock; @@ -307,7 +313,8 @@ namespace ImageSharp.Formats rgbBytes.Reset(); pixels.CopyRGBBytesStretchedTo(rgbBytes, y, x); - byte* data = (byte*)rgbBytes.DataPointer; + ref byte data0 = ref rgbBytes.Bytes[0]; + int dataIdx = 0; for (int j = 0; j < 8; j++) { @@ -315,35 +322,15 @@ namespace ImageSharp.Formats for (int i = 0; i < 8; i++) { // Convert returned bytes into the YCbCr color space. Assume RGBA - int r = data[0]; - int g = data[1]; - int b = data[2]; - - // Speed up the algorithm by removing floating point calculation - // Scale by 65536, add .5F and truncate value. We use bit shifting to divide the result - int y0 = 19595 * r; // (0.299F * 65536) + .5F - int y1 = 38470 * g; // (0.587F * 65536) + .5F - int y2 = 7471 * b; // (0.114F * 65536) + .5F - - int cb0 = -11057 * r; // (-0.168736F * 65536) + .5F - int cb1 = 21710 * g; // (0.331264F * 65536) + .5F - int cb2 = 32768 * b; // (0.5F * 65536) + .5F - - int cr0 = 32768 * r; // (0.5F * 65536) + .5F - int cr1 = 27439 * g; // (0.418688F * 65536) + .5F - int cr2 = 5329 * b; // (0.081312F * 65536) + .5F - - float yy = (y0 + y1 + y2) >> 16; - float cb = 128 + ((cb0 - cb1 + cb2) >> 16); - float cr = 128 + ((cr0 - cr1 - cr2) >> 16); + int r = Unsafe.Add(ref data0, dataIdx); + int g = Unsafe.Add(ref data0, dataIdx + 1); + int b = Unsafe.Add(ref data0, dataIdx + 2); int index = j8 + i; - yBlockRaw[index] = yy; - cbBlockRaw[index] = cb; - crBlockRaw[index] = cr; + RgbToYCbCrTables.Allocate(ref yBlockRaw, ref cbBlockRaw, ref crBlockRaw, ref tables, index, r, g, b); - data += 3; + dataIdx += 3; } } } @@ -441,12 +428,12 @@ namespace ImageSharp.Formats /// /// Encodes the image with no subsampling. /// - /// The pixel format. + /// The pixel format. /// The pixel accessor providing access to the image pixels. - private void Encode444(PixelAccessor pixels) - where TColor : struct, IPixel + private void Encode444(PixelAccessor pixels) + where TPixel : struct, IPixel { - // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) + // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) Block8x8F b = default(Block8x8F); Block8x8F cb = default(Block8x8F); Block8x8F cr = default(Block8x8F); @@ -462,38 +449,41 @@ namespace ImageSharp.Formats // ReSharper disable once InconsistentNaming int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; - using (PixelArea rgbBytes = new PixelArea(8, 8, ComponentOrder.Xyz)) + fixed (RgbToYCbCrTables* tables = &rgbToYCbCrTables) { - for (int y = 0; y < pixels.Height; y += 8) + using (PixelArea rgbBytes = new PixelArea(8, 8, ComponentOrder.Xyz)) { - for (int x = 0; x < pixels.Width; x += 8) + for (int y = 0; y < pixels.Height; y += 8) { - ToYCbCr(pixels, x, y, &b, &cb, &cr, rgbBytes); - - prevDCY = this.WriteBlock( - QuantIndex.Luminance, - prevDCY, - &b, - &temp1, - &temp2, - &onStackLuminanceQuantTable, - unzig.Data); - prevDCCb = this.WriteBlock( - QuantIndex.Chrominance, - prevDCCb, - &cb, - &temp1, - &temp2, - &onStackChrominanceQuantTable, - unzig.Data); - prevDCCr = this.WriteBlock( - QuantIndex.Chrominance, - prevDCCr, - &cr, - &temp1, - &temp2, - &onStackChrominanceQuantTable, - unzig.Data); + for (int x = 0; x < pixels.Width; x += 8) + { + ToYCbCr(pixels, tables, x, y, &b, &cb, &cr, rgbBytes); + + prevDCY = this.WriteBlock( + QuantIndex.Luminance, + prevDCY, + &b, + &temp1, + &temp2, + &onStackLuminanceQuantTable, + unzig.Data); + prevDCCb = this.WriteBlock( + QuantIndex.Chrominance, + prevDCCb, + &cb, + &temp1, + &temp2, + &onStackChrominanceQuantTable, + unzig.Data); + prevDCCr = this.WriteBlock( + QuantIndex.Chrominance, + prevDCCr, + &cr, + &temp1, + &temp2, + &onStackChrominanceQuantTable, + unzig.Data); + } } } } @@ -524,19 +514,17 @@ namespace ImageSharp.Formats this.buffer[12] = 0x01; // versionlo this.buffer[13] = 0x01; // xyunits as dpi - // No thumbnail - this.buffer[14] = 0x00; // Thumbnail width - this.buffer[15] = 0x00; // Thumbnail height - - this.outputStream.Write(this.buffer, 0, 16); - // Resolution. Big Endian - this.buffer[0] = (byte)(horizontalResolution >> 8); - this.buffer[1] = (byte)horizontalResolution; - this.buffer[2] = (byte)(verticalResolution >> 8); - this.buffer[3] = (byte)verticalResolution; + this.buffer[14] = (byte)(horizontalResolution >> 8); + this.buffer[15] = (byte)horizontalResolution; + this.buffer[16] = (byte)(verticalResolution >> 8); + this.buffer[17] = (byte)verticalResolution; - this.outputStream.Write(this.buffer, 0, 4); + // No thumbnail + this.buffer[18] = 0x00; // Thumbnail width + this.buffer[19] = 0x00; // Thumbnail height + + this.outputStream.Write(this.buffer, 0, 20); } /// @@ -713,9 +701,9 @@ namespace ImageSharp.Formats /// Writes the metadata profiles to the image. /// /// The image. - /// The pixel format. - private void WriteProfiles(Image image) - where TColor : struct, IPixel + /// The pixel format. + private void WriteProfiles(Image image) + where TPixel : struct, IPixel { if (this.options.IgnoreMetadata) { @@ -785,12 +773,12 @@ namespace ImageSharp.Formats /// /// Writes the StartOfScan marker. /// - /// The pixel format. + /// The pixel format. /// The pixel accessor providing access to the image pixels. - private void WriteStartOfScan(PixelAccessor pixels) - where TColor : struct, IPixel + private void WriteStartOfScan(PixelAccessor pixels) + where TPixel : struct, IPixel { - // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) + // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) // TODO: We should allow grayscale writing. this.outputStream.Write(SosHeaderYCbCr, 0, SosHeaderYCbCr.Length); @@ -812,12 +800,12 @@ namespace ImageSharp.Formats /// Encodes the image with subsampling. The Cb and Cr components are each subsampled /// at a factor of 2 both horizontally and vertically. /// - /// The pixel format. + /// The pixel format. /// The pixel accessor providing access to the image pixels. - private void Encode420(PixelAccessor pixels) - where TColor : struct, IPixel + private void Encode420(PixelAccessor pixels) + where TPixel : struct, IPixel { - // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) + // TODO: Need a JpegScanEncoder class or struct that encapsulates the scan-encoding implementation. (Similar to JpegScanDecoder.) Block8x8F b = default(Block8x8F); BlockQuad cb = default(BlockQuad); @@ -835,49 +823,51 @@ namespace ImageSharp.Formats // ReSharper disable once InconsistentNaming int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; - - using (PixelArea rgbBytes = new PixelArea(8, 8, ComponentOrder.Xyz)) + fixed (RgbToYCbCrTables* tables = &rgbToYCbCrTables) { - for (int y = 0; y < pixels.Height; y += 16) + using (PixelArea rgbBytes = new PixelArea(8, 8, ComponentOrder.Xyz)) { - for (int x = 0; x < pixels.Width; x += 16) + for (int y = 0; y < pixels.Height; y += 16) { - for (int i = 0; i < 4; i++) + for (int x = 0; x < pixels.Width; x += 16) { - int xOff = (i & 1) * 8; - int yOff = (i & 2) * 4; - - ToYCbCr(pixels, x + xOff, y + yOff, &b, cbPtr + i, crPtr + i, rgbBytes); + for (int i = 0; i < 4; i++) + { + int xOff = (i & 1) * 8; + int yOff = (i & 2) * 4; + + ToYCbCr(pixels, tables, x + xOff, y + yOff, &b, cbPtr + i, crPtr + i, rgbBytes); + + prevDCY = this.WriteBlock( + QuantIndex.Luminance, + prevDCY, + &b, + &temp1, + &temp2, + &onStackLuminanceQuantTable, + unzig.Data); + } + + Block8x8F.Scale16X16To8X8(&b, cbPtr); + prevDCCb = this.WriteBlock( + QuantIndex.Chrominance, + prevDCCb, + &b, + &temp1, + &temp2, + &onStackChrominanceQuantTable, + unzig.Data); - prevDCY = this.WriteBlock( - QuantIndex.Luminance, - prevDCY, + Block8x8F.Scale16X16To8X8(&b, crPtr); + prevDCCr = this.WriteBlock( + QuantIndex.Chrominance, + prevDCCr, &b, &temp1, &temp2, - &onStackLuminanceQuantTable, + &onStackChrominanceQuantTable, unzig.Data); } - - Block8x8F.Scale16X16To8X8(&b, cbPtr); - prevDCCb = this.WriteBlock( - QuantIndex.Chrominance, - prevDCCb, - &b, - &temp1, - &temp2, - &onStackChrominanceQuantTable, - unzig.Data); - - Block8x8F.Scale16X16To8X8(&b, crPtr); - prevDCCr = this.WriteBlock( - QuantIndex.Chrominance, - prevDCCr, - &b, - &temp1, - &temp2, - &onStackChrominanceQuantTable, - unzig.Data); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Utils/JpegUtils.cs b/src/ImageSharp/Formats/Jpeg/Utils/JpegUtils.cs index dd96985d9a..afb8e07007 100644 --- a/src/ImageSharp/Formats/Jpeg/Utils/JpegUtils.cs +++ b/src/ImageSharp/Formats/Jpeg/Utils/JpegUtils.cs @@ -6,6 +6,9 @@ namespace ImageSharp.Formats.Jpg { using System; using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + using ImageSharp.PixelFormats; /// /// Jpeg specific utilities and extension methods @@ -15,17 +18,17 @@ namespace ImageSharp.Formats.Jpg /// /// Copy a region of an image into dest. De "outlier" area will be stretched out with pixels on the right and bottom of the image. /// - /// The pixel type + /// The pixel type /// The input pixel acessor - /// The destination + /// The destination /// Starting Y coord /// Starting X coord - public static void CopyRGBBytesStretchedTo( - this PixelAccessor pixels, - PixelArea dest, + public static void CopyRGBBytesStretchedTo( + this PixelAccessor pixels, + PixelArea dest, int sourceY, int sourceX) - where TColor : struct, IPixel + where TPixel : struct, IPixel { pixels.SafeCopyTo(dest, sourceY, sourceX); int stretchFromX = pixels.Width - sourceX; @@ -33,29 +36,16 @@ namespace ImageSharp.Formats.Jpg StretchPixels(dest, stretchFromX, stretchFromY); } - /// - /// Copy an RGB value - /// - /// Source pointer - /// Destination pointer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CopyRgb(byte* source, byte* dest) - { - *dest++ = *source++; // R - *dest++ = *source++; // G - *dest = *source; // B - } - // Nothing to stretch if (fromX, fromY) is outside the area, or is at (0,0) [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IsInvalidStretchStartingPosition(PixelArea area, int fromX, int fromY) - where TColor : struct, IPixel + private static bool IsInvalidStretchStartingPosition(PixelArea area, int fromX, int fromY) + where TPixel : struct, IPixel { return fromX <= 0 || fromY <= 0 || fromX >= area.Width || fromY >= area.Height; } - private static void StretchPixels(PixelArea area, int fromX, int fromY) - where TColor : struct, IPixel + private static void StretchPixels(PixelArea area, int fromX, int fromY) + where TPixel : struct, IPixel { if (IsInvalidStretchStartingPosition(area, fromX, fromY)) { @@ -64,31 +54,38 @@ namespace ImageSharp.Formats.Jpg for (int y = 0; y < fromY; y++) { - byte* ptrBase = (byte*)area.DataPointer + (y * area.RowStride); + ref RGB24 ptrBase = ref GetRowStart(area, y); for (int x = fromX; x < area.Width; x++) { - byte* prevPtr = ptrBase + ((x - 1) * 3); - byte* currPtr = ptrBase + (x * 3); - - CopyRgb(prevPtr, currPtr); + // Copy the left neighbour pixel to the current one + Unsafe.Add(ref ptrBase, x) = Unsafe.Add(ref ptrBase, x - 1); } } for (int y = fromY; y < area.Height; y++) { - byte* currBase = (byte*)area.DataPointer + (y * area.RowStride); - byte* prevBase = (byte*)area.DataPointer + ((y - 1) * area.RowStride); + ref RGB24 currBase = ref GetRowStart(area, y); + ref RGB24 prevBase = ref GetRowStart(area, y - 1); for (int x = 0; x < area.Width; x++) { - int x3 = 3 * x; - byte* currPtr = currBase + x3; - byte* prevPtr = prevBase + x3; - - CopyRgb(prevPtr, currPtr); + // Copy the top neighbour pixel to the current one + Unsafe.Add(ref currBase, x) = Unsafe.Add(ref prevBase, x); } } } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ref RGB24 GetRowStart(PixelArea area, int y) + where TPixel : struct, IPixel + { + return ref Unsafe.As(ref area.GetRowSpan(y).DangerousGetPinnableReference()); + } + + [StructLayout(LayoutKind.Sequential, Size = 3)] + private struct RGB24 + { + } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index b4ec499469..db2189b3c4 100644 --- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs @@ -5,6 +5,7 @@ namespace ImageSharp.Formats { + using System; using System.Runtime.CompilerServices; /// @@ -12,28 +13,37 @@ namespace ImageSharp.Formats /// the value of a pixel. /// /// - internal static unsafe class AverageFilter + internal static class AverageFilter { /// /// Decodes the scanline /// /// The scanline to decode /// The previous scanline. - /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] scanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel) + public static void Decode(Span scanline, Span previousScanline, int bytesPerPixel) { + DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); + // Average(x) + floor((Raw(x-bpp)+Prior(x))/2) - fixed (byte* scan = scanline) - fixed (byte* prev = previousScanline) + for (int x = 1; x < scanline.Length; x++) { - for (int x = 1; x < bytesPerScanline; x++) + if (x - bytesPerPixel < 1) { - byte left = (x - bytesPerPixel < 1) ? (byte)0 : scan[x - bytesPerPixel]; - byte above = prev[x]; - - scan[x] = (byte)((scan[x] + Average(left, above)) % 256); + ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); + byte above = Unsafe.Add(ref prevBaseRef, x); + scan = (byte)((scan + (above >> 1)) % 256); + } + else + { + ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); + byte left = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); + byte above = Unsafe.Add(ref prevBaseRef, x); + scan = (byte)((scan + Average(left, above)) % 256); } } } @@ -46,21 +56,34 @@ namespace ImageSharp.Formats /// The filtered scanline result. /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel) + public static void Encode(Span scanline, Span previousScanline, Span result, int bytesPerPixel) { + DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); + + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); + ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); + // Average(x) = Raw(x) - floor((Raw(x-bpp)+Prior(x))/2) - fixed (byte* scan = scanline) - fixed (byte* prev = previousScanline) - fixed (byte* res = result) - { - res[0] = 3; + resultBaseRef = 3; - for (int x = 0; x < scanline.Length; x++) + for (int x = 0; x < scanline.Length; x++) + { + if (x - bytesPerPixel < 0) { - byte left = (x - bytesPerPixel < 0) ? (byte)0 : scan[x - bytesPerPixel]; - byte above = prev[x]; - - res[x + 1] = (byte)((scan[x] - Average(left, above)) % 256); + byte scan = Unsafe.Add(ref scanBaseRef, x); + byte above = Unsafe.Add(ref prevBaseRef, x); + ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); + res = (byte)((scan - (above >> 1)) % 256); + } + else + { + byte scan = Unsafe.Add(ref scanBaseRef, x); + byte left = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); + byte above = Unsafe.Add(ref prevBaseRef, x); + ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); + res = (byte)((scan - Average(left, above)) % 256); } } } diff --git a/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs b/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs index 5abd892964..ee77d1a5eb 100644 --- a/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/NoneFilter.cs @@ -8,6 +8,8 @@ namespace ImageSharp.Formats using System; using System.Runtime.CompilerServices; + using ImageSharp.Memory; + /// /// The None filter, the scanline is transmitted unmodified; it is only necessary to /// insert a filter type byte before the data. @@ -15,29 +17,18 @@ namespace ImageSharp.Formats /// internal static class NoneFilter { - /// - /// Decodes the scanline - /// - /// The scanline to decode - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte[] Decode(byte[] scanline) - { - // No change required. - return scanline; - } - /// /// Encodes the scanline /// /// The scanline to encode /// The filtered scanline result. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(byte[] scanline, byte[] result) + public static void Encode(Span scanline, Span result) { // Insert a byte before the data. result[0] = 0; - Buffer.BlockCopy(scanline, 0, result, 1, scanline.Length); + result = result.Slice(1); + SpanHelper.Copy(scanline, result); } } } diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index a43d4f0802..ec12eca058 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -21,22 +21,31 @@ namespace ImageSharp.Formats /// /// The scanline to decode /// The previous scanline. - /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] scanline, byte[] previousScanline, int bytesPerScanline, int bytesPerPixel) + public static void Decode(Span scanline, Span previousScanline, int bytesPerPixel) { + DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); + // Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp)) - fixed (byte* scan = scanline) - fixed (byte* prev = previousScanline) + for (int x = 1; x < scanline.Length; x++) { - for (int x = 1; x < bytesPerScanline; x++) + if (x - bytesPerPixel < 1) { - byte left = (x - bytesPerPixel < 1) ? (byte)0 : scan[x - bytesPerPixel]; - byte above = prev[x]; - byte upperLeft = (x - bytesPerPixel < 1) ? (byte)0 : prev[x - bytesPerPixel]; - - scan[x] = (byte)((scan[x] + PaethPredicator(left, above, upperLeft)) % 256); + ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); + byte above = Unsafe.Add(ref prevBaseRef, x); + scan = (byte)((scan + PaethPredicator(0, above, 0)) % 256); + } + else + { + ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); + byte left = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); + byte above = Unsafe.Add(ref prevBaseRef, x); + byte upperLeft = Unsafe.Add(ref prevBaseRef, x - bytesPerPixel); + scan = (byte)((scan + PaethPredicator(left, above, upperLeft)) % 256); } } } @@ -49,22 +58,35 @@ namespace ImageSharp.Formats /// The filtered scanline result. /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result, int bytesPerPixel) + public static void Encode(Span scanline, Span previousScanline, Span result, int bytesPerPixel) { + DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); + + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); + ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); + // Paeth(x) = Raw(x) - PaethPredictor(Raw(x-bpp), Prior(x), Prior(x - bpp)) - fixed (byte* scan = scanline) - fixed (byte* prev = previousScanline) - fixed (byte* res = result) - { - res[0] = 4; + resultBaseRef = 4; - for (int x = 0; x < scanline.Length; x++) + for (int x = 0; x < scanline.Length; x++) + { + if (x - bytesPerPixel < 0) { - byte left = (x - bytesPerPixel < 0) ? (byte)0 : scan[x - bytesPerPixel]; - byte above = prev[x]; - byte upperLeft = (x - bytesPerPixel < 0) ? (byte)0 : prev[x - bytesPerPixel]; - - res[x + 1] = (byte)((scan[x] - PaethPredicator(left, above, upperLeft)) % 256); + byte scan = Unsafe.Add(ref scanBaseRef, x); + byte above = Unsafe.Add(ref prevBaseRef, x); + ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); + res = (byte)((scan - PaethPredicator(0, above, 0)) % 256); + } + else + { + byte scan = Unsafe.Add(ref scanBaseRef, x); + byte left = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); + byte above = Unsafe.Add(ref prevBaseRef, x); + byte upperLeft = Unsafe.Add(ref prevBaseRef, x - bytesPerPixel); + ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); + res = (byte)((scan - PaethPredicator(left, above, upperLeft)) % 256); } } } @@ -100,4 +122,4 @@ namespace ImageSharp.Formats return upperLeft; } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs index 4610ed0ae4..f81122ad24 100644 --- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs @@ -5,6 +5,7 @@ namespace ImageSharp.Formats { + using System; using System.Runtime.CompilerServices; /// @@ -18,18 +19,25 @@ namespace ImageSharp.Formats /// Decodes the scanline /// /// The scanline to decode - /// The number of bytes per scanline /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] scanline, int bytesPerScanline, int bytesPerPixel) + public static void Decode(Span scanline, int bytesPerPixel) { + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + // Sub(x) + Raw(x-bpp) - fixed (byte* scan = scanline) + for (int x = 1; x < scanline.Length; x++) { - for (int x = 1; x < bytesPerScanline; x++) + if (x - bytesPerPixel < 1) + { + ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); + scan = (byte)(scan % 256); + } + else { - byte priorRawByte = (x - bytesPerPixel < 1) ? (byte)0 : scan[x - bytesPerPixel]; - scan[x] = (byte)((scan[x] + priorRawByte) % 256); + ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); + byte prev = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); + scan = (byte)((scan + prev) % 256); } } } @@ -41,19 +49,30 @@ namespace ImageSharp.Formats /// The filtered scanline result. /// The bytes per pixel. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(byte[] scanline, byte[] result, int bytesPerPixel) + public static void Encode(Span scanline, Span result, int bytesPerPixel) { + DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); + + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); + // Sub(x) = Raw(x) - Raw(x-bpp) - fixed (byte* scan = scanline) - fixed (byte* res = result) - { - res[0] = 1; + resultBaseRef = 1; - for (int x = 0; x < scanline.Length; x++) + for (int x = 0; x < scanline.Length; x++) + { + if (x - bytesPerPixel < 0) { - byte priorRawByte = (x - bytesPerPixel < 0) ? (byte)0 : scan[x - bytesPerPixel]; - - res[x + 1] = (byte)((scan[x] - priorRawByte) % 256); + byte scan = Unsafe.Add(ref scanBaseRef, x); + ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); + res = (byte)(scan % 256); + } + else + { + byte scan = Unsafe.Add(ref scanBaseRef, x); + byte prev = Unsafe.Add(ref scanBaseRef, x - bytesPerPixel); + ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); + res = (byte)((scan - prev) % 256); } } } diff --git a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs index 525f50d0ff..b89a3c5fcc 100644 --- a/src/ImageSharp/Formats/Png/Filters/UpFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/UpFilter.cs @@ -5,6 +5,7 @@ namespace ImageSharp.Formats { + using System; using System.Runtime.CompilerServices; /// @@ -19,20 +20,20 @@ namespace ImageSharp.Formats /// /// The scanline to decode /// The previous scanline. - /// The number of bytes per scanline [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Decode(byte[] scanline, byte[] previousScanline, int bytesPerScanline) + public static void Decode(Span scanline, Span previousScanline) { + DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); + // Up(x) + Prior(x) - fixed (byte* scan = scanline) - fixed (byte* prev = previousScanline) + for (int x = 1; x < scanline.Length; x++) { - for (int x = 1; x < bytesPerScanline; x++) - { - byte above = prev[x]; - - scan[x] = (byte)((scan[x] + above) % 256); - } + ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); + byte above = Unsafe.Add(ref prevBaseRef, x); + scan = (byte)((scan + above) % 256); } } @@ -43,21 +44,24 @@ namespace ImageSharp.Formats /// The previous scanline. /// The filtered scanline result. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Encode(byte[] scanline, byte[] previousScanline, byte[] result) + public static void Encode(Span scanline, Span previousScanline, Span result) { - // Up(x) = Raw(x) - Prior(x) - fixed (byte* scan = scanline) - fixed (byte* prev = previousScanline) - fixed (byte* res = result) - { - res[0] = 2; + DebugGuard.MustBeSameSized(scanline, previousScanline, nameof(scanline)); + DebugGuard.MustBeSizedAtLeast(result, scanline, nameof(result)); + + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); + ref byte prevBaseRef = ref previousScanline.DangerousGetPinnableReference(); + ref byte resultBaseRef = ref result.DangerousGetPinnableReference(); - for (int x = 0; x < scanline.Length; x++) - { - byte above = prev[x]; + // Up(x) = Raw(x) - Prior(x) + resultBaseRef = 2; - res[x + 1] = (byte)((scan[x] - above) % 256); - } + for (int x = 0; x < scanline.Length; x++) + { + byte scan = Unsafe.Add(ref scanBaseRef, x); + byte above = Unsafe.Add(ref prevBaseRef, x); + ref byte res = ref Unsafe.Add(ref resultBaseRef, x + 1); + res = (byte)((scan - above) % 256); } } } diff --git a/src/ImageSharp/Formats/Png/ImageExtensions.cs b/src/ImageSharp/Formats/Png/ImageExtensions.cs index 79e96175c1..44f242b3f8 100644 --- a/src/ImageSharp/Formats/Png/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Png/ImageExtensions.cs @@ -9,23 +9,25 @@ namespace ImageSharp using Formats; + using ImageSharp.PixelFormats; + /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Saves the image to the given stream with the png format. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. /// - /// The . + /// The . /// - public static Image SaveAsPng(this Image source, Stream stream) - where TColor : struct, IPixel + public static Image SaveAsPng(this Image source, Stream stream) + where TPixel : struct, IPixel { return SaveAsPng(source, stream, null); } @@ -33,16 +35,16 @@ namespace ImageSharp /// /// Saves the image to the given stream with the png format. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. /// - /// The . + /// The . /// - public static Image SaveAsPng(this Image source, Stream stream, IPngEncoderOptions options) - where TColor : struct, IPixel + public static Image SaveAsPng(this Image source, Stream stream, IPngEncoderOptions options) + where TPixel : struct, IPixel { PngEncoder encoder = new PngEncoder(); encoder.Encode(source, stream, options); diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index d0a820c17f..3a34147e2c 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -8,6 +8,8 @@ namespace ImageSharp.Formats using System; using System.IO; + using ImageSharp.PixelFormats; + /// /// Encoder for generating an image out of a png encoded stream. /// @@ -31,27 +33,27 @@ namespace ImageSharp.Formats public class PngDecoder : IImageDecoder { /// - public Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) + public Image Decode(Configuration configuration, Stream stream, IDecoderOptions options) - where TColor : struct, IPixel + where TPixel : struct, IPixel { IPngDecoderOptions pngOptions = PngDecoderOptions.Create(options); - return this.Decode(configuration, stream, pngOptions); + return this.Decode(configuration, stream, pngOptions); } /// - /// Decodes the image from the specified stream to the . + /// Decodes the image from the specified stream to the . /// - /// The pixel format. + /// The pixel format. /// The configuration for the image. /// The containing image data. /// The options for the decoder. /// The decoded image. - public Image Decode(Configuration configuration, Stream stream, IPngDecoderOptions options) - where TColor : struct, IPixel + public Image Decode(Configuration configuration, Stream stream, IPngDecoderOptions options) + where TPixel : struct, IPixel { - return new PngDecoderCore(options, configuration).Decode(stream); + return new PngDecoderCore(options, configuration).Decode(stream); } } } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index d6529940e2..f8be0f6cc7 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -12,6 +12,9 @@ namespace ImageSharp.Formats using System.Linq; using System.Runtime.CompilerServices; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + using static ComparableExtensions; /// @@ -22,7 +25,14 @@ namespace ImageSharp.Formats /// /// The dictionary of available color types. /// - private static readonly Dictionary ColorTypes = new Dictionary(); + private static readonly Dictionary ColorTypes = new Dictionary() + { + [PngColorType.Grayscale] = new byte[] { 1, 2, 4, 8 }, + [PngColorType.Rgb] = new byte[] { 8 }, + [PngColorType.Palette] = new byte[] { 1, 2, 4, 8 }, + [PngColorType.GrayscaleWithAlpha] = new byte[] { 8 }, + [PngColorType.RgbWithAlpha] = new byte[] { 8 }, + }; /// /// The amount to increment when processing each column per scanline for each interlaced pass @@ -120,20 +130,29 @@ namespace ImageSharp.Formats private bool isEndChunkReached; /// - /// Initializes static members of the class. + /// Previous scanline processed /// - static PngDecoderCore() - { - ColorTypes.Add((int)PngColorType.Grayscale, new byte[] { 1, 2, 4, 8 }); + private Buffer previousScanline; - ColorTypes.Add((int)PngColorType.Rgb, new byte[] { 8 }); + /// + /// The current scanline that is being processed + /// + private Buffer scanline; - ColorTypes.Add((int)PngColorType.Palette, new byte[] { 1, 2, 4, 8 }); + /// + /// The index of the current scanline being processed + /// + private int currentRow = Adam7FirstRow[0]; - ColorTypes.Add((int)PngColorType.GrayscaleWithAlpha, new byte[] { 8 }); + /// + /// The current pass for an interlaced PNG + /// + private int pass = 0; - ColorTypes.Add((int)PngColorType.RgbWithAlpha, new byte[] { 8 }); - } + /// + /// The current number of bytes read in the current scanline + /// + private int currentRowBytesRead = 0; /// /// Initializes a new instance of the class. @@ -154,7 +173,7 @@ namespace ImageSharp.Formats /// /// Decodes the stream to the image. /// - /// The pixel format. + /// The pixel format. /// The stream containing image data. /// /// Thrown if the stream does not contain and end chunk. @@ -163,72 +182,80 @@ namespace ImageSharp.Formats /// Thrown if the image is larger than the maximum allowable size. /// /// The decoded image - public Image Decode(Stream stream) - where TColor : struct, IPixel + public Image Decode(Stream stream) + where TPixel : struct, IPixel { - ImageMetaData metadata = new ImageMetaData(); + var metadata = new ImageMetaData(); this.currentStream = stream; this.currentStream.Skip(8); - - using (MemoryStream dataStream = new MemoryStream()) + Image image = null; + PixelAccessor pixels = null; + try { - PngChunk currentChunk; - while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null) + using (var deframeStream = new ZlibInflateStream(this.currentStream)) { - try + PngChunk currentChunk; + while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null) { - switch (currentChunk.Type) + try { - case PngChunkTypes.Header: - this.ReadHeaderChunk(currentChunk.Data); - this.ValidateHeader(); - break; - case PngChunkTypes.Physical: - this.ReadPhysicalChunk(metadata, currentChunk.Data); - break; - case PngChunkTypes.Data: - dataStream.Write(currentChunk.Data, 0, currentChunk.Length); - break; - case PngChunkTypes.Palette: - byte[] pal = new byte[currentChunk.Length]; - Buffer.BlockCopy(currentChunk.Data, 0, pal, 0, currentChunk.Length); - this.palette = pal; - metadata.Quality = pal.Length / 3; - break; - case PngChunkTypes.PaletteAlpha: - byte[] alpha = new byte[currentChunk.Length]; - Buffer.BlockCopy(currentChunk.Data, 0, alpha, 0, currentChunk.Length); - this.paletteAlpha = alpha; - break; - case PngChunkTypes.Text: - this.ReadTextChunk(metadata, currentChunk.Data, currentChunk.Length); - break; - case PngChunkTypes.End: - this.isEndChunkReached = true; - break; + switch (currentChunk.Type) + { + case PngChunkTypes.Header: + this.ReadHeaderChunk(currentChunk.Data); + this.ValidateHeader(); + break; + case PngChunkTypes.Physical: + this.ReadPhysicalChunk(metadata, currentChunk.Data); + break; + case PngChunkTypes.Data: + if (image == null) + { + this.InitializeImage(metadata, out image, out pixels); + } + + deframeStream.AllocateNewBytes(currentChunk.Length); + this.ReadScanlines(deframeStream.CompressedStream, pixels); + stream.Read(this.crcBuffer, 0, 4); + break; + case PngChunkTypes.Palette: + byte[] pal = new byte[currentChunk.Length]; + Buffer.BlockCopy(currentChunk.Data, 0, pal, 0, currentChunk.Length); + this.palette = pal; + metadata.Quality = pal.Length / 3; + break; + case PngChunkTypes.PaletteAlpha: + byte[] alpha = new byte[currentChunk.Length]; + Buffer.BlockCopy(currentChunk.Data, 0, alpha, 0, currentChunk.Length); + this.paletteAlpha = alpha; + break; + case PngChunkTypes.Text: + this.ReadTextChunk(metadata, currentChunk.Data, currentChunk.Length); + break; + case PngChunkTypes.End: + this.isEndChunkReached = true; + break; + } + } + finally + { + // Data is rented in ReadChunkData() + if (currentChunk.Data != null) + { + ArrayPool.Shared.Return(currentChunk.Data); + } } } - finally - { - // Data is rented in ReadChunkData() - ArrayPool.Shared.Return(currentChunk.Data); - } - } - - if (this.header.Width > Image.MaxWidth || this.header.Height > Image.MaxHeight) - { - throw new ArgumentOutOfRangeException($"The input png '{this.header.Width}x{this.header.Height}' is bigger than the max allowed size '{Image.MaxWidth}x{Image.MaxHeight}'"); - } - - Image image = Image.Create(this.header.Width, this.header.Height, metadata, this.configuration); - - using (PixelAccessor pixels = image.Lock()) - { - this.ReadScanlines(dataStream, pixels); } return image; } + finally + { + pixels?.Dispose(); + this.scanline?.Dispose(); + this.previousScanline?.Dispose(); + } } /// @@ -291,6 +318,35 @@ namespace ImageSharp.Formats metadata.VerticalResolution = BitConverter.ToInt32(data, 4) / 39.3700787d; } + /// + /// Initializes the image and various buffers needed for processing + /// + /// The type the pixels will be + /// The metadata information for the image + /// The image that we will populate + /// The pixel accessor + private void InitializeImage(ImageMetaData metadata, out Image image, out PixelAccessor pixels) + where TPixel : struct, IPixel + { + if (this.header.Width > Image.MaxWidth || this.header.Height > Image.MaxHeight) + { + throw new ArgumentOutOfRangeException($"The input png '{this.header.Width}x{this.header.Height}' is bigger than the max allowed size '{Image.MaxWidth}x{Image.MaxHeight}'"); + } + + image = new Image(this.configuration, this.header.Width, this.header.Height, metadata); + pixels = image.Lock(); + this.bytesPerPixel = this.CalculateBytesPerPixel(); + this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1; + this.bytesPerSample = 1; + if (this.header.BitDepth >= 8) + { + this.bytesPerSample = this.header.BitDepth / 8; + } + + this.previousScanline = Buffer.CreateClean(this.bytesPerScanline); + this.scanline = Buffer.CreateClean(this.bytesPerScanline); + } + /// /// Calculates the correct number of bytes per pixel for the given color type. /// @@ -340,103 +396,76 @@ namespace ImageSharp.Formats /// /// Reads the scanlines within the image. /// - /// The pixel format. + /// The pixel format. /// The containing data. /// The pixel data. - private void ReadScanlines(MemoryStream dataStream, PixelAccessor pixels) - where TColor : struct, IPixel + private void ReadScanlines(Stream dataStream, PixelAccessor pixels) + where TPixel : struct, IPixel { - this.bytesPerPixel = this.CalculateBytesPerPixel(); - this.bytesPerScanline = this.CalculateScanlineLength(this.header.Width) + 1; - this.bytesPerSample = 1; - if (this.header.BitDepth >= 8) + if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) { - this.bytesPerSample = this.header.BitDepth / 8; + this.DecodeInterlacedPixelData(dataStream, pixels); } - - dataStream.Position = 0; - using (ZlibInflateStream compressedStream = new ZlibInflateStream(dataStream)) + else { - if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) - { - this.DecodeInterlacedPixelData(compressedStream, pixels); - } - else - { - this.DecodePixelData(compressedStream, pixels); - } + this.DecodePixelData(dataStream, pixels); } } /// /// Decodes the raw pixel data row by row /// - /// The pixel format. + /// The pixel format. /// The compressed pixel data stream. /// The image pixel accessor. - private void DecodePixelData(Stream compressedStream, PixelAccessor pixels) - where TColor : struct, IPixel + private void DecodePixelData(Stream compressedStream, PixelAccessor pixels) + where TPixel : struct, IPixel { - byte[] previousScanline = ArrayPool.Shared.Rent(this.bytesPerScanline); - byte[] scanline = ArrayPool.Shared.Rent(this.bytesPerScanline); - - // Zero out the scanlines, because the bytes that are rented from the arraypool may not be zero. - Array.Clear(scanline, 0, this.bytesPerScanline); - Array.Clear(previousScanline, 0, this.bytesPerScanline); - - try + while (this.currentRow < this.header.Height) { - for (int y = 0; y < this.header.Height; y++) + int bytesRead = compressedStream.Read(this.scanline.Array, this.currentRowBytesRead, this.bytesPerScanline - this.currentRowBytesRead); + this.currentRowBytesRead += bytesRead; + if (this.currentRowBytesRead < this.bytesPerScanline) { - compressedStream.Read(scanline, 0, this.bytesPerScanline); - - FilterType filterType = (FilterType)scanline[0]; - - switch (filterType) - { - case FilterType.None: - - NoneFilter.Decode(scanline); - - break; - - case FilterType.Sub: - - SubFilter.Decode(scanline, this.bytesPerScanline, this.bytesPerPixel); + return; + } - break; + this.currentRowBytesRead = 0; + var filterType = (FilterType)this.scanline[0]; - case FilterType.Up: + switch (filterType) + { + case FilterType.None: + break; - UpFilter.Decode(scanline, previousScanline, this.bytesPerScanline); + case FilterType.Sub: - break; + SubFilter.Decode(this.scanline, this.bytesPerPixel); + break; - case FilterType.Average: + case FilterType.Up: - AverageFilter.Decode(scanline, previousScanline, this.bytesPerScanline, this.bytesPerPixel); + UpFilter.Decode(this.scanline, this.previousScanline); + break; - break; + case FilterType.Average: - case FilterType.Paeth: + AverageFilter.Decode(this.scanline, this.previousScanline, this.bytesPerPixel); + break; - PaethFilter.Decode(scanline, previousScanline, this.bytesPerScanline, this.bytesPerPixel); + case FilterType.Paeth: - break; + PaethFilter.Decode(this.scanline, this.previousScanline, this.bytesPerPixel); + break; - default: - throw new ImageFormatException("Unknown filter type."); - } + default: + throw new ImageFormatException("Unknown filter type."); + } - this.ProcessDefilteredScanline(scanline, y, pixels); + this.ProcessDefilteredScanline(this.scanline.Array, pixels); - Swap(ref scanline, ref previousScanline); - } - } - finally - { - ArrayPool.Shared.Return(previousScanline); - ArrayPool.Shared.Return(scanline); + Swap(ref this.scanline, ref this.previousScanline); + this.currentRow++; } } @@ -444,102 +473,102 @@ namespace ImageSharp.Formats /// Decodes the raw interlaced pixel data row by row /// /// - /// The pixel format. + /// The pixel format. /// The compressed pixel data stream. /// The image pixel accessor. - private void DecodeInterlacedPixelData(Stream compressedStream, PixelAccessor pixels) - where TColor : struct, IPixel + private void DecodeInterlacedPixelData(Stream compressedStream, PixelAccessor pixels) + where TPixel : struct, IPixel { - byte[] previousScanline = ArrayPool.Shared.Rent(this.bytesPerScanline); - byte[] scanline = ArrayPool.Shared.Rent(this.bytesPerScanline); - - try + while (true) { - for (int pass = 0; pass < 7; pass++) - { - // Zero out the scanlines, because the bytes that are rented from the arraypool may not be zero. - Array.Clear(scanline, 0, this.bytesPerScanline); - Array.Clear(previousScanline, 0, this.bytesPerScanline); + int numColumns = this.ComputeColumnsAdam7(this.pass); - int y = Adam7FirstRow[pass]; - int numColumns = this.ComputeColumnsAdam7(pass); + if (numColumns == 0) + { + this.pass++; - if (numColumns == 0) - { - // This pass contains no data; skip to next pass - continue; - } + // This pass contains no data; skip to next pass + continue; + } - int bytesPerInterlaceScanline = this.CalculateScanlineLength(numColumns) + 1; + int bytesPerInterlaceScanline = this.CalculateScanlineLength(numColumns) + 1; - while (y < this.header.Height) + while (this.currentRow < this.header.Height) + { + int bytesRead = compressedStream.Read(this.scanline.Array, this.currentRowBytesRead, bytesPerInterlaceScanline - this.currentRowBytesRead); + this.currentRowBytesRead += bytesRead; + if (this.currentRowBytesRead < bytesPerInterlaceScanline) { - compressedStream.Read(scanline, 0, bytesPerInterlaceScanline); - - FilterType filterType = (FilterType)scanline[0]; - - switch (filterType) - { - case FilterType.None: - - NoneFilter.Decode(scanline); - - break; + return; + } - case FilterType.Sub: + this.currentRowBytesRead = 0; - SubFilter.Decode(scanline, bytesPerInterlaceScanline, this.bytesPerPixel); + Span scanSpan = this.scanline.Slice(0, bytesPerInterlaceScanline); + Span prevSpan = this.previousScanline.Span.Slice(0, bytesPerInterlaceScanline); + var filterType = (FilterType)scanSpan[0]; - break; + switch (filterType) + { + case FilterType.None: + break; - case FilterType.Up: + case FilterType.Sub: - UpFilter.Decode(scanline, previousScanline, bytesPerInterlaceScanline); + SubFilter.Decode(scanSpan, this.bytesPerPixel); + break; - break; + case FilterType.Up: - case FilterType.Average: + UpFilter.Decode(scanSpan, prevSpan); + break; - AverageFilter.Decode(scanline, previousScanline, bytesPerInterlaceScanline, this.bytesPerPixel); + case FilterType.Average: - break; + AverageFilter.Decode(scanSpan, prevSpan, this.bytesPerPixel); + break; - case FilterType.Paeth: + case FilterType.Paeth: - PaethFilter.Decode(scanline, previousScanline, bytesPerInterlaceScanline, this.bytesPerPixel); + PaethFilter.Decode(scanSpan, prevSpan, this.bytesPerPixel); + break; - break; + default: + throw new ImageFormatException("Unknown filter type."); + } - default: - throw new ImageFormatException("Unknown filter type."); - } + this.ProcessInterlacedDefilteredScanline(this.scanline.Array, this.currentRow, pixels, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]); - this.ProcessInterlacedDefilteredScanline(scanline, y, pixels, Adam7FirstColumn[pass], Adam7ColumnIncrement[pass]); + Swap(ref this.scanline, ref this.previousScanline); - Swap(ref scanline, ref previousScanline); + this.currentRow += Adam7RowIncrement[this.pass]; + } - y += Adam7RowIncrement[pass]; - } + this.pass++; + if (this.pass < 7) + { + this.currentRow = Adam7FirstRow[this.pass]; + } + else + { + break; } - } - finally - { - ArrayPool.Shared.Return(previousScanline); - ArrayPool.Shared.Return(scanline); } } /// /// Processes the de-filtered scanline filling the image pixel data /// - /// The pixel format. + /// The pixel format. /// The de-filtered scanline - /// The current image row. /// The image pixels - private void ProcessDefilteredScanline(byte[] defilteredScanline, int row, PixelAccessor pixels) - where TColor : struct, IPixel + private void ProcessDefilteredScanline(byte[] defilteredScanline, PixelAccessor pixels) + where TPixel : struct, IPixel { - TColor color = default(TColor); + var color = default(TPixel); + Span pixelBuffer = pixels.GetRowSpan(this.currentRow); + var scanlineBuffer = new Span(defilteredScanline, 1); + switch (this.PngColorType) { case PngColorType.Grayscale: @@ -549,7 +578,7 @@ namespace ImageSharp.Formats { byte intensity = (byte)(newScanline1[x] * factor); color.PackFromBytes(intensity, intensity, intensity, 255); - pixels[x, row] = color; + pixels[x, this.currentRow] = color; } break; @@ -564,107 +593,100 @@ namespace ImageSharp.Formats byte alpha = defilteredScanline[offset + this.bytesPerSample]; color.PackFromBytes(intensity, intensity, intensity, alpha); - pixels[x, row] = color; + pixels[x, this.currentRow] = color; } break; case PngColorType.Palette: - byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); + this.ProcessScanlineFromPalette(defilteredScanline, pixels); - if (this.paletteAlpha != null && this.paletteAlpha.Length > 0) - { - // If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha - // channel and we should try to read it. - for (int x = 0; x < this.header.Width; x++) - { - int index = newScanline[x]; - int pixelOffset = index * 3; + break; - byte a = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255; + case PngColorType.Rgb: - if (a > 0) - { - byte r = this.palette[pixelOffset]; - byte g = this.palette[pixelOffset + 1]; - byte b = this.palette[pixelOffset + 2]; - color.PackFromBytes(r, g, b, a); - } - else - { - color.PackFromBytes(0, 0, 0, 0); - } + PixelOperations.Instance.PackFromXyzBytes(scanlineBuffer, pixelBuffer, this.header.Width); - pixels[x, row] = color; - } - } - else - { - for (int x = 0; x < this.header.Width; x++) - { - int index = newScanline[x]; - int pixelOffset = index * 3; + break; - byte r = this.palette[pixelOffset]; - byte g = this.palette[pixelOffset + 1]; - byte b = this.palette[pixelOffset + 2]; + case PngColorType.RgbWithAlpha: - color.PackFromBytes(r, g, b, 255); - pixels[x, row] = color; - } - } + PixelOperations.Instance.PackFromXyzwBytes(scanlineBuffer, pixelBuffer, this.header.Width); break; + } + } - case PngColorType.Rgb: + /// + /// Processes a scanline that uses a palette + /// + /// The type of pixel we are expanding to + /// The scanline + /// The output pixels + private void ProcessScanlineFromPalette(byte[] defilteredScanline, PixelAccessor pixels) + where TPixel : struct, IPixel + { + byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); + byte[] palette = this.palette; + var color = default(TPixel); - for (int x = 0; x < this.header.Width; x++) - { - int offset = 1 + (x * this.bytesPerPixel); + if (this.paletteAlpha != null && this.paletteAlpha.Length > 0) + { + // If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha + // channel and we should try to read it. + for (int x = 0; x < this.header.Width; x++) + { + int index = newScanline[x + 1]; + int pixelOffset = index * 3; - byte r = defilteredScanline[offset]; - byte g = defilteredScanline[offset + this.bytesPerSample]; - byte b = defilteredScanline[offset + (2 * this.bytesPerSample)]; + byte a = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255; - color.PackFromBytes(r, g, b, 255); - pixels[x, row] = color; + if (a > 0) + { + byte r = palette[pixelOffset]; + byte g = palette[pixelOffset + 1]; + byte b = palette[pixelOffset + 2]; + color.PackFromBytes(r, g, b, a); } - - break; - - case PngColorType.RgbWithAlpha: - - for (int x = 0; x < this.header.Width; x++) + else { - int offset = 1 + (x * this.bytesPerPixel); + color.PackFromBytes(0, 0, 0, 0); + } - byte r = defilteredScanline[offset]; - byte g = defilteredScanline[offset + this.bytesPerSample]; - byte b = defilteredScanline[offset + (2 * this.bytesPerSample)]; - byte a = defilteredScanline[offset + (3 * this.bytesPerSample)]; + pixels[x, this.currentRow] = color; + } + } + else + { + for (int x = 0; x < this.header.Width; x++) + { + int index = newScanline[x + 1]; + int pixelOffset = index * 3; - color.PackFromBytes(r, g, b, a); - pixels[x, row] = color; - } + byte r = palette[pixelOffset]; + byte g = palette[pixelOffset + 1]; + byte b = palette[pixelOffset + 2]; - break; + color.PackFromBytes(r, g, b, 255); + pixels[x, this.currentRow] = color; + } } } /// /// Processes the interlaced de-filtered scanline filling the image pixel data /// - /// The pixel format. + /// The pixel format. /// The de-filtered scanline /// The current image row. /// The image pixels /// The column start index. Always 0 for none interlaced images. /// The column increment. Always 1 for none interlaced images. - private void ProcessInterlacedDefilteredScanline(byte[] defilteredScanline, int row, PixelAccessor pixels, int pixelOffset = 0, int increment = 1) - where TColor : struct, IPixel + private void ProcessInterlacedDefilteredScanline(byte[] defilteredScanline, int row, PixelAccessor pixels, int pixelOffset = 0, int increment = 1) + where TPixel : struct, IPixel { - TColor color = default(TColor); + var color = default(TPixel); switch (this.PngColorType) { @@ -817,7 +839,7 @@ namespace ImageSharp.Formats this.header.Height = BitConverter.ToInt32(data, 4); this.header.BitDepth = data[8]; - this.header.ColorType = data[9]; + this.header.ColorType = (PngColorType)data[9]; this.header.CompressionMethod = data[10]; this.header.FilterMethod = data[11]; this.header.InterlaceMethod = (PngInterlaceMode)data[12]; @@ -862,7 +884,7 @@ namespace ImageSharp.Formats /// private PngChunk ReadChunk() { - PngChunk chunk = new PngChunk(); + var chunk = new PngChunk(); this.ReadChunkLength(chunk); if (chunk.Length < 0) { @@ -870,6 +892,11 @@ namespace ImageSharp.Formats } this.ReadChunkType(chunk); + if (chunk.Type == PngChunkTypes.Data) + { + return chunk; + } + this.ReadChunkData(chunk); this.ReadChunkCrc(chunk); @@ -982,4 +1009,4 @@ namespace ImageSharp.Formats } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs index e583f381fb..f89b624f78 100644 --- a/src/ImageSharp/Formats/Png/PngEncoder.cs +++ b/src/ImageSharp/Formats/Png/PngEncoder.cs @@ -7,14 +7,16 @@ namespace ImageSharp.Formats { using System.IO; + using ImageSharp.PixelFormats; + /// /// Image encoder for writing image data to a stream in png format. /// public class PngEncoder : IImageEncoder { /// - public void Encode(Image image, Stream stream, IEncoderOptions options) - where TColor : struct, IPixel + public void Encode(Image image, Stream stream, IEncoderOptions options) + where TPixel : struct, IPixel { IPngEncoderOptions pngOptions = PngEncoderOptions.Create(options); @@ -22,17 +24,19 @@ namespace ImageSharp.Formats } /// - /// Encodes the image to the specified stream from the . + /// Encodes the image to the specified stream from the . /// - /// The pixel format. - /// The to encode from. + /// The pixel format. + /// The to encode from. /// The to encode the image data to. /// The options for the encoder. - public void Encode(Image image, Stream stream, IPngEncoderOptions options) - where TColor : struct, IPixel + public void Encode(Image image, Stream stream, IPngEncoderOptions options) + where TPixel : struct, IPixel { - PngEncoderCore encode = new PngEncoderCore(options); - encode.Encode(image, stream); + using (var encode = new PngEncoderCore(options)) + { + encode.Encode(image, stream); + } } } } diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 498ae578c3..0482b2691c 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -7,9 +7,12 @@ namespace ImageSharp.Formats { using System; using System.Buffers; - using System.Collections.Generic; using System.IO; using System.Linq; + using System.Runtime.CompilerServices; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; using Quantizers; @@ -18,7 +21,7 @@ namespace ImageSharp.Formats /// /// Performs the png encoding operation. /// - internal sealed class PngEncoderCore + internal sealed class PngEncoderCore : IDisposable { /// /// The maximum block size, defaults at 64k for uncompressed blocks. @@ -70,25 +73,45 @@ namespace ImageSharp.Formats /// private int bytesPerPixel; + /// + /// The number of bytes per scanline. + /// + private int bytesPerScanline; + + /// + /// The previous scanline. + /// + private Buffer previousScanline; + + /// + /// The raw scanline. + /// + private Buffer rawScanline; + + /// + /// The filtered scanline result. + /// + private Buffer result; + /// /// The buffer for the sub filter /// - private byte[] sub; + private Buffer sub; /// /// The buffer for the up filter /// - private byte[] up; + private Buffer up; /// /// The buffer for the average filter /// - private byte[] average; + private Buffer average; /// /// The buffer for the paeth filter /// - private byte[] paeth; + private Buffer paeth; /// /// The quality of output for images. @@ -115,13 +138,13 @@ namespace ImageSharp.Formats } /// - /// Encodes the image to the specified stream from the . + /// Encodes the image to the specified stream from the . /// - /// The pixel format. - /// The to encode from. + /// The pixel format. + /// The to encode from. /// The to encode the image data to. - public void Encode(Image image, Stream stream) - where TColor : struct, IPixel + public void Encode(Image image, Stream stream) + where TPixel : struct, IPixel { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); @@ -176,11 +199,11 @@ namespace ImageSharp.Formats this.bytesPerPixel = this.CalculateBytesPerPixel(); - PngHeader header = new PngHeader + var header = new PngHeader { Width = image.Width, Height = image.Height, - ColorType = (byte)this.pngColorType, + ColorType = this.pngColorType, BitDepth = this.bitDepth, FilterMethod = 0, // None CompressionMethod = 0, @@ -197,7 +220,7 @@ namespace ImageSharp.Formats this.WritePhysicalChunk(stream, image); this.WriteGammaChunk(stream); - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { this.WriteDataChunks(pixels, stream); } @@ -206,6 +229,20 @@ namespace ImageSharp.Formats stream.Flush(); } + /// + /// Disposes PngEncoderCore instance, disposing it's internal buffers. + /// + public void Dispose() + { + this.previousScanline?.Dispose(); + this.rawScanline?.Dispose(); + this.result?.Dispose(); + this.sub?.Dispose(); + this.up?.Dispose(); + this.average?.Dispose(); + this.paeth?.Dispose(); + } + /// /// Writes an integer to the byte array. /// @@ -249,28 +286,29 @@ namespace ImageSharp.Formats /// /// Collects the indexed pixel data. /// - /// The pixel format. + /// The pixel format. /// The image to encode. /// The containing image data. /// The . - private void CollectIndexedBytes(ImageBase image, Stream stream, PngHeader header) - where TColor : struct, IPixel + private void CollectIndexedBytes(ImageBase image, Stream stream, PngHeader header) + where TPixel : struct, IPixel { // Quantize the image and get the pixels. - QuantizedImage quantized = this.WritePaletteChunk(stream, header, image); + QuantizedImage quantized = this.WritePaletteChunk(stream, header, image); this.palettePixelData = quantized.Pixels; } /// /// Collects a row of grayscale pixels. /// - /// The pixel format. + /// The pixel format. /// The image pixels accessor. /// The row index. - /// The raw scanline. - private void CollectGrayscaleBytes(PixelAccessor pixels, int row, byte[] rawScanline) - where TColor : struct, IPixel + private void CollectGrayscaleBytes(PixelAccessor pixels, int row) + where TPixel : struct, IPixel { + byte[] rawScanlineArray = this.rawScanline.Array; + // Copy the pixels across from the image. // Reuse the chunk type buffer. for (int x = 0; x < this.width; x++) @@ -285,11 +323,11 @@ namespace ImageSharp.Formats { if (i == 0) { - rawScanline[offset] = luminance; + rawScanlineArray[offset] = luminance; } else { - rawScanline[offset + i] = this.chunkTypeBuffer[3]; + rawScanlineArray[offset + i] = this.chunkTypeBuffer[3]; } } } @@ -298,17 +336,21 @@ namespace ImageSharp.Formats /// /// Collects a row of true color pixel data. /// - /// The pixel format. + /// The pixel format. /// The image pixel accessor. /// The row index. - /// The raw scanline. - private void CollectColorBytes(PixelAccessor pixels, int row, byte[] rawScanline) - where TColor : struct, IPixel + private void CollecTPixelBytes(PixelAccessor pixels, int row) + where TPixel : struct, IPixel { - // We can use the optimized PixelAccessor here and copy the bytes in unmanaged memory. - using (PixelArea pixelRow = new PixelArea(this.width, rawScanline, this.bytesPerPixel == 4 ? ComponentOrder.Xyzw : ComponentOrder.Xyz)) + Span rowSpan = pixels.GetRowSpan(row); + + if (this.bytesPerPixel == 4) { - pixels.CopyTo(pixelRow, row); + PixelOperations.Instance.ToXyzwBytes(rowSpan, this.rawScanline, this.width); + } + else + { + PixelOperations.Instance.ToXyzBytes(rowSpan, this.rawScanline, this.width); } } @@ -316,84 +358,82 @@ namespace ImageSharp.Formats /// Encodes the pixel data line by line. /// Each scanline is encoded in the most optimal manner to improve compression. /// - /// The pixel format. + /// The pixel format. /// The image pixel accessor. /// The row. - /// The previous scanline. - /// The raw scanline. - /// The filtered scanline result. /// The - private byte[] EncodePixelRow(PixelAccessor pixels, int row, byte[] previousScanline, byte[] rawScanline, byte[] result) - where TColor : struct, IPixel + private Buffer EncodePixelRow(PixelAccessor pixels, int row) + where TPixel : struct, IPixel { switch (this.pngColorType) { case PngColorType.Palette: - Buffer.BlockCopy(this.palettePixelData, row * rawScanline.Length, rawScanline, 0, rawScanline.Length); + Buffer.BlockCopy(this.palettePixelData, row * this.rawScanline.Length, this.rawScanline.Array, 0, this.rawScanline.Length); break; case PngColorType.Grayscale: case PngColorType.GrayscaleWithAlpha: - this.CollectGrayscaleBytes(pixels, row, rawScanline); + this.CollectGrayscaleBytes(pixels, row); break; default: - this.CollectColorBytes(pixels, row, rawScanline); + this.CollecTPixelBytes(pixels, row); break; } - return this.GetOptimalFilteredScanline(rawScanline, previousScanline, result); + return this.GetOptimalFilteredScanline(); } /// /// Applies all PNG filters to the given scanline and returns the filtered scanline that is deemed /// to be most compressible, using lowest total variation as proxy for compressibility. /// - /// The raw scanline - /// The previous scanline - /// The filtered scanline result. /// The - private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, byte[] result) + private Buffer GetOptimalFilteredScanline() { + Span scanSpan = this.rawScanline.Span; + Span prevSpan = this.previousScanline.Span; + // Palette images don't compress well with adaptive filtering. if (this.pngColorType == PngColorType.Palette || this.bitDepth < 8) { - NoneFilter.Encode(rawScanline, result); - return result; + NoneFilter.Encode(this.rawScanline, this.result); + return this.result; } // This order, while different to the enumerated order is more likely to produce a smaller sum // early on which shaves a couple of milliseconds off the processing time. - UpFilter.Encode(rawScanline, previousScanline, this.up); + UpFilter.Encode(scanSpan, prevSpan, this.up); + int currentSum = this.CalculateTotalVariation(this.up, int.MaxValue); int lowestSum = currentSum; - result = this.up; + Buffer actualResult = this.up; - PaethFilter.Encode(rawScanline, previousScanline, this.paeth, this.bytesPerPixel); + PaethFilter.Encode(scanSpan, prevSpan, this.paeth, this.bytesPerPixel); currentSum = this.CalculateTotalVariation(this.paeth, currentSum); if (currentSum < lowestSum) { lowestSum = currentSum; - result = this.paeth; + actualResult = this.paeth; } - SubFilter.Encode(rawScanline, this.sub, this.bytesPerPixel); + SubFilter.Encode(scanSpan, this.sub, this.bytesPerPixel); currentSum = this.CalculateTotalVariation(this.sub, int.MaxValue); if (currentSum < lowestSum) { lowestSum = currentSum; - result = this.sub; + actualResult = this.sub; } - AverageFilter.Encode(rawScanline, previousScanline, this.average, this.bytesPerPixel); + AverageFilter.Encode(scanSpan, prevSpan, this.average, this.bytesPerPixel); currentSum = this.CalculateTotalVariation(this.average, currentSum); if (currentSum < lowestSum) { - result = this.average; + actualResult = this.average; } - return result; + return actualResult; } /// @@ -403,17 +443,19 @@ namespace ImageSharp.Formats /// The scanline bytes /// The last variation sum /// The - private int CalculateTotalVariation(byte[] scanline, int lastSum) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int CalculateTotalVariation(Span scanline, int lastSum) { + ref byte scanBaseRef = ref scanline.DangerousGetPinnableReference(); int sum = 0; - for (int i = 1; i < scanline.Length; i++) + for (int i = 1; i < this.bytesPerScanline; i++) { - byte v = scanline[i]; + byte v = Unsafe.Add(ref scanBaseRef, i); sum += v < 128 ? v : 256 - v; // No point continuing if we are larger. - if (sum > lastSum) + if (sum >= lastSum) { break; } @@ -461,7 +503,7 @@ namespace ImageSharp.Formats WriteInteger(this.chunkDataBuffer, 4, header.Height); this.chunkDataBuffer[8] = header.BitDepth; - this.chunkDataBuffer[9] = header.ColorType; + this.chunkDataBuffer[9] = (byte)header.ColorType; this.chunkDataBuffer[10] = header.CompressionMethod; this.chunkDataBuffer[11] = header.FilterMethod; this.chunkDataBuffer[12] = (byte)header.InterlaceMethod; @@ -472,13 +514,13 @@ namespace ImageSharp.Formats /// /// Writes the palette chunk to the stream. /// - /// The pixel format. + /// The pixel format. /// The containing image data. /// The . /// The image to encode. - /// The - private QuantizedImage WritePaletteChunk(Stream stream, PngHeader header, ImageBase image) - where TColor : struct, IPixel + /// The + private QuantizedImage WritePaletteChunk(Stream stream, PngHeader header, ImageBase image) + where TPixel : struct, IPixel { if (this.quality > 256) { @@ -487,66 +529,73 @@ namespace ImageSharp.Formats if (this.quantizer == null) { - this.quantizer = new OctreeQuantizer(); + this.quantizer = new WuQuantizer(); } // Quantize the image returning a palette. This boxing is icky. - QuantizedImage quantized = ((IQuantizer)this.quantizer).Quantize(image, this.quality); + QuantizedImage quantized = ((IQuantizer)this.quantizer).Quantize(image, this.quality); // Grab the palette and write it to the stream. - TColor[] palette = quantized.Palette; - int pixelCount = palette.Length; - List transparentPixels = new List(); + TPixel[] palette = quantized.Palette; + byte pixelCount = palette.Length.ToByte(); // Get max colors for bit depth. int colorTableLength = (int)Math.Pow(2, header.BitDepth) * 3; byte[] colorTable = ArrayPool.Shared.Rent(colorTableLength); + byte[] alphaTable = ArrayPool.Shared.Rent(pixelCount); byte[] bytes = ArrayPool.Shared.Rent(4); - + bool anyAlpha = false; try { - for (int i = 0; i < pixelCount; i++) + for (byte i = 0; i < pixelCount; i++) { - int offset = i * 3; - palette[i].ToXyzwBytes(bytes, 0); + if (quantized.Pixels.Contains(i)) + { + int offset = i * 3; + palette[i].ToXyzwBytes(bytes, 0); - int alpha = bytes[3]; + byte alpha = bytes[3]; - colorTable[offset] = bytes[0]; - colorTable[offset + 1] = bytes[1]; - colorTable[offset + 2] = bytes[2]; + colorTable[offset] = bytes[0]; + colorTable[offset + 1] = bytes[1]; + colorTable[offset + 2] = bytes[2]; - if (alpha <= this.options.Threshold) - { - transparentPixels.Add((byte)offset); + if (alpha > this.options.Threshold) + { + alpha = 255; + } + + anyAlpha = anyAlpha || alpha < 255; + alphaTable[i] = alpha; } } this.WriteChunk(stream, PngChunkTypes.Palette, colorTable, 0, colorTableLength); + + // Write the transparency data + if (anyAlpha) + { + this.WriteChunk(stream, PngChunkTypes.PaletteAlpha, alphaTable, 0, pixelCount); + } } finally { ArrayPool.Shared.Return(colorTable); + ArrayPool.Shared.Return(alphaTable); ArrayPool.Shared.Return(bytes); } - // Write the transparency data - if (transparentPixels.Any()) - { - this.WriteChunk(stream, PngChunkTypes.PaletteAlpha, transparentPixels.ToArray()); - } - return quantized; } /// /// Writes the physical dimension information to the stream. /// - /// The pixel format. + /// The pixel format. /// The containing image data. /// The image. - private void WritePhysicalChunk(Stream stream, Image image) - where TColor : struct, IPixel + private void WritePhysicalChunk(Stream stream, Image image) + where TPixel : struct, IPixel { if (image.MetaData.HorizontalResolution > 0 && image.MetaData.VerticalResolution > 0) { @@ -587,24 +636,25 @@ namespace ImageSharp.Formats /// /// Writes the pixel information to the stream. /// - /// The pixel format. + /// The pixel format. /// The pixel accessor. /// The stream. - private void WriteDataChunks(PixelAccessor pixels, Stream stream) - where TColor : struct, IPixel + private void WriteDataChunks(PixelAccessor pixels, Stream stream) + where TPixel : struct, IPixel { - int bytesPerScanline = this.width * this.bytesPerPixel; - byte[] previousScanline = new byte[bytesPerScanline]; - byte[] rawScanline = new byte[bytesPerScanline]; - int resultLength = bytesPerScanline + 1; - byte[] result = new byte[resultLength]; + this.bytesPerScanline = this.width * this.bytesPerPixel; + int resultLength = this.bytesPerScanline + 1; + + this.previousScanline = new Buffer(this.bytesPerScanline); + this.rawScanline = new Buffer(this.bytesPerScanline); + this.result = new Buffer(resultLength); if (this.pngColorType != PngColorType.Palette) { - this.sub = new byte[resultLength]; - this.up = new byte[resultLength]; - this.average = new byte[resultLength]; - this.paeth = new byte[resultLength]; + this.sub = Buffer.CreateClean(resultLength); + this.up = Buffer.CreateClean(resultLength); + this.average = Buffer.CreateClean(resultLength); + this.paeth = Buffer.CreateClean(resultLength); } byte[] buffer; @@ -613,13 +663,14 @@ namespace ImageSharp.Formats try { memoryStream = new MemoryStream(); - using (ZlibDeflateStream deflateStream = new ZlibDeflateStream(memoryStream, this.options.CompressionLevel)) + using (var deflateStream = new ZlibDeflateStream(memoryStream, this.options.CompressionLevel)) { for (int y = 0; y < this.height; y++) { - deflateStream.Write(this.EncodePixelRow(pixels, y, previousScanline, rawScanline, result), 0, resultLength); + Buffer r = this.EncodePixelRow(pixels, y); + deflateStream.Write(r.Array, 0, resultLength); - Swap(ref rawScanline, ref previousScanline); + Swap(ref this.rawScanline, ref this.previousScanline); } } diff --git a/src/ImageSharp/Formats/Png/PngEncoderOptions.cs b/src/ImageSharp/Formats/Png/PngEncoderOptions.cs index 2891f1974e..90175c6d65 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderOptions.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderOptions.cs @@ -60,7 +60,7 @@ namespace ImageSharp.Formats /// /// Gets or sets the transparency threshold. /// - public byte Threshold { get; set; } = 0; + public byte Threshold { get; set; } = 255; /// /// Gets or sets a value indicating whether this instance should write diff --git a/src/ImageSharp/Formats/Png/PngHeader.cs b/src/ImageSharp/Formats/Png/PngHeader.cs index f1d332c044..50d6cc9eca 100644 --- a/src/ImageSharp/Formats/Png/PngHeader.cs +++ b/src/ImageSharp/Formats/Png/PngHeader.cs @@ -34,7 +34,7 @@ namespace ImageSharp.Formats /// image data. Color type codes represent sums of the following values: /// 1 (palette used), 2 (color used), and 4 (alpha channel used). /// - public byte ColorType { get; set; } + public PngColorType ColorType { get; set; } /// /// Gets or sets the compression method. diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs index 2deb7dcf07..c1f04fa981 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs @@ -40,7 +40,7 @@ namespace ImageSharp.Formats /// /// The stream responsible for compressing the input stream. /// - private DeflateStream deflateStream; + private System.IO.Compression.DeflateStream deflateStream; /// /// Initializes a new instance of the class. @@ -102,7 +102,7 @@ namespace ImageSharp.Formats level = CompressionLevel.NoCompression; } - this.deflateStream = new DeflateStream(this.rawStream, level, true); + this.deflateStream = new System.IO.Compression.DeflateStream(this.rawStream, level, true); } /// diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs index 977a4a167c..0743d8ded3 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs @@ -1,23 +1,25 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Formats +namespace ImageSharp.Formats { using System; + using System.Collections.Generic; using System.IO; using System.IO.Compression; + using System.Text; /// - /// Provides methods and properties for decompressing streams by using the Zlib Deflate algorithm. + /// Provides methods and properties for deframing streams from PNGs. /// - internal sealed class ZlibInflateStream : Stream + internal class ZlibInflateStream : Stream { /// - /// The raw stream containing the uncompressed image data. + /// The inner raw memory stream + /// + private readonly Stream innerStream; + + /// + /// The compressed stream sitting over the top of the deframer /// - private readonly Stream rawStream; + private DeflateStream compressedStream; /// /// A value indicating whether this instance of the given entity has been disposed. @@ -38,123 +40,76 @@ namespace ImageSharp.Formats private byte[] crcread; /// - /// The stream responsible for decompressing the input stream. + /// The current data remaining to be read /// - private DeflateStream deflateStream; + private int currentDataRemaining; /// /// Initializes a new instance of the class. /// - /// The stream. - /// - /// Thrown if the compression method is incorrect. - /// - public ZlibInflateStream(Stream stream) + /// The inner raw stream + public ZlibInflateStream(Stream innerStream) { - // The DICT dictionary identifier identifying the used dictionary. - - // The preset dictionary. - bool fdict; - this.rawStream = stream; - - // Read the zlib header : http://tools.ietf.org/html/rfc1950 - // CMF(Compression Method and flags) - // This byte is divided into a 4 - bit compression method and a - // 4-bit information field depending on the compression method. - // bits 0 to 3 CM Compression method - // bits 4 to 7 CINFO Compression info - // - // 0 1 - // +---+---+ - // |CMF|FLG| - // +---+---+ - int cmf = this.rawStream.ReadByte(); - int flag = this.rawStream.ReadByte(); - if (cmf == -1 || flag == -1) - { - return; - } - - if ((cmf & 0x0f) != 8) - { - throw new Exception($"Bad compression method for ZLIB header: cmf={cmf}"); - } - - // CINFO is the base-2 logarithm of the LZ77 window size, minus eight. - // int cinfo = ((cmf & (0xf0)) >> 8); - fdict = (flag & 32) != 0; - - if (fdict) - { - // The DICT dictionary identifier identifying the used dictionary. - byte[] dictId = new byte[4]; - - for (int i = 0; i < 4; i++) - { - // We consume but don't use this. - dictId[i] = (byte)this.rawStream.ReadByte(); - } - } - - // Initialize the deflate Stream. - this.deflateStream = new DeflateStream(this.rawStream, CompressionMode.Decompress, true); + this.innerStream = innerStream; } /// - public override bool CanRead => true; + public override bool CanRead => this.innerStream.CanRead; /// public override bool CanSeek => false; /// - public override bool CanWrite => false; + public override bool CanWrite => throw new NotSupportedException(); /// - public override long Length + public override long Length => throw new NotSupportedException(); + + /// + public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + /// + /// Gets the compressed stream over the deframed inner stream + /// + public DeflateStream CompressedStream => this.compressedStream; + + /// + /// Adds new bytes from a frame found in the original stream + /// + /// blabla + public void AllocateNewBytes(int bytes) { - get + this.currentDataRemaining = bytes; + if (this.compressedStream == null) { - throw new NotSupportedException(); + this.InitializeInflateStream(); } } /// - public override long Position + public override void Flush() { - get - { - throw new NotSupportedException(); - } - - set - { - throw new NotSupportedException(); - } + throw new NotSupportedException(); } /// - public override void Flush() + public override int ReadByte() { - this.deflateStream?.Flush(); + this.currentDataRemaining--; + return this.innerStream.ReadByte(); } /// public override int Read(byte[] buffer, int offset, int count) { - // We dont't check CRC on reading - int read = this.deflateStream.Read(buffer, offset, count); - if (read < 1 && this.crcread == null) + if (this.currentDataRemaining == 0) { - // The deflater has ended. We try to read the next 4 bytes from raw stream (crc) - this.crcread = new byte[4]; - for (int i = 0; i < 4; i++) - { - // we dont really check/use this - this.crcread[i] = (byte)this.rawStream.ReadByte(); - } + return 0; } - return read; + int bytesToRead = Math.Min(count, this.currentDataRemaining); + this.currentDataRemaining -= bytesToRead; + return this.innerStream.Read(buffer, offset, bytesToRead); } /// @@ -186,10 +141,10 @@ namespace ImageSharp.Formats if (disposing) { // dispose managed resources - if (this.deflateStream != null) + if (this.compressedStream != null) { - this.deflateStream.Dispose(); - this.deflateStream = null; + this.compressedStream.Dispose(); + this.compressedStream = null; if (this.crcread == null) { @@ -197,7 +152,7 @@ namespace ImageSharp.Formats this.crcread = new byte[4]; for (int i = 0; i < 4; i++) { - this.crcread[i] = (byte)this.rawStream.ReadByte(); + this.crcread[i] = (byte)this.innerStream.ReadByte(); } } } @@ -210,5 +165,57 @@ namespace ImageSharp.Formats // Note disposing is done. this.isDisposed = true; } + + private void InitializeInflateStream() + { + // The DICT dictionary identifier identifying the used dictionary. + + // The preset dictionary. + bool fdict; + + // Read the zlib header : http://tools.ietf.org/html/rfc1950 + // CMF(Compression Method and flags) + // This byte is divided into a 4 - bit compression method and a + // 4-bit information field depending on the compression method. + // bits 0 to 3 CM Compression method + // bits 4 to 7 CINFO Compression info + // + // 0 1 + // +---+---+ + // |CMF|FLG| + // +---+---+ + int cmf = this.innerStream.ReadByte(); + int flag = this.innerStream.ReadByte(); + this.currentDataRemaining -= 2; + if (cmf == -1 || flag == -1) + { + return; + } + + if ((cmf & 0x0f) != 8) + { + throw new Exception($"Bad compression method for ZLIB header: cmf={cmf}"); + } + + // CINFO is the base-2 logarithm of the LZ77 window size, minus eight. + // int cinfo = ((cmf & (0xf0)) >> 8); + fdict = (flag & 32) != 0; + + if (fdict) + { + // The DICT dictionary identifier identifying the used dictionary. + byte[] dictId = new byte[4]; + + for (int i = 0; i < 4; i++) + { + // We consume but don't use this. + dictId[i] = (byte)this.innerStream.ReadByte(); + this.currentDataRemaining--; + } + } + + // Initialize the deflate Stream. + this.compressedStream = new DeflateStream(this, CompressionMode.Decompress, true); + } } } diff --git a/src/ImageSharp/GraphicsOptions.cs b/src/ImageSharp/GraphicsOptions.cs new file mode 100644 index 0000000000..f45582e1e6 --- /dev/null +++ b/src/ImageSharp/GraphicsOptions.cs @@ -0,0 +1,80 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using ImageSharp.PixelFormats; + + /// + /// Options for influencing the drawing functions. + /// + public struct GraphicsOptions + { + /// + /// Represents the default . + /// + public static readonly GraphicsOptions Default = new GraphicsOptions(true); + + private float? blendPercentage; + + private int? antialiasSubpixelDepth; + + private bool? antialias; + + private PixelBlenderMode blenderMode; + + /// + /// Initializes a new instance of the struct. + /// + /// If set to true [enable antialiasing]. + public GraphicsOptions(bool enableAntialiasing) + { + this.blenderMode = PixelBlenderMode.Normal; + this.blendPercentage = 1; + this.antialiasSubpixelDepth = 16; + this.antialias = enableAntialiasing; + } + + /// + /// Gets or sets a value indicating whether antialiasing should be applied. + /// + public bool Antialias + { + get => this.antialias ?? true; + set => this.antialias = value; + } + + /// + /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. + /// + public int AntialiasSubpixelDepth + { + get => this.antialiasSubpixelDepth ?? 16; + set => this.antialiasSubpixelDepth = value; + } + + /// + /// Gets or sets a value indicating the blending percentage to apply to the drawing operation + /// + public float BlendPercentage + { + get => (this.blendPercentage ?? 1).Clamp(0, 1); + set => this.blendPercentage = value; + } + + // In the future we could expose a PixelBlender directly on here + // or some forms of PixelBlender factory for each pixel type. Will need + // some API thought post V1. + + /// + /// Gets or sets a value indicating the blending percentage to apply to the drawing operation + /// + public PixelBlenderMode BlenderMode + { + get => this.blenderMode; + set => this.blenderMode = value; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image.Create.cs b/src/ImageSharp/Image.Create.cs deleted file mode 100644 index fcecefd7b7..0000000000 --- a/src/ImageSharp/Image.Create.cs +++ /dev/null @@ -1,66 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Diagnostics; - using System.IO; - - using Formats; - - /// - /// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha - /// packed into a single unsigned integer value. - /// - public sealed partial class Image - { - /// - /// Create a new instance of the class - /// with the height and the width of the image. - /// - /// The pixel format. - /// The width of the image in pixels. - /// The height of the image in pixels. - /// The images matadata to preload. - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// - /// A new unless is in which case it returns - /// - internal static Image Create(int width, int height, ImageMetaData metadata, Configuration configuration) - where TColor : struct, IPixel - { - if (typeof(TColor) == typeof(Color)) - { - return new Image(width, height, metadata, configuration) as Image; - } - else - { - return new Image(width, height, metadata, configuration); - } - } - - /// - /// Create a new instance of the class - /// with the height and the width of the image. - /// - /// The pixel format. - /// The width of the image in pixels. - /// The height of the image in pixels. - /// - /// The configuration providing initialization code which allows extending the library. - /// - /// - /// A new unless is in which case it returns - /// - internal static Image Create(int width, int height, Configuration configuration) - where TColor : struct, IPixel - { - return Image.Create(width, height, null, configuration); - } - } -} diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs deleted file mode 100644 index b2f9854f22..0000000000 --- a/src/ImageSharp/Image.FromBytes.cs +++ /dev/null @@ -1,212 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.IO; - using Formats; - - /// - /// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha - /// packed into a single unsigned integer value. - /// - public sealed partial class Image - { - /// - /// Loads the image from the given byte array. - /// - /// The byte array containing image data. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// The image - public static Image Load(byte[] data) - { - return Load(null, data, null); - } - - /// - /// Loads the image from the given byte array. - /// - /// The byte array containing image data. - /// The options for the decoder. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// The image - public static Image Load(byte[] data, IDecoderOptions options) - { - return Load(null, data, options); - } - - /// - /// Loads the image from the given byte array. - /// - /// The config for the decoder. - /// The byte array containing image data. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// The image - public static Image Load(Configuration config, byte[] data) - { - return Load(config, data, null); - } - - /// - /// Loads the image from the given byte array. - /// - /// The byte array containing image data. - /// The decoder. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// The image - public static Image Load(byte[] data, IImageDecoder decoder) - { - return Load(data, decoder, null); - } - - /// - /// Loads the image from the given byte array. - /// - /// The configuration options. - /// The byte array containing image data. - /// The options for the decoder. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// The image - public static Image Load(Configuration config, byte[] data, IDecoderOptions options) - { - using (MemoryStream ms = new MemoryStream(data)) - { - return Load(config, ms, options); - } - } - - /// - /// Loads the image from the given byte array. - /// - /// The byte array containing image data. - /// The decoder. - /// The options for the decoder. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// The image - public static Image Load(byte[] data, IImageDecoder decoder, IDecoderOptions options) - { - using (MemoryStream ms = new MemoryStream(data)) - { - return Load(ms, decoder, options); - } - } - - /// - /// Loads the image from the given byte array. - /// - /// The pixel format. - /// The byte array containing image data. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// The image - public static Image Load(byte[] data) - where TColor : struct, IPixel - { - return Load(null, data, null); - } - - /// - /// Loads the image from the given byte array. - /// - /// The pixel format. - /// The byte array containing image data. - /// The options for the decoder. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// The image - public static Image Load(byte[] data, IDecoderOptions options) - where TColor : struct, IPixel - { - return Load(null, data, options); - } - - /// - /// Loads the image from the given byte array. - /// - /// The pixel format. - /// The config for the decoder. - /// The byte array containing image data. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// The image - public static Image Load(Configuration config, byte[] data) - where TColor : struct, IPixel - { - return Load(config, data, null); - } - - /// - /// Loads the image from the given byte array. - /// - /// The pixel format. - /// The byte array containing image data. - /// The decoder. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// The image - public static Image Load(byte[] data, IImageDecoder decoder) - where TColor : struct, IPixel - { - return Load(data, decoder, null); - } - - /// - /// Loads the image from the given byte array. - /// - /// The pixel format. - /// The configuration options. - /// The byte array containing image data. - /// The options for the decoder. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// The image - public static Image Load(Configuration config, byte[] data, IDecoderOptions options) - where TColor : struct, IPixel - { - using (MemoryStream ms = new MemoryStream(data)) - { - return Load(config, ms, options); - } - } - - /// - /// Loads the image from the given byte array. - /// - /// The pixel format. - /// The byte array containing image data. - /// The decoder. - /// The options for the decoder. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// The image - public static Image Load(byte[] data, IImageDecoder decoder, IDecoderOptions options) - where TColor : struct, IPixel - { - using (MemoryStream ms = new MemoryStream(data)) - { - return Load(ms, decoder, options); - } - } - } -} diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs deleted file mode 100644 index 00688afc96..0000000000 --- a/src/ImageSharp/Image.cs +++ /dev/null @@ -1,72 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Diagnostics; - using System.IO; - - using Formats; - - /// - /// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha - /// packed into a single unsigned integer value. - /// - [DebuggerDisplay("Image: {Width}x{Height}")] - public sealed partial class Image : Image - { - /// - /// Initializes a new instance of the class - /// with the height and the width of the image. - /// - /// The width of the image in pixels. - /// The height of the image in pixels. - /// - /// The configuration providing initialization code which allows extending the library. - /// - public Image(int width, int height, Configuration configuration) - : base(width, height, configuration) - { - } - - /// - /// Initializes a new instance of the class - /// with the height and the width of the image. - /// - /// The width of the image in pixels. - /// The height of the image in pixels. - public Image(int width, int height) - : this(width, height, null) - { - } - - /// - /// Initializes a new instance of the class - /// by making a copy from another image. - /// - /// The other image, where the clone should be made from. - /// is null. - public Image(Image other) - : base(other) - { - } - - /// - /// Initializes a new instance of the class - /// with the height and the width of the image. - /// - /// The width of the image in pixels. - /// The height of the image in pixels. - /// The metadata. - /// - /// The configuration providing initialization code which allows extending the library. - /// - internal Image(int width, int height, ImageMetaData metadata, Configuration configuration) - : base(width, height, metadata, configuration) - { - } - } -} diff --git a/src/ImageSharp/Image/IImageBase{TColor}.cs b/src/ImageSharp/Image/IImageBase{TPixel}.cs similarity index 67% rename from src/ImageSharp/Image/IImageBase{TColor}.cs rename to src/ImageSharp/Image/IImageBase{TPixel}.cs index 14bdffc672..08d25709b3 100644 --- a/src/ImageSharp/Image/IImageBase{TColor}.cs +++ b/src/ImageSharp/Image/IImageBase{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -6,20 +6,21 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; /// /// Encapsulates the basic properties and methods required to manipulate images in varying formats. /// - /// The pixel format. - public interface IImageBase : IImageBase, IDisposable - where TColor : struct, IPixel + /// The pixel format. + public interface IImageBase : IImageBase, IDisposable + where TPixel : struct, IPixel { /// /// Gets the pixels as an array of the given packed pixel format. /// Important. Due to the nature in the way this is constructed do not rely on the length /// of the array for calculations. Use Width * Height. /// - TColor[] Pixels { get; } + TPixel[] Pixels { get; } /// /// Locks the image providing access to the pixels. @@ -27,7 +28,7 @@ namespace ImageSharp /// It is imperative that the accessor is correctly disposed off after use. /// /// - /// The - PixelAccessor Lock(); + /// The + PixelAccessor Lock(); } } \ No newline at end of file diff --git a/src/ImageSharp/Image/IImageProcessor.cs b/src/ImageSharp/Image/IImageProcessor.cs index 0440cdd766..c4fa9afa29 100644 --- a/src/ImageSharp/Image/IImageProcessor.cs +++ b/src/ImageSharp/Image/IImageProcessor.cs @@ -8,12 +8,14 @@ namespace ImageSharp.Processing using System; using System.Threading.Tasks; + using ImageSharp.PixelFormats; + /// /// Encapsulates methods to alter the pixels of an image. /// - /// The pixel format. - public interface IImageProcessor - where TColor : struct, IPixel + /// The pixel format. + public interface IImageProcessor + where TPixel : struct, IPixel { /// /// Gets or sets the parallel options for processing tasks in parallel. @@ -27,7 +29,7 @@ namespace ImageSharp.Processing bool Compand { get; set; } /// - /// Applies the process to the specified portion of the specified . + /// Applies the process to the specified portion of the specified . /// /// The source image. Cannot be null. /// @@ -39,6 +41,6 @@ namespace ImageSharp.Processing /// /// doesnt fit the dimension of the image. /// - void Apply(ImageBase source, Rectangle sourceRectangle); + void Apply(ImageBase source, Rectangle sourceRectangle); } } diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image/Image.Decode.cs similarity index 81% rename from src/ImageSharp/Image.Decode.cs rename to src/ImageSharp/Image/Image.Decode.cs index c1c1371220..c162f17726 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image/Image.Decode.cs @@ -10,11 +10,12 @@ namespace ImageSharp using System.Linq; using Formats; - /// - /// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha - /// packed into a single unsigned integer value. - /// - public sealed partial class Image + using ImageSharp.PixelFormats; + + /// + /// Adds static methods allowing the decoding of new images. + /// + public static partial class Image { /// /// By reading the header on the provided stream this calculates the images format. @@ -51,15 +52,15 @@ namespace ImageSharp /// /// Decodes the image stream to the current image. /// - /// The pixel format. /// The stream. /// The options for the decoder. /// the configuration. + /// The pixel format. /// - /// The decoded image + /// A new . /// - private static Image Decode(Stream stream, IDecoderOptions options, Configuration config) - where TColor : struct, IPixel + private static Image Decode(Stream stream, IDecoderOptions options, Configuration config) + where TPixel : struct, IPixel { IImageFormat format = DiscoverFormat(stream, config); if (format == null) @@ -67,9 +68,9 @@ namespace ImageSharp return null; } - Image img = format.Decoder.Decode(config, stream, options); + Image img = format.Decoder.Decode(config, stream, options); img.CurrentImageFormat = format; return img; } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Image/Image.FromBytes.cs b/src/ImageSharp/Image/Image.FromBytes.cs new file mode 100644 index 0000000000..c7309c4b14 --- /dev/null +++ b/src/ImageSharp/Image/Image.FromBytes.cs @@ -0,0 +1,152 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System.IO; + using Formats; + + using ImageSharp.PixelFormats; + + /// + /// Adds static methods allowing the creation of new image from a byte array. + /// + public static partial class Image + { + /// + /// Create a new instance of the class from the given byte array. + /// + /// The byte array containing image data. + /// A new . + public static Image Load(byte[] data) => Load(null, data, null); + + /// + /// Create a new instance of the class from the given byte array. + /// + /// The byte array containing image data. + /// The options for the decoder. + /// A new . + public static Image Load(byte[] data, IDecoderOptions options) => Load(null, data, options); + + /// + /// Create a new instance of the class from the given byte array. + /// + /// The config for the decoder. + /// The byte array containing image data. + /// A new . + public static Image Load(Configuration config, byte[] data) => Load(config, data, null); + + /// + /// Create a new instance of the class from the given byte array. + /// + /// The byte array containing image data. + /// The decoder. + /// A new . + public static Image Load(byte[] data, IImageDecoder decoder) => Load(data, decoder, null); + + /// + /// Create a new instance of the class from the given byte array. + /// + /// The configuration options. + /// The byte array containing image data. + /// The options for the decoder. + /// A new . + public static Image Load(Configuration config, byte[] data, IDecoderOptions options) => Load(config, data, options); + + /// + /// Create a new instance of the class from the given byte array. + /// + /// The byte array containing image data. + /// The decoder. + /// The options for the decoder. + /// A new . + public static Image Load(byte[] data, IImageDecoder decoder, IDecoderOptions options) => Load(data, decoder, options); + + /// + /// Create a new instance of the class from the given byte array. + /// + /// The byte array containing image data. + /// The pixel format. + /// A new . + public static Image Load(byte[] data) + where TPixel : struct, IPixel + { + return Load(null, data, null); + } + + /// + /// Create a new instance of the class from the given byte array. + /// + /// The byte array containing image data. + /// The options for the decoder. + /// The pixel format. + /// A new . + public static Image Load(byte[] data, IDecoderOptions options) + where TPixel : struct, IPixel + { + return Load(null, data, options); + } + + /// + /// Create a new instance of the class from the given byte array. + /// + /// The config for the decoder. + /// The byte array containing image data. + /// The pixel format. + /// A new . + public static Image Load(Configuration config, byte[] data) + where TPixel : struct, IPixel + { + return Load(config, data, null); + } + + /// + /// Create a new instance of the class from the given byte array. + /// + /// The byte array containing image data. + /// The decoder. + /// The pixel format. + /// A new . + public static Image Load(byte[] data, IImageDecoder decoder) + where TPixel : struct, IPixel + { + return Load(data, decoder, null); + } + + /// + /// Create a new instance of the class from the given byte array. + /// + /// The configuration options. + /// The byte array containing image data. + /// The options for the decoder. + /// The pixel format. + /// A new . + public static Image Load(Configuration config, byte[] data, IDecoderOptions options) + where TPixel : struct, IPixel + { + using (MemoryStream ms = new MemoryStream(data)) + { + return Load(config, ms, options); + } + } + + /// + /// Create a new instance of the class from the given byte array. + /// + /// The byte array containing image data. + /// The decoder. + /// The options for the decoder. + /// The pixel format. + /// A new . + public static Image Load(byte[] data, IImageDecoder decoder, IDecoderOptions options) + where TPixel : struct, IPixel + { + using (MemoryStream ms = new MemoryStream(data)) + { + return Load(ms, decoder, options); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image/Image.FromFile.cs similarity index 53% rename from src/ImageSharp/Image.FromFile.cs rename to src/ImageSharp/Image/Image.FromFile.cs index 40cdfe3eff..a135c43f5e 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image/Image.FromFile.cs @@ -9,206 +9,184 @@ namespace ImageSharp using System; using System.IO; using Formats; + using ImageSharp.PixelFormats; - /// - /// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha - /// packed into a single unsigned integer value. - /// - public sealed partial class Image + /// + /// Adds static methods allowing the creation of new image from a given file. + /// + public static partial class Image { /// - /// Loads the image from the given file. + /// Create a new instance of the class from the given file. /// /// The file path to the image. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(string path) - { - return Load(null, path, null); - } + /// A new . + public static Image Load(string path) => Load(path); /// - /// Loads the image from the given file. + /// Create a new instance of the class from the given file. /// /// The file path to the image. /// The options for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(string path, IDecoderOptions options) - { - return Load(null, path, options); - } + /// A new . + public static Image Load(string path, IDecoderOptions options) => Load(path, options); /// - /// Loads the image from the given file. + /// Create a new instance of the class from the given file. /// /// The config for the decoder. /// The file path to the image. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(Configuration config, string path) - { - return Load(config, path, null); - } + /// A new . + public static Image Load(Configuration config, string path) => Load(config, path); /// - /// Loads the image from the given file. + /// Create a new instance of the class from the given file. /// /// The file path to the image. /// The decoder. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(string path, IImageDecoder decoder) - { - return Load(path, decoder, null); - } + /// A new . + public static Image Load(string path, IImageDecoder decoder) => Load(path, decoder); /// - /// Loads the image from the given file. + /// Create a new instance of the class from the given file. /// + /// The configuration options. /// The file path to the image. - /// The decoder. /// The options for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(string path, IImageDecoder decoder, IDecoderOptions options) - { - return new Image(Load(path, decoder, options)); - } + /// A new . + public static Image Load(Configuration config, string path, IDecoderOptions options) => Load(config, path, options); /// - /// Loads the image from the given file. + /// Create a new instance of the class from the given file. /// - /// The configuration options. /// The file path to the image. + /// The decoder. /// The options for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(Configuration config, string path, IDecoderOptions options) - { - config = config ?? Configuration.Default; - using (Stream s = config.FileSystem.OpenRead(path)) - { - return Load(config, s, options); - } - } + /// A new . + public static Image Load(string path, IImageDecoder decoder, IDecoderOptions options) => Load(path, decoder, options); /// - /// Loads the image from the given file. + /// Create a new instance of the class from the given file. /// - /// The pixel format. /// The file path to the image. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(string path) - where TColor : struct, IPixel + /// The pixel format. + /// A new . + public static Image Load(string path) + where TPixel : struct, IPixel { - return Load(null, path, null); + return Load(null, path, null); } /// - /// Loads the image from the given file. + /// Create a new instance of the class from the given file. /// - /// The pixel format. /// The file path to the image. /// The options for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(string path, IDecoderOptions options) - where TColor : struct, IPixel + /// The pixel format. + /// A new . + public static Image Load(string path, IDecoderOptions options) + where TPixel : struct, IPixel { - return Load(null, path, options); + return Load(null, path, options); } /// - /// Loads the image from the given file. + /// Create a new instance of the class from the given file. /// - /// The pixel format. /// The config for the decoder. /// The file path to the image. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(Configuration config, string path) - where TColor : struct, IPixel + /// The pixel format. + /// A new . + public static Image Load(Configuration config, string path) + where TPixel : struct, IPixel { - return Load(config, path, null); + return Load(config, path, null); } /// - /// Loads the image from the given file. + /// Create a new instance of the class from the given file. /// - /// The pixel format. /// The file path to the image. /// The decoder. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(string path, IImageDecoder decoder) - where TColor : struct, IPixel + /// The pixel format. + /// A new . + public static Image Load(string path, IImageDecoder decoder) + where TPixel : struct, IPixel { - return Load(path, decoder, null); + return Load(path, decoder, null); } /// - /// Loads the image from the given file. + /// Create a new instance of the class from the given file. /// - /// The pixel format. /// The configuration options. /// The file path to the image. /// The options for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(Configuration config, string path, IDecoderOptions options) - where TColor : struct, IPixel + /// The pixel format. + /// A new . + public static Image Load(Configuration config, string path, IDecoderOptions options) + where TPixel : struct, IPixel { config = config ?? Configuration.Default; using (Stream s = config.FileSystem.OpenRead(path)) { - return Load(config, s, options); + return Load(config, s, options); } } /// - /// Loads the image from the given file. + /// Create a new instance of the class from the given file. /// - /// The pixel format. /// The file path to the image. /// The decoder. /// The options for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(string path, IImageDecoder decoder, IDecoderOptions options) - where TColor : struct, IPixel + /// The pixel format. + /// A new . + public static Image Load(string path, IImageDecoder decoder, IDecoderOptions options) + where TPixel : struct, IPixel { Configuration config = Configuration.Default; using (Stream s = config.FileSystem.OpenRead(path)) { - return Load(s, decoder, options); + return Load(s, decoder, options); } } } #endif -} +} \ No newline at end of file diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image/Image.FromStream.cs similarity index 53% rename from src/ImageSharp/Image.FromStream.cs rename to src/ImageSharp/Image/Image.FromStream.cs index 41ac7757e7..1bcb5adc9a 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image/Image.FromStream.cs @@ -10,86 +10,58 @@ namespace ImageSharp using System.Text; using Formats; - /// - /// Represents an image. Each pixel is a made up four 8-bit components red, green, blue, and alpha - /// packed into a single unsigned integer value. - /// - public sealed partial class Image + using ImageSharp.PixelFormats; + + /// + /// Adds static methods allowing the creation of new image from a given stream. + /// + public static partial class Image { /// - /// Loads the image from the given stream. + /// Create a new instance of the class from the given stream. /// /// The stream containing image information. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(Stream stream) - { - return Load(null, stream, null); - } + /// A new .> + public static Image Load(Stream stream) => Load(stream); /// - /// Loads the image from the given stream. + /// Create a new instance of the class from the given stream. /// /// The stream containing image information. /// The options for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(Stream stream, IDecoderOptions options) - { - return Load(null, stream, options); - } - - /// - /// Loads the image from the given stream. - /// - /// The config for the decoder. - /// The stream containing image information. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// The image - public static Image Load(Configuration config, Stream stream) - { - return Load(config, stream, null); - } + /// A new .> + public static Image Load(Stream stream, IDecoderOptions options) => Load(stream, options); /// - /// Loads the image from the given stream. + /// Create a new instance of the class from the given stream. /// /// The stream containing image information. /// The decoder. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(Stream stream, IImageDecoder decoder) - { - return Load(stream, decoder, null); - } + /// A new .> + public static Image Load(Stream stream, IImageDecoder decoder) => Load(stream, decoder); /// - /// Loads the image from the given stream. + /// Create a new instance of the class from the given stream. /// - /// The configuration options. + /// The config for the decoder. /// The stream containing image information. - /// The options for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(Configuration config, Stream stream, IDecoderOptions options) - { - Image image = Load(config, stream, options); - - return image as Image ?? new Image(image); - } + /// A new .> + public static Image Load(Configuration config, Stream stream) => Load(config, stream); /// - /// Loads the image from the given stream. + /// Create a new instance of the class from the given stream. /// /// The stream containing image information. /// The decoder. @@ -97,111 +69,105 @@ namespace ImageSharp /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(Stream stream, IImageDecoder decoder, IDecoderOptions options) - { - Image image = new Image(Load(stream, decoder, options)); - - return image as Image ?? new Image(image); - } + /// A new .> + public static Image Load(Stream stream, IImageDecoder decoder, IDecoderOptions options) => Load(stream, decoder, options); /// - /// Loads the image from the given stream. + /// Create a new instance of the class from the given stream. /// - /// The pixel format. /// The stream containing image information. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(Stream stream) - where TColor : struct, IPixel + /// The pixel format. + /// A new .> + public static Image Load(Stream stream) + where TPixel : struct, IPixel { - return Load(null, stream, null); + return Load(null, stream, null); } /// - /// Loads the image from the given stream. + /// Create a new instance of the class from the given stream. /// - /// The pixel format. /// The stream containing image information. /// The options for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(Stream stream, IDecoderOptions options) - where TColor : struct, IPixel + /// The pixel format. + /// A new .> + public static Image Load(Stream stream, IDecoderOptions options) + where TPixel : struct, IPixel { - return Load(null, stream, options); + return Load(null, stream, options); } /// - /// Loads the image from the given stream. + /// Create a new instance of the class from the given stream. /// - /// The pixel format. /// The config for the decoder. /// The stream containing image information. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(Configuration config, Stream stream) - where TColor : struct, IPixel + /// The pixel format. + /// A new .> + public static Image Load(Configuration config, Stream stream) + where TPixel : struct, IPixel { - return Load(config, stream, null); + return Load(config, stream, null); } /// - /// Loads the image from the given stream. + /// Create a new instance of the class from the given stream. /// - /// The pixel format. /// The stream containing image information. /// The decoder. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(Stream stream, IImageDecoder decoder) - where TColor : struct, IPixel + /// The pixel format. + /// A new .> + public static Image Load(Stream stream, IImageDecoder decoder) + where TPixel : struct, IPixel { - return Load(stream, decoder, null); + return Load(stream, decoder, null); } /// - /// Loads the image from the given stream. + /// Create a new instance of the class from the given stream. /// - /// The pixel format. /// The stream containing image information. /// The decoder. /// The options for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(Stream stream, IImageDecoder decoder, IDecoderOptions options) - where TColor : struct, IPixel + /// The pixel format. + /// A new .> + public static Image Load(Stream stream, IImageDecoder decoder, IDecoderOptions options) + where TPixel : struct, IPixel { - return WithSeekableStream(stream, s => decoder.Decode(Configuration.Default, s, options)); + return WithSeekableStream(stream, s => decoder.Decode(Configuration.Default, s, options)); } /// - /// Loads the image from the given stream. + /// Create a new instance of the class from the given stream. /// - /// The pixel format. /// The configuration options. /// The stream containing image information. /// The options for the decoder. /// /// Thrown if the stream is not readable nor seekable. /// - /// The image - public static Image Load(Configuration config, Stream stream, IDecoderOptions options) - where TColor : struct, IPixel + /// The pixel format. + /// A new .> + public static Image Load(Configuration config, Stream stream, IDecoderOptions options) + where TPixel : struct, IPixel { config = config ?? Configuration.Default; - - Image img = WithSeekableStream(stream, s => Decode(stream, options, config)); + Image img = WithSeekableStream(stream, s => Decode(s, options, config)); if (img != null) { @@ -230,17 +196,15 @@ namespace ImageSharp { return action(stream); } - else + + // We want to be able to load images from things like HttpContext.Request.Body + using (MemoryStream ms = new MemoryStream()) { - // We want to be able to load images from things like HttpContext.Request.Body - using (MemoryStream ms = new MemoryStream()) - { - stream.CopyTo(ms); - ms.Position = 0; - - return action(stream); - } + stream.CopyTo(ms); + ms.Position = 0; + + return action(ms); } } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TPixel}.cs similarity index 85% rename from src/ImageSharp/Image/ImageBase{TColor}.cs rename to src/ImageSharp/Image/ImageBase{TPixel}.cs index cfce7184b6..4fd9d26cbf 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -7,16 +7,19 @@ namespace ImageSharp { using System; using System.Diagnostics; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; using Processing; /// /// The base class of all images. Encapsulates the basic properties and methods required to manipulate /// images in different pixel formats. /// - /// The pixel format. + /// The pixel format. [DebuggerDisplay("Image: {Width}x{Height}")] - public abstract class ImageBase : IImageBase - where TColor : struct, IPixel + public abstract class ImageBase : IImageBase + where TPixel : struct, IPixel { /// /// Gets or sets the maximum allowable width in pixels. @@ -31,7 +34,7 @@ namespace ImageSharp /// /// The image pixels /// - private TColor[] pixelBuffer; + private TPixel[] pixelBuffer; /// /// A value indicating whether this instance of the given entity has been disposed. @@ -45,7 +48,7 @@ namespace ImageSharp private bool isDisposed; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The configuration providing initialization code which allows extending the library. @@ -56,17 +59,17 @@ namespace ImageSharp } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The width of the image in pixels. - /// The height of the image in pixels. /// /// The configuration providing initialization code which allows extending the library. /// + /// The width of the image in pixels. + /// The height of the image in pixels. /// /// Thrown if either or are less than or equal to 0. /// - protected ImageBase(int width, int height, Configuration configuration) + protected ImageBase(Configuration configuration, int width, int height) : this(configuration) { Guard.MustBeGreaterThan(width, 0, nameof(width)); @@ -79,15 +82,15 @@ namespace ImageSharp } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// - /// The other to create this instance from. + /// The other to create this instance from. /// /// - /// Thrown if the given is null. + /// Thrown if the given is null. /// - protected ImageBase(ImageBase other) + protected ImageBase(ImageBase other) : this(other.Configuration) { Guard.NotNull(other, nameof(other), "Other image cannot be null."); @@ -98,8 +101,8 @@ namespace ImageSharp // Rent then copy the pixels. Unsafe.CopyBlock gives us a nice speed boost here. this.RentPixels(); - using (PixelAccessor sourcePixels = other.Lock()) - using (PixelAccessor target = this.Lock()) + using (PixelAccessor sourcePixels = other.Lock()) + using (PixelAccessor target = this.Lock()) { // Check we can do this without crashing sourcePixels.CopyTo(target); @@ -107,7 +110,7 @@ namespace ImageSharp } /// - public TColor[] Pixels => this.pixelBuffer; + public TPixel[] Pixels => this.pixelBuffer; /// public int Width { get; private set; } @@ -131,7 +134,7 @@ namespace ImageSharp /// /// The processor. /// The rectangle. - public virtual void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + public virtual void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { processor.Apply(this, rectangle); } @@ -150,16 +153,16 @@ namespace ImageSharp } /// - public PixelAccessor Lock() + public PixelAccessor Lock() { - return new PixelAccessor(this); + return new PixelAccessor(this); } /// /// Switches the buffers used by the image and the PixelAccessor meaning that the Image will "own" the buffer from the PixelAccessor and the PixelAccessor will now own the Images buffer. /// /// The pixel source. - internal void SwapPixelsBuffers(PixelAccessor pixelSource) + internal void SwapPixelsBuffers(PixelAccessor pixelSource) { Guard.NotNull(pixelSource, nameof(pixelSource)); @@ -167,7 +170,7 @@ namespace ImageSharp int newHeight = pixelSource.Height; // Push my memory into the accessor (which in turn unpins the old puffer ready for the images use) - TColor[] newPixels = pixelSource.ReturnCurrentPixelsAndReplaceThemInternally(this.Width, this.Height, this.pixelBuffer); + TPixel[] newPixels = pixelSource.ReturnCurrentColorsAndReplaceThemInternally(this.Width, this.Height, this.pixelBuffer); this.Width = newWidth; this.Height = newHeight; this.pixelBuffer = newPixels; @@ -221,7 +224,7 @@ namespace ImageSharp /// private void RentPixels() { - this.pixelBuffer = PixelDataPool.Rent(this.Width * this.Height); + this.pixelBuffer = PixelDataPool.Rent(this.Width * this.Height); } /// @@ -229,7 +232,7 @@ namespace ImageSharp /// private void ReturnPixels() { - PixelDataPool.Return(this.pixelBuffer); + PixelDataPool.Return(this.pixelBuffer); this.pixelBuffer = null; } diff --git a/src/ImageSharp/Image/ImageFrame{TColor}.cs b/src/ImageSharp/Image/ImageFrame{TPixel}.cs similarity index 67% rename from src/ImageSharp/Image/ImageFrame{TColor}.cs rename to src/ImageSharp/Image/ImageFrame{TPixel}.cs index 2712dc6878..04b9902c67 100644 --- a/src/ImageSharp/Image/ImageFrame{TColor}.cs +++ b/src/ImageSharp/Image/ImageFrame{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -8,16 +8,17 @@ namespace ImageSharp using System; using System.Numerics; using System.Threading.Tasks; + using ImageSharp.PixelFormats; /// /// Represents a single frame in a animation. /// - /// The pixel format. - public class ImageFrame : ImageBase, IImageFrame - where TColor : struct, IPixel + /// The pixel format. + public class ImageFrame : ImageBase, IImageFrame + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The width of the image in pixels. /// The height of the image in pixels. @@ -25,15 +26,25 @@ namespace ImageSharp /// The configuration providing initialization code which allows extending the library. /// public ImageFrame(int width, int height, Configuration configuration = null) - : base(width, height, configuration) + : base(configuration, width, height) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The image to create the frame from. - public ImageFrame(ImageBase image) + public ImageFrame(ImageFrame image) + : base(image) + { + this.CopyProperties(image); + } + + /// + /// Initializes a new instance of the class. + /// + /// The image to create the frame from. + public ImageFrame(ImageBase image) : base(image) { } @@ -53,18 +64,18 @@ namespace ImageSharp /// Returns a copy of the image frame in the given pixel format. /// /// A function that allows for the correction of vector scaling between unknown color formats. - /// The pixel format. - /// The - public ImageFrame To(Func scaleFunc = null) - where TColor2 : struct, IPixel + /// The pixel format. + /// The + public ImageFrame To(Func scaleFunc = null) + where TPixel2 : struct, IPixel { - scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(scaleFunc); + scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(scaleFunc); - ImageFrame target = new ImageFrame(this.Width, this.Height, this.Configuration); + ImageFrame target = new ImageFrame(this.Width, this.Height, this.Configuration); target.CopyProperties(this); - using (PixelAccessor pixels = this.Lock()) - using (PixelAccessor targetPixels = target.Lock()) + using (PixelAccessor pixels = this.Lock()) + using (PixelAccessor targetPixels = target.Lock()) { Parallel.For( 0, @@ -74,7 +85,7 @@ namespace ImageSharp { for (int x = 0; x < target.Width; x++) { - TColor2 color = default(TColor2); + TPixel2 color = default(TPixel2); color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4())); targetPixels[x, y] = color; } @@ -87,10 +98,10 @@ namespace ImageSharp /// /// Clones the current instance. /// - /// The - internal virtual ImageFrame Clone() + /// The + internal virtual ImageFrame Clone() { - return new ImageFrame(this); + return new ImageFrame(this); } /// diff --git a/src/ImageSharp/Image/ImageProcessingExtensions.cs b/src/ImageSharp/Image/ImageProcessingExtensions.cs index ff3ecd5eef..c9577ac15f 100644 --- a/src/ImageSharp/Image/ImageProcessingExtensions.cs +++ b/src/ImageSharp/Image/ImageProcessingExtensions.cs @@ -5,11 +5,12 @@ namespace ImageSharp { - using System; + using ImageSharp.PixelFormats; + using Processing; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { @@ -17,12 +18,12 @@ namespace ImageSharp /// Applies the processor to the image. /// This method does not resize the target image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The processor to apply to the image. - /// The . - public static Image Apply(this Image source, IImageProcessor processor) - where TColor : struct, IPixel + /// The . + public static Image Apply(this Image source, IImageProcessor processor) + where TPixel : struct, IPixel { source.ApplyProcessor(processor, source.Bounds); return source; diff --git a/src/ImageSharp/Image/Image{TColor}.cs b/src/ImageSharp/Image/Image{TPixel}.cs similarity index 80% rename from src/ImageSharp/Image/Image{TColor}.cs rename to src/ImageSharp/Image/Image{TPixel}.cs index d063c3ff16..092706e416 100644 --- a/src/ImageSharp/Image/Image{TColor}.cs +++ b/src/ImageSharp/Image/Image{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -6,65 +6,64 @@ namespace ImageSharp { using System; - using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Numerics; - using System.Text; using System.Threading.Tasks; using Formats; + using ImageSharp.PixelFormats; using Processing; /// /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. /// - /// The pixel format. + /// The pixel format. [DebuggerDisplay("Image: {Width}x{Height}")] - public class Image : ImageBase, IImage - where TColor : struct, IPixel + public class Image : ImageBase, IImage + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class /// with the height and the width of the image. /// - /// The width of the image in pixels. - /// The height of the image in pixels. /// /// The configuration providing initialization code which allows extending the library. /// - public Image(int width, int height, Configuration configuration) - : this(width, height, new ImageMetaData(), configuration) + /// The width of the image in pixels. + /// The height of the image in pixels. + public Image(Configuration configuration, int width, int height) + : this(configuration, width, height, new ImageMetaData()) { } /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class /// with the height and the width of the image. /// /// The width of the image in pixels. /// The height of the image in pixels. public Image(int width, int height) - : this(width, height, null) + : this(null, width, height) { } /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class /// by making a copy from another image. /// /// The other image, where the clone should be made from. /// is null. - public Image(Image other) + public Image(Image other) : base(other) { - foreach (ImageFrame frame in other.Frames) + foreach (ImageFrame frame in other.Frames) { if (frame != null) { - this.Frames.Add(new ImageFrame(frame)); + this.Frames.Add(new ImageFrame(frame)); } } @@ -72,29 +71,28 @@ namespace ImageSharp } /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class /// by making a copy from another image. /// /// The other image, where the clone should be made from. /// is null. - public Image(ImageBase other) + public Image(ImageBase other) : base(other) { - this.MetaData = new ImageMetaData(); } /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class /// with the height and the width of the image. /// - /// The width of the image in pixels. - /// The height of the image in pixels. - /// The images metadata. /// /// The configuration providing initialization code which allows extending the library. /// - internal Image(int width, int height, ImageMetaData metadata, Configuration configuration) - : base(width, height, configuration) + /// The width of the image in pixels. + /// The height of the image in pixels. + /// The images metadata. + internal Image(Configuration configuration, int width, int height, ImageMetaData metadata) + : base(configuration, width, height) { if (!this.Configuration.ImageFormats.Any()) { @@ -108,7 +106,7 @@ namespace ImageSharp /// /// Gets the meta data of the image. /// - public ImageMetaData MetaData { get; private set; } + public ImageMetaData MetaData { get; private set; } = new ImageMetaData(); /// /// Gets the width of the image in inches. It is calculated as the width of the image @@ -138,7 +136,7 @@ namespace ImageSharp /// Gets the other frames for the animation. /// /// The list of frame images. - public IList> Frames { get; } = new List>(); + public IList> Frames { get; } = new List>(); /// /// Gets the currently loaded image format. @@ -150,11 +148,11 @@ namespace ImageSharp /// /// The processor to apply to the image. /// The structure that specifies the portion of the image object to draw. - public override void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + public override void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { // we want to put this on on here as it gives us a really go place to test/verify processor settings base.ApplyProcessor(processor, rectangle); - foreach (ImageFrame sourceFrame in this.Frames) + foreach (ImageFrame sourceFrame in this.Frames) { sourceFrame.ApplyProcessor(processor, rectangle); } @@ -165,8 +163,8 @@ namespace ImageSharp /// /// The stream to save the image to. /// Thrown if the stream is null. - /// The - public Image Save(Stream stream) + /// The + public Image Save(Stream stream) { return this.Save(stream, (IEncoderOptions)null); } @@ -177,8 +175,8 @@ namespace ImageSharp /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. - /// The - public Image Save(Stream stream, IEncoderOptions options) + /// The + public Image Save(Stream stream, IEncoderOptions options) { return this.Save(stream, this.CurrentImageFormat?.Encoder, options); } @@ -188,8 +186,8 @@ namespace ImageSharp /// /// The stream to save the image to. /// The format to save the image as. - /// The - public Image Save(Stream stream, IImageFormat format) + /// The + public Image Save(Stream stream, IImageFormat format) { return this.Save(stream, format, null); } @@ -200,8 +198,8 @@ namespace ImageSharp /// The stream to save the image to. /// The format to save the image as. /// The options for the encoder. - /// The - public Image Save(Stream stream, IImageFormat format, IEncoderOptions options) + /// The + public Image Save(Stream stream, IImageFormat format, IEncoderOptions options) { Guard.NotNull(format, nameof(format)); @@ -215,9 +213,9 @@ namespace ImageSharp /// The encoder to save the image with. /// Thrown if the stream or encoder is null. /// - /// The . + /// The . /// - public Image Save(Stream stream, IImageEncoder encoder) + public Image Save(Stream stream, IImageEncoder encoder) { return this.Save(stream, encoder, null); } @@ -230,9 +228,9 @@ namespace ImageSharp /// The options for the encoder. /// Thrown if the stream or encoder is null. /// - /// The . + /// The . /// - public Image Save(Stream stream, IImageEncoder encoder, IEncoderOptions options) + public Image Save(Stream stream, IImageEncoder encoder, IEncoderOptions options) { Guard.NotNull(stream, nameof(stream)); Guard.NotNull(encoder, nameof(encoder)); @@ -248,8 +246,8 @@ namespace ImageSharp /// /// The file path to save the image to. /// Thrown if the stream is null. - /// The - public Image Save(string filePath) + /// The + public Image Save(string filePath) { return this.Save(filePath, (IEncoderOptions)null); } @@ -260,8 +258,8 @@ namespace ImageSharp /// The file path to save the image to. /// The options for the encoder. /// Thrown if the stream is null. - /// The - public Image Save(string filePath, IEncoderOptions options) + /// The + public Image Save(string filePath, IEncoderOptions options) { string ext = Path.GetExtension(filePath).Trim('.'); IImageFormat format = this.Configuration.ImageFormats.SingleOrDefault(f => f.SupportedExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase)); @@ -279,8 +277,8 @@ namespace ImageSharp /// The file path to save the image to. /// The format to save the image as. /// Thrown if the format is null. - /// The - public Image Save(string filePath, IImageFormat format) + /// The + public Image Save(string filePath, IImageFormat format) { return this.Save(filePath, format, null); } @@ -292,8 +290,8 @@ namespace ImageSharp /// The format to save the image as. /// The options for the encoder. /// Thrown if the format is null. - /// The - public Image Save(string filePath, IImageFormat format, IEncoderOptions options) + /// The + public Image Save(string filePath, IImageFormat format, IEncoderOptions options) { Guard.NotNull(format, nameof(format)); return this.Save(filePath, format.Encoder, options); @@ -305,8 +303,8 @@ namespace ImageSharp /// The file path to save the image to. /// The encoder to save the image with. /// Thrown if the encoder is null. - /// The - public Image Save(string filePath, IImageEncoder encoder) + /// The + public Image Save(string filePath, IImageEncoder encoder) { return this.Save(filePath, encoder, null); } @@ -318,8 +316,8 @@ namespace ImageSharp /// The encoder to save the image with. /// The options for the encoder. /// Thrown if the encoder is null. - /// The - public Image Save(string filePath, IImageEncoder encoder, IEncoderOptions options) + /// The + public Image Save(string filePath, IImageEncoder encoder, IEncoderOptions options) { Guard.NotNull(encoder, nameof(encoder)); using (Stream fs = this.Configuration.FileSystem.Create(filePath)) @@ -354,18 +352,18 @@ namespace ImageSharp /// Returns a copy of the image in the given pixel format. /// /// A function that allows for the correction of vector scaling between unknown color formats. - /// The pixel format. - /// The - public Image To(Func scaleFunc = null) - where TColor2 : struct, IPixel + /// The pixel format. + /// The + public Image To(Func scaleFunc = null) + where TPixel2 : struct, IPixel { - scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(scaleFunc); + scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction(scaleFunc); - Image target = new Image(this.Width, this.Height, this.Configuration); + Image target = new Image(this.Configuration, this.Width, this.Height); target.CopyProperties(this); - using (PixelAccessor pixels = this.Lock()) - using (PixelAccessor targetPixels = target.Lock()) + using (PixelAccessor pixels = this.Lock()) + using (PixelAccessor targetPixels = target.Lock()) { Parallel.For( 0, @@ -375,7 +373,7 @@ namespace ImageSharp { for (int x = 0; x < target.Width; x++) { - TColor2 color = default(TColor2); + TPixel2 color = default(TPixel2); color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4())); targetPixels[x, y] = color; } @@ -384,19 +382,19 @@ namespace ImageSharp for (int i = 0; i < this.Frames.Count; i++) { - target.Frames.Add(this.Frames[i].To()); + target.Frames.Add(this.Frames[i].To()); } return target; } /// - /// Creates a new from this instance + /// Creates a new from this instance /// - /// The - internal virtual ImageFrame ToFrame() + /// The + internal virtual ImageFrame ToFrame() { - return new ImageFrame(this); + return new ImageFrame(this); } /// diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs similarity index 80% rename from src/ImageSharp/Image/PixelAccessor{TColor}.cs rename to src/ImageSharp/Image/PixelAccessor{TPixel}.cs index f5393cfb38..a54c03b635 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -8,21 +8,18 @@ namespace ImageSharp using System; using System.Diagnostics; using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; using System.Threading.Tasks; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// - /// Provides per-pixel access to generic pixels. + /// Provides per-pixel access to generic pixels. /// - /// The pixel format. - public sealed unsafe class PixelAccessor : IDisposable, IPinnedImageBuffer - where TColor : struct, IPixel + /// The pixel format. + public sealed class PixelAccessor : IDisposable, IBuffer2D + where TPixel : struct, IPixel { - /// - /// The position of the first pixel in the image. - /// - private byte* pixelsBase; - /// /// A value indicating whether this instance of the given entity has been disposed. /// @@ -35,15 +32,15 @@ namespace ImageSharp private bool isDisposed; /// - /// The containing the pixel data. + /// The containing the pixel data. /// - private PinnedImageBuffer pixelBuffer; + private Buffer2D pixelBuffer; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The image to provide pixel access for. - public PixelAccessor(ImageBase image) + public PixelAccessor(ImageBase image) { Guard.NotNull(image, nameof(image)); Guard.MustBeGreaterThan(image.Width, 0, "image width"); @@ -54,22 +51,22 @@ namespace ImageSharp } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. public PixelAccessor(int width, int height) - : this(width, height, PinnedImageBuffer.CreateClean(width, height)) + : this(width, height, Buffer2D.CreateClean(width, height)) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. /// The pixel buffer. - private PixelAccessor(int width, int height, PinnedImageBuffer pixels) + private PixelAccessor(int width, int height, Buffer2D pixels) { Guard.NotNull(pixels, nameof(pixels)); Guard.MustBeGreaterThan(width, 0, nameof(width)); @@ -81,7 +78,7 @@ namespace ImageSharp } /// - /// Finalizes an instance of the class. + /// Finalizes an instance of the class. /// ~PixelAccessor() { @@ -91,12 +88,7 @@ namespace ImageSharp /// /// Gets the pixel buffer array. /// - public TColor[] PixelArray => this.pixelBuffer.Array; - - /// - /// Gets the pointer to the pixel buffer. - /// - public IntPtr DataPointer => this.pixelBuffer.Pointer; + public TPixel[] PixelArray => this.pixelBuffer.Array; /// /// Gets the size of a single pixel in the number of bytes. @@ -124,30 +116,28 @@ namespace ImageSharp public ParallelOptions ParallelOptions { get; } /// - BufferSpan IPinnedImageBuffer.Span => this.pixelBuffer; + Span IBuffer2D.Span => this.pixelBuffer; - private static BulkPixelOperations Operations => BulkPixelOperations.Instance; + private static PixelOperations Operations => PixelOperations.Instance; /// /// Gets or sets the pixel at the specified position. /// /// The x-coordinate of the pixel. Must be greater than or equal to zero and less than the width of the image. /// The y-coordinate of the pixel. Must be greater than or equal to zero and less than the height of the image. - /// The at the specified position. - public TColor this[int x, int y] + /// The at the specified position. + public TPixel this[int x, int y] { get { this.CheckCoordinates(x, y); - - return Unsafe.Read(this.pixelsBase + (((y * this.Width) + x) * Unsafe.SizeOf())); + return this.PixelArray[(y * this.Width) + x]; } set { this.CheckCoordinates(x, y); - - Unsafe.Write(this.pixelsBase + (((y * this.Width) + x) * Unsafe.SizeOf()), value); + this.PixelArray[(y * this.Width) + x] = value; } } @@ -179,7 +169,7 @@ namespace ImageSharp /// public void Reset() { - Unsafe.InitBlock(this.pixelsBase, 0, (uint)(this.RowStride * this.Height)); + this.pixelBuffer.Clear(); } /// @@ -191,7 +181,7 @@ namespace ImageSharp /// /// Thrown when an unsupported component order value is passed. /// - internal void CopyFrom(PixelArea area, int targetY, int targetX = 0) + internal void CopyFrom(PixelArea area, int targetY, int targetX = 0) { this.CheckCoordinates(area, targetX, targetY); @@ -207,7 +197,7 @@ namespace ImageSharp /// /// Thrown when an unsupported component order value is passed. /// - internal void CopyTo(PixelArea area, int sourceY, int sourceX = 0) + internal void CopyTo(PixelArea area, int sourceY, int sourceX = 0) { this.CheckCoordinates(area, sourceX, sourceY); @@ -224,7 +214,7 @@ namespace ImageSharp /// /// Thrown when an unsupported component order value is passed. /// - internal void SafeCopyTo(PixelArea area, int sourceY, int sourceX = 0) + internal void SafeCopyTo(PixelArea area, int sourceY, int sourceX = 0) { int width = Math.Min(area.Width, this.Width - sourceX); if (width < 1) @@ -249,22 +239,20 @@ namespace ImageSharp /// The pixels. /// Returns the old pixel data thats has gust been replaced. /// If is true then caller is responsible for ensuring is called. - internal TColor[] ReturnCurrentPixelsAndReplaceThemInternally(int width, int height, TColor[] pixels) + internal TPixel[] ReturnCurrentColorsAndReplaceThemInternally(int width, int height, TPixel[] pixels) { - TColor[] oldPixels = this.pixelBuffer.UnPinAndTakeArrayOwnership(); + TPixel[] oldPixels = this.pixelBuffer.TakeArrayOwnership(); this.SetPixelBufferUnsafe(width, height, pixels); return oldPixels; } /// - /// Copies the pixels to another of the same size. + /// Copies the pixels to another of the same size. /// /// The target pixel buffer accessor. - internal void CopyTo(PixelAccessor target) + internal void CopyTo(PixelAccessor target) { - uint byteCount = (uint)(this.Width * this.Height * Unsafe.SizeOf()); - - Unsafe.CopyBlock(target.pixelsBase, this.pixelsBase, byteCount); + SpanHelper.Copy(this.pixelBuffer.Span, target.pixelBuffer.Span); } /// @@ -276,12 +264,12 @@ namespace ImageSharp /// The width. /// The height. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyFromZyx(PixelArea area, int targetX, int targetY, int width, int height) + private void CopyFromZyx(PixelArea area, int targetX, int targetY, int width, int height) { for (int y = 0; y < height; y++) { - BufferSpan source = area.GetRowSpan(y); - BufferSpan destination = this.GetRowSpan(targetX, targetY + y); + Span source = area.GetRowSpan(y); + Span destination = this.GetRowSpan(targetX, targetY + y); Operations.PackFromZyxBytes(source, destination, width); } @@ -296,12 +284,12 @@ namespace ImageSharp /// The width. /// The height. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyFromZyxw(PixelArea area, int targetX, int targetY, int width, int height) + private void CopyFromZyxw(PixelArea area, int targetX, int targetY, int width, int height) { for (int y = 0; y < height; y++) { - BufferSpan source = area.GetRowSpan(y); - BufferSpan destination = this.GetRowSpan(targetX, targetY + y); + Span source = area.GetRowSpan(y); + Span destination = this.GetRowSpan(targetX, targetY + y); Operations.PackFromZyxwBytes(source, destination, width); } @@ -316,12 +304,12 @@ namespace ImageSharp /// The width. /// The height. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyFromXyz(PixelArea area, int targetX, int targetY, int width, int height) + private void CopyFromXyz(PixelArea area, int targetX, int targetY, int width, int height) { for (int y = 0; y < height; y++) { - BufferSpan source = area.GetRowSpan(y); - BufferSpan destination = this.GetRowSpan(targetX, targetY + y); + Span source = area.GetRowSpan(y); + Span destination = this.GetRowSpan(targetX, targetY + y); Operations.PackFromXyzBytes(source, destination, width); } @@ -336,12 +324,12 @@ namespace ImageSharp /// The width. /// The height. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyFromXyzw(PixelArea area, int targetX, int targetY, int width, int height) + private void CopyFromXyzw(PixelArea area, int targetX, int targetY, int width, int height) { for (int y = 0; y < height; y++) { - BufferSpan source = area.GetRowSpan(y); - BufferSpan destination = this.GetRowSpan(targetX, targetY + y); + Span source = area.GetRowSpan(y); + Span destination = this.GetRowSpan(targetX, targetY + y); Operations.PackFromXyzwBytes(source, destination, width); } } @@ -355,12 +343,12 @@ namespace ImageSharp /// The width. /// The height. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyToZyx(PixelArea area, int sourceX, int sourceY, int width, int height) + private void CopyToZyx(PixelArea area, int sourceX, int sourceY, int width, int height) { for (int y = 0; y < height; y++) { - BufferSpan source = this.GetRowSpan(sourceX, sourceY + y); - BufferSpan destination = area.GetRowSpan(y); + Span source = this.GetRowSpan(sourceX, sourceY + y); + Span destination = area.GetRowSpan(y); Operations.ToZyxBytes(source, destination, width); } } @@ -374,12 +362,12 @@ namespace ImageSharp /// The width. /// The height. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyToZyxw(PixelArea area, int sourceX, int sourceY, int width, int height) + private void CopyToZyxw(PixelArea area, int sourceX, int sourceY, int width, int height) { for (int y = 0; y < height; y++) { - BufferSpan source = this.GetRowSpan(sourceX, sourceY + y); - BufferSpan destination = area.GetRowSpan(y); + Span source = this.GetRowSpan(sourceX, sourceY + y); + Span destination = area.GetRowSpan(y); Operations.ToZyxwBytes(source, destination, width); } } @@ -393,12 +381,12 @@ namespace ImageSharp /// The width. /// The height. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyToXyz(PixelArea area, int sourceX, int sourceY, int width, int height) + private void CopyToXyz(PixelArea area, int sourceX, int sourceY, int width, int height) { for (int y = 0; y < height; y++) { - BufferSpan source = this.GetRowSpan(sourceX, sourceY + y); - BufferSpan destination = area.GetRowSpan(y); + Span source = this.GetRowSpan(sourceX, sourceY + y); + Span destination = area.GetRowSpan(y); Operations.ToXyzBytes(source, destination, width); } } @@ -412,19 +400,19 @@ namespace ImageSharp /// The width. /// The height. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CopyToXyzw(PixelArea area, int sourceX, int sourceY, int width, int height) + private void CopyToXyzw(PixelArea area, int sourceX, int sourceY, int width, int height) { for (int y = 0; y < height; y++) { - BufferSpan source = this.GetRowSpan(sourceX, sourceY + y); - BufferSpan destination = area.GetRowSpan(y); + Span source = this.GetRowSpan(sourceX, sourceY + y); + Span destination = area.GetRowSpan(y); Operations.ToXyzwBytes(source, destination, width); } } - private void SetPixelBufferUnsafe(int width, int height, TColor[] pixels) + private void SetPixelBufferUnsafe(int width, int height, TPixel[] pixels) { - this.SetPixelBufferUnsafe(width, height, new PinnedImageBuffer(pixels, width, height)); + this.SetPixelBufferUnsafe(width, height, new Buffer2D(pixels, width, height)); } /// @@ -433,14 +421,13 @@ namespace ImageSharp /// The width. /// The height. /// The pixel buffer - private void SetPixelBufferUnsafe(int width, int height, PinnedImageBuffer pixels) + private void SetPixelBufferUnsafe(int width, int height, Buffer2D pixels) { this.pixelBuffer = pixels; - this.pixelsBase = (byte*)pixels.Pointer; this.Width = width; this.Height = height; - this.PixelSize = Unsafe.SizeOf(); + this.PixelSize = Unsafe.SizeOf(); this.RowStride = this.Width * this.PixelSize; } @@ -455,7 +442,7 @@ namespace ImageSharp /// /// Thrown when an unsupported component order value is passed. /// - private void CopyFrom(PixelArea area, int targetX, int targetY, int width, int height) + private void CopyFrom(PixelArea area, int targetX, int targetY, int width, int height) { switch (area.ComponentOrder) { @@ -487,7 +474,7 @@ namespace ImageSharp /// /// Thrown when an unsupported component order value is passed. /// - private void CopyTo(PixelArea area, int sourceX, int sourceY, int width, int height) + private void CopyTo(PixelArea area, int sourceX, int sourceY, int width, int height) { switch (area.ComponentOrder) { @@ -518,7 +505,7 @@ namespace ImageSharp /// Thrown if the dimensions are not within the bounds of the image. /// [Conditional("DEBUG")] - private void CheckCoordinates(PixelArea area, int x, int y) + private void CheckCoordinates(PixelArea area, int x, int y) { int width = Math.Min(area.Width, this.Width - x); if (width < 1) diff --git a/src/ImageSharp/Image/PixelArea{TColor}.cs b/src/ImageSharp/Image/PixelArea{TPixel}.cs similarity index 86% rename from src/ImageSharp/Image/PixelArea{TColor}.cs rename to src/ImageSharp/Image/PixelArea{TPixel}.cs index bd10c9b6b0..4ddfcadb7b 100644 --- a/src/ImageSharp/Image/PixelArea{TColor}.cs +++ b/src/ImageSharp/Image/PixelArea{TPixel}.cs @@ -1,20 +1,23 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // + namespace ImageSharp { using System; using System.Diagnostics; using System.IO; - using System.Runtime.CompilerServices; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; /// - /// Represents an area of generic pixels. + /// Represents an area of generic pixels. /// - /// The pixel format. - internal sealed unsafe class PixelArea : IDisposable - where TColor : struct, IPixel + /// The pixel format. + internal sealed class PixelArea : IDisposable + where TPixel : struct, IPixel { /// /// A value indicating whether this instance of the given entity has been disposed. @@ -30,10 +33,10 @@ namespace ImageSharp /// /// The underlying buffer containing the raw pixel data. /// - private PinnedBuffer byteBuffer; + private Buffer byteBuffer; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The width. /// The bytes. @@ -47,7 +50,7 @@ namespace ImageSharp } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The width. /// The height. @@ -66,12 +69,11 @@ namespace ImageSharp this.RowStride = width * GetComponentCount(componentOrder); this.Length = bytes.Length; // TODO: Is this the right value for Length? - this.byteBuffer = new PinnedBuffer(bytes); - this.PixelBase = (byte*)this.byteBuffer.Pointer; + this.byteBuffer = new Buffer(bytes); } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The width. /// The component order. @@ -81,7 +83,7 @@ namespace ImageSharp } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The width. /// The component order. @@ -92,7 +94,7 @@ namespace ImageSharp } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The width. /// The height. @@ -103,7 +105,7 @@ namespace ImageSharp } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The width. /// The height. @@ -117,8 +119,7 @@ namespace ImageSharp this.RowStride = (width * GetComponentCount(componentOrder)) + padding; this.Length = this.RowStride * height; - this.byteBuffer = new PinnedBuffer(this.Length); - this.PixelBase = (byte*)this.byteBuffer.Pointer; + this.byteBuffer = new Buffer(this.Length); } /// @@ -136,21 +137,11 @@ namespace ImageSharp /// public ComponentOrder ComponentOrder { get; } - /// - /// Gets the pointer to the pixel buffer. - /// - public IntPtr DataPointer => this.byteBuffer.Pointer; - /// /// Gets the height. /// public int Height { get; } - /// - /// Gets the data pointer. - /// - public byte* PixelBase { get; private set; } - /// /// Gets the width of one row in the number of bytes. /// @@ -198,15 +189,15 @@ namespace ImageSharp /// public void Reset() { - Unsafe.InitBlock(this.PixelBase, 0, (uint)(this.RowStride * this.Height)); + this.byteBuffer.Clear(); } /// - /// Gets a to the row y. + /// Gets a to the row y. /// /// The y coordinate - /// The - internal BufferSpan GetRowSpan(int y) + /// The + internal Span GetRowSpan(int y) { return this.byteBuffer.Slice(y * this.RowStride); } diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index c51c0833a0..6194be1bf4 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -2,7 +2,7 @@ A cross-platform library for the processing of image files; written in C# ImageSharp - 1.0.0-alpha5 + 1.0.0-alpha9 James Jackson-South and contributors netstandard1.3;netstandard1.1 true @@ -38,9 +38,11 @@ All + - + + ..\..\ImageSharp.ruleset diff --git a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs b/src/ImageSharp/Memory/Buffer.cs similarity index 62% rename from src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs rename to src/ImageSharp/Memory/Buffer.cs index 611688c995..a894ea53a4 100644 --- a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs +++ b/src/ImageSharp/Memory/Buffer.cs @@ -1,23 +1,27 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.Memory { using System; - using System.Buffers; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; /// - /// Manages a pinned buffer of value type objects as a Disposable resource. + /// Manages a buffer of value type objects as a Disposable resource. /// The backing array is either pooled or comes from the outside. /// /// The value type. - internal class PinnedBuffer : IDisposable + internal class Buffer : IDisposable where T : struct { + /// + /// A pointer to the first element of when pinned. + /// + private IntPtr pointer; + /// /// A handle that allows to access the managed as an unmanaged memory by pinning. /// @@ -25,40 +29,38 @@ namespace ImageSharp /// /// A value indicating wheter should be returned to - /// when disposing this instance. + /// when disposing this instance. /// private bool isPoolingOwner; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The desired count of elements. (Minimum size for ) - public PinnedBuffer(int length) + public Buffer(int length) { this.Length = length; this.Array = PixelDataPool.Rent(length); this.isPoolingOwner = true; - this.Pin(); } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The array to pin. - public PinnedBuffer(T[] array) + public Buffer(T[] array) { this.Length = array.Length; this.Array = array; this.isPoolingOwner = false; - this.Pin(); } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The array to pin. /// The count of "relevant" elements in 'array'. - public PinnedBuffer(T[] array, int length) + public Buffer(T[] array, int length) { if (array.Length < length) { @@ -68,19 +70,18 @@ namespace ImageSharp this.Length = length; this.Array = array; this.isPoolingOwner = false; - this.Pin(); } /// - /// Finalizes an instance of the class. + /// Finalizes an instance of the class. /// - ~PinnedBuffer() + ~Buffer() { this.UnPin(); } /// - /// Gets a value indicating whether this instance is disposed, or has lost ownership of . + /// Gets a value indicating whether this instance is disposed, or has lost ownership of . /// public bool IsDisposedOrLostArrayOwnership { get; private set; } @@ -95,80 +96,73 @@ namespace ImageSharp public T[] Array { get; private set; } /// - /// Gets a pointer to the pinned . + /// Gets a to the backing buffer. /// - public IntPtr Pointer { get; private set; } - - /// - /// Gets a to the backing buffer. - /// - public BufferSpan Span => this; + public Span Span => this; /// /// Returns a reference to specified element of the buffer. /// /// The index /// The reference to the specified element - public unsafe ref T this[int index] + public ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { DebugGuard.MustBeLessThan(index, this.Length, nameof(index)); - - byte* ptr = (byte*)this.Pointer + BufferSpan.SizeOf(index); - return ref Unsafe.AsRef(ptr); + return ref this.Array[index]; } } /// - /// Converts to an . + /// Converts to an . /// - /// The to convert. + /// The to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe implicit operator BufferSpan(PinnedBuffer buffer) + public static implicit operator Span(Buffer buffer) { - return new BufferSpan(buffer.Array, (void*)buffer.Pointer, 0, buffer.Length); + return new Span(buffer.Array, 0, buffer.Length); } /// - /// Creates a clean instance of initializing it's elements with 'default(T)'. + /// Creates a clean instance of initializing it's elements with 'default(T)'. /// /// The desired count of elements. (Minimum size for ) - /// The instance + /// The instance [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static PinnedBuffer CreateClean(int count) + public static Buffer CreateClean(int count) { - PinnedBuffer buffer = new PinnedBuffer(count); + Buffer buffer = new Buffer(count); buffer.Clear(); return buffer; } /// - /// Gets a to an offseted position inside the buffer. + /// Gets a to an offseted position inside the buffer. /// /// The start - /// The + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe BufferSpan Slice(int start) + public Span Slice(int start) { - return new BufferSpan(this.Array, (void*)this.Pointer, start, this.Length - start); + return new Span(this.Array, start, this.Length - start); } /// - /// Gets a to an offseted position inside the buffer. + /// Gets a to an offsetted position inside the buffer. /// /// The start /// The length of the slice - /// The + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe BufferSpan Slice(int start, int length) + public Span Slice(int start, int length) { - return new BufferSpan(this.Array, (void*)this.Pointer, start, length); + return new Span(this.Array, start, length); } /// - /// Disposes the instance by unpinning the array, and returning the pooled buffer when necessary. + /// Disposes the instance by unpinning the array, and returning the pooled buffer when necessary. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() @@ -199,11 +193,11 @@ namespace ImageSharp /// /// The unpinned [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T[] UnPinAndTakeArrayOwnership() + public T[] TakeArrayOwnership() { if (this.IsDisposedOrLostArrayOwnership) { - throw new InvalidOperationException("UnPinAndTakeArrayOwnership() is invalid: either PinnedBuffer is disposed or UnPinAndTakeArrayOwnership() has been called multiple times!"); + throw new InvalidOperationException("TakeArrayOwnership() is invalid: either Buffer is disposed or TakeArrayOwnership() has been called multiple times!"); } this.IsDisposedOrLostArrayOwnership = true; @@ -215,22 +209,34 @@ namespace ImageSharp } /// - /// Clears the buffer, filling elements between 0 and -1 with default(T) + /// Clears the contents of this buffer. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { - ((BufferSpan)this).Clear(); + this.Span.Clear(); } /// /// Pins . /// + /// The pinned pointer [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void Pin() + public IntPtr Pin() { - this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned); - this.Pointer = this.handle.AddrOfPinnedObject(); + if (this.IsDisposedOrLostArrayOwnership) + { + throw new InvalidOperationException( + "Pin() is invalid on a buffer with IsDisposedOrLostArrayOwnership == true!"); + } + + if (this.pointer == IntPtr.Zero) + { + this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned); + this.pointer = this.handle.AddrOfPinnedObject(); + } + + return this.pointer; } /// @@ -239,13 +245,13 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] private void UnPin() { - if (this.Pointer == IntPtr.Zero || !this.handle.IsAllocated) + if (this.pointer == IntPtr.Zero || !this.handle.IsAllocated) { return; } this.handle.Free(); - this.Pointer = IntPtr.Zero; + this.pointer = IntPtr.Zero; } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/PinnedImageBuffer{T}.cs b/src/ImageSharp/Memory/Buffer2D.cs similarity index 68% rename from src/ImageSharp/Common/Memory/PinnedImageBuffer{T}.cs rename to src/ImageSharp/Memory/Buffer2D.cs index 3ff174c5dd..e5ccfbd193 100644 --- a/src/ImageSharp/Common/Memory/PinnedImageBuffer{T}.cs +++ b/src/ImageSharp/Memory/Buffer2D.cs @@ -1,27 +1,26 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.Memory { - using System; using System.Runtime.CompilerServices; /// - /// Represents a pinned buffer of value type objects + /// Represents a buffer of value type objects /// interpreted as a 2D region of x elements. /// /// The value type. - internal class PinnedImageBuffer : PinnedBuffer, IPinnedImageBuffer + internal class Buffer2D : Buffer, IBuffer2D where T : struct { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of elements in a row /// The number of rows - public PinnedImageBuffer(int width, int height) + public Buffer2D(int width, int height) : base(width * height) { this.Width = width; @@ -29,12 +28,12 @@ namespace ImageSharp } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The array to pin /// The number of elements in a row /// The number of rows - public PinnedImageBuffer(T[] array, int width, int height) + public Buffer2D(T[] array, int width, int height) : base(array, width * height) { this.Width = width; @@ -63,14 +62,14 @@ namespace ImageSharp } /// - /// Creates a clean instance of initializing it's elements with 'default(T)'. + /// Creates a clean instance of initializing it's elements with 'default(T)'. /// /// The number of elements in a row /// The number of rows - /// The instance - public static PinnedImageBuffer CreateClean(int width, int height) + /// The instance + public static Buffer2D CreateClean(int width, int height) { - PinnedImageBuffer buffer = new PinnedImageBuffer(width, height); + Buffer2D buffer = new Buffer2D(width, height); buffer.Clear(); return buffer; } diff --git a/src/ImageSharp/Common/Memory/PinnedImageBufferExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs similarity index 59% rename from src/ImageSharp/Common/Memory/PinnedImageBufferExtensions.cs rename to src/ImageSharp/Memory/Buffer2DExtensions.cs index fcd5b3831e..51e5582815 100644 --- a/src/ImageSharp/Common/Memory/PinnedImageBufferExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -1,42 +1,42 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.Memory { using System; using System.Runtime.CompilerServices; /// - /// Defines extension methods for . + /// Defines extension methods for . /// - internal static class PinnedImageBufferExtensions + internal static class Buffer2DExtensions { /// - /// Gets a to the row 'y' beginning from the pixel at 'x'. + /// Gets a to the row 'y' beginning from the pixel at 'x'. /// /// The buffer /// The x coordinate (position in the row) /// The y (row) coordinate /// The element type - /// The + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BufferSpan GetRowSpan(this IPinnedImageBuffer buffer, int x, int y) + public static Span GetRowSpan(this IBuffer2D buffer, int x, int y) where T : struct { return buffer.Span.Slice((y * buffer.Width) + x, buffer.Width - x); } /// - /// Gets a to the row 'y' beginning from the pixel at 'x'. + /// Gets a to the row 'y' beginning from the pixel at 'x'. /// /// The buffer /// The y (row) coordinate /// The element type - /// The + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static BufferSpan GetRowSpan(this IPinnedImageBuffer buffer, int y) + public static Span GetRowSpan(this IBuffer2D buffer, int y) where T : struct { return buffer.Span.Slice(y * buffer.Width, buffer.Width); diff --git a/src/ImageSharp/Common/Memory/Fast2DArray{T}.cs b/src/ImageSharp/Memory/Fast2DArray{T}.cs similarity index 96% rename from src/ImageSharp/Common/Memory/Fast2DArray{T}.cs rename to src/ImageSharp/Memory/Fast2DArray{T}.cs index 401c83ce61..260c829e21 100644 --- a/src/ImageSharp/Common/Memory/Fast2DArray{T}.cs +++ b/src/ImageSharp/Memory/Fast2DArray{T}.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.Memory { using System; using System.Diagnostics; @@ -94,11 +94,11 @@ namespace ImageSharp } /// - /// Performs an implicit conversion from a 2D array to a . + /// Performs an implicit conversion from a 2D array to a . /// /// The source array. /// - /// The represenation on the source data. + /// The represenation on the source data. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Fast2DArray(T[,] data) diff --git a/src/ImageSharp/Common/Memory/IPinnedImageBuffer{T}.cs b/src/ImageSharp/Memory/IBuffer2D.cs similarity index 72% rename from src/ImageSharp/Common/Memory/IPinnedImageBuffer{T}.cs rename to src/ImageSharp/Memory/IBuffer2D.cs index 374cbed997..300c29a1ba 100644 --- a/src/ImageSharp/Common/Memory/IPinnedImageBuffer{T}.cs +++ b/src/ImageSharp/Memory/IBuffer2D.cs @@ -1,16 +1,18 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.Memory { + using System; + /// /// An interface that represents a pinned buffer of value type objects /// interpreted as a 2D region of x elements. /// /// The value type. - internal interface IPinnedImageBuffer + internal interface IBuffer2D where T : struct { /// @@ -24,8 +26,8 @@ namespace ImageSharp int Height { get; } /// - /// Gets a to the backing buffer. + /// Gets a to the backing buffer. /// - BufferSpan Span { get; } + Span Span { get; } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs b/src/ImageSharp/Memory/PixelDataPool{T}.cs similarity index 94% rename from src/ImageSharp/Common/Memory/PixelDataPool{T}.cs rename to src/ImageSharp/Memory/PixelDataPool{T}.cs index f5c7871409..a8b5501cc4 100644 --- a/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs +++ b/src/ImageSharp/Memory/PixelDataPool{T}.cs @@ -3,11 +3,12 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.Memory { - using System; using System.Buffers; + using ImageSharp.PixelFormats; + /// /// Provides a resource pool that enables reusing instances of value type arrays for image data . /// @@ -24,7 +25,7 @@ namespace ImageSharp /// Rents the pixel array from the pool. /// /// The minimum length of the array to return. - /// The + /// The public static T[] Rent(int minimumLength) { return ArrayPool.Rent(minimumLength); diff --git a/src/ImageSharp/Memory/SpanHelper.cs b/src/ImageSharp/Memory/SpanHelper.cs new file mode 100644 index 0000000000..57b7715911 --- /dev/null +++ b/src/ImageSharp/Memory/SpanHelper.cs @@ -0,0 +1,97 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Memory +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// Utility methods for + /// + internal static class SpanHelper + { + /// + /// Fetches a from the beginning of the span. + /// + /// The value type + /// The span to fetch the vector from + /// A reference to the beginning of the span + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref Vector FetchVector(this Span span) + where T : struct + { + return ref Unsafe.As>(ref span.DangerousGetPinnableReference()); + } + + /// + /// Copy 'count' number of elements of the same type from 'source' to 'dest' + /// + /// The element type. + /// The to copy elements from. + /// The destination . + /// The number of elements to copy + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void Copy(Span source, Span destination, int count) + where T : struct + { + DebugGuard.MustBeLessThanOrEqualTo(count, source.Length, nameof(count)); + DebugGuard.MustBeLessThanOrEqualTo(count, destination.Length, nameof(count)); + + ref byte srcRef = ref Unsafe.As(ref source.DangerousGetPinnableReference()); + ref byte destRef = ref Unsafe.As(ref destination.DangerousGetPinnableReference()); + + int byteCount = Unsafe.SizeOf() * count; + + // TODO: Use unfixed Unsafe.CopyBlock(ref T, ref T, int) for small blocks, when it gets available! + // This is now available. Check with Anton re intent. Do we replace both ifdefs? + fixed (byte* pSrc = &srcRef) + fixed (byte* pDest = &destRef) + { +#if NETSTANDARD1_1 + Unsafe.CopyBlock(pDest, pSrc, (uint)byteCount); +#else + int destLength = destination.Length * Unsafe.SizeOf(); + Buffer.MemoryCopy(pSrc, pDest, destLength, byteCount); +#endif + } + } + + /// + /// Copy all elements of 'source' into 'destination'. + /// + /// The element type. + /// The to copy elements from. + /// The destination . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Copy(Span source, Span destination) + where T : struct + { + Copy(source, destination, source.Length); + } + + /// + /// Gets the size of `count` elements in bytes. + /// + /// The element type. + /// The count of the elements + /// The size in bytes as int + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SizeOf(int count) + where T : struct => Unsafe.SizeOf() * count; + + /// + /// Gets the size of `count` elements in bytes as UInt32 + /// + /// The element type. + /// The count of the elements + /// The size in bytes as UInt32 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint USizeOf(int count) + where T : struct + => (uint)SizeOf(count); + } +} \ No newline at end of file diff --git a/src/ImageSharp/MetaData/IMetaData.cs b/src/ImageSharp/MetaData/IMetaData.cs index 38fd313493..6daa04dd63 100644 --- a/src/ImageSharp/MetaData/IMetaData.cs +++ b/src/ImageSharp/MetaData/IMetaData.cs @@ -5,6 +5,8 @@ namespace ImageSharp { + using ImageSharp.Formats; + /// /// Encapsulates the metadata of an image frame. /// @@ -12,10 +14,17 @@ namespace ImageSharp { /// /// Gets or sets the frame delay for animated images. - /// If not 0, this field specifies the number of hundredths (1/100) of a second to + /// If not 0, when utilized in Gif animation, this field specifies the number of hundredths (1/100) of a second to /// wait before continuing with the processing of the Data Stream. /// The clock starts ticking immediately after the graphic is rendered. /// int FrameDelay { get; set; } + + /// + /// Gets or sets the disposal method for animated images. + /// Primarily used in Gif animation, this field indicates the way in which the graphic is to + /// be treated after being displayed. + /// + DisposalMethod DisposalMethod { get; set; } } } diff --git a/src/ImageSharp/MetaData/ImageFrameMetaData.cs b/src/ImageSharp/MetaData/ImageFrameMetaData.cs index 50119096e9..b55bfd1ad1 100644 --- a/src/ImageSharp/MetaData/ImageFrameMetaData.cs +++ b/src/ImageSharp/MetaData/ImageFrameMetaData.cs @@ -5,6 +5,8 @@ namespace ImageSharp { + using ImageSharp.Formats; + /// /// Encapsulates the metadata of an image frame. /// @@ -29,14 +31,13 @@ namespace ImageSharp DebugGuard.NotNull(other, nameof(other)); this.FrameDelay = other.FrameDelay; + this.DisposalMethod = other.DisposalMethod; } - /// - /// Gets or sets the frame delay for animated images. - /// If not 0, this field specifies the number of hundredths (1/100) of a second to - /// wait before continuing with the processing of the Data Stream. - /// The clock starts ticking immediately after the graphic is rendered. - /// + /// public int FrameDelay { get; set; } + + /// + public DisposalMethod DisposalMethod { get; set; } } } diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs index aed6efa2cb..127ae720f2 100644 --- a/src/ImageSharp/MetaData/ImageMetaData.cs +++ b/src/ImageSharp/MetaData/ImageMetaData.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Collections.Generic; + using ImageSharp.Formats; /// /// Encapsulates the metadata of an image. @@ -52,6 +53,7 @@ namespace ImageSharp this.VerticalResolution = other.VerticalResolution; this.Quality = other.Quality; this.FrameDelay = other.FrameDelay; + this.DisposalMethod = other.DisposalMethod; this.RepeatCount = other.RepeatCount; foreach (ImageProperty property in other.Properties) @@ -83,10 +85,10 @@ namespace ImageSharp set { - if (value > 0) - { - this.horizontalResolution = value; - } + if (value > 0) + { + this.horizontalResolution = value; + } } } @@ -116,14 +118,12 @@ namespace ImageSharp /// public ExifProfile ExifProfile { get; set; } - /// - /// Gets or sets the frame delay for animated images. - /// If not 0, this field specifies the number of hundredths (1/100) of a second to - /// wait before continuing with the processing of the Data Stream. - /// The clock starts ticking immediately after the graphic is rendered. - /// + /// public int FrameDelay { get; set; } + /// + public DisposalMethod DisposalMethod { get; set; } + /// /// Gets the list of properties for storing meta information about this image. /// diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs index c4a94c5ff1..a7fd8fd6a8 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs @@ -5,11 +5,12 @@ namespace ImageSharp { - using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; + using ImageSharp.PixelFormats; + /// /// Represents an EXIF profile providing access to the collection of values. /// @@ -116,12 +117,12 @@ namespace ImageSharp /// /// Returns the thumbnail in the EXIF profile when available. /// - /// The pixel format. + /// The pixel format. /// - /// The . + /// The . /// - public Image CreateThumbnail() - where TColor : struct, IPixel + public Image CreateThumbnail() + where TPixel : struct, IPixel { this.InitializeValues(); @@ -135,9 +136,9 @@ namespace ImageSharp return null; } - using (MemoryStream memStream = new MemoryStream(this.data, this.thumbnailOffset, this.thumbnailLength)) + using (var memStream = new MemoryStream(this.data, this.thumbnailOffset, this.thumbnailLength)) { - return Image.Load(memStream); + return Image.Load(memStream); } } @@ -200,7 +201,7 @@ namespace ImageSharp } } - ExifValue newExifValue = ExifValue.Create(tag, value); + var newExifValue = ExifValue.Create(tag, value); this.values.Add(newExifValue); } @@ -220,7 +221,7 @@ namespace ImageSharp return null; } - ExifWriter writer = new ExifWriter(this.values, this.Parts); + var writer = new ExifWriter(this.values, this.Parts); return writer.GetData(); } @@ -237,11 +238,18 @@ namespace ImageSharp private void SyncResolution(ExifTag tag, double resolution) { ExifValue value = this.GetValue(tag); - if (value != null) + if (value == null) + { + return; + } + + if (value.IsArray || value.DataType != ExifDataType.Rational) { - Rational newResolution = new Rational(resolution, false); - this.SetValue(tag, newResolution); + this.RemoveValue(value.Tag); } + + var newResolution = new Rational(resolution, false); + this.SetValue(tag, newResolution); } private void InitializeValues() @@ -257,7 +265,7 @@ namespace ImageSharp return; } - ExifReader reader = new ExifReader(); + var reader = new ExifReader(); this.values = reader.Read(this.data); this.invalidTags = new List(reader.InvalidTags); this.thumbnailOffset = (int)reader.ThumbnailOffset; diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs index 0b845c4914..53123bfc29 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs @@ -73,7 +73,9 @@ namespace ImageSharp /// public Collection Read(byte[] data) { - Collection result = new Collection(); + DebugGuard.NotNull(data, nameof(data)); + + var result = new Collection(); this.exifData = data; @@ -319,6 +321,13 @@ namespace ImageSharp uint numberOfComponents = this.GetLong(); + // Issue #132: ExifDataType == Undefined is treated like a byte array. + // If numberOfComponents == 0 this value can only be handled as an inline value and must fallback to 4 (bytes) + if (dataType == ExifDataType.Undefined && numberOfComponents == 0) + { + numberOfComponents = 4; + } + uint size = numberOfComponents * ExifValue.GetSize(dataType); byte[] data = this.GetBytes(4); @@ -348,7 +357,7 @@ namespace ImageSharp private TEnum ToEnum(int value, TEnum defaultValue) where TEnum : struct { - TEnum enumValue = (TEnum)(object)value; + var enumValue = (TEnum)(object)value; if (Enum.GetValues(typeof(TEnum)).Cast().Any(v => v.Equals(enumValue))) { return enumValue; @@ -383,12 +392,18 @@ namespace ImageSharp private string GetString(uint length) { - return ToString(this.GetBytes(length)); + byte[] data = this.GetBytes(length); + if (data == null || data.Length == 0) + { + return null; + } + + return ToString(data); } private void GetThumbnail(uint offset) { - Collection values = new Collection(); + var values = new Collection(); this.AddValues(values, offset); foreach (ExifValue value in values) diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs index 8cd05b53c2..a2965917b1 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs @@ -39,7 +39,7 @@ namespace ImageSharp } else { - Array array = (Array)other.exifValue; + var array = (Array)other.exifValue; this.exifValue = array.Clone(); } } @@ -264,7 +264,7 @@ namespace ImageSharp return this.ToString(this.exifValue); } - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); foreach (object value in (Array)this.exifValue) { sb.Append(this.ToString(value)); diff --git a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs b/src/ImageSharp/PixelFormats/Alpha8.cs similarity index 91% rename from src/ImageSharp/Colors/PackedPixel/Alpha8.cs rename to src/ImageSharp/PixelFormats/Alpha8.cs index 9a340544cf..c184ed9cf6 100644 --- a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs +++ b/src/ImageSharp/PixelFormats/Alpha8.cs @@ -3,14 +3,17 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; using System.Runtime.CompilerServices; /// - /// Packed pixel type containing a single 8 bit normalized W values ranging from 0 to 1. + /// Packed pixel type containing a single 8 bit normalized W values. + /// + /// Ranges from <0, 0, 0, 0> to <0, 0, 0, 1> in vector form. + /// /// public struct Alpha8 : IPixel, IPackedVector { @@ -59,7 +62,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -84,7 +87,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { bytes[startIndex] = 0; bytes[startIndex + 1] = 0; @@ -93,7 +96,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { bytes[startIndex] = 0; bytes[startIndex + 1] = 0; @@ -103,7 +106,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { bytes[startIndex] = 0; bytes[startIndex + 1] = 0; @@ -112,7 +115,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { bytes[startIndex] = 0; bytes[startIndex + 1] = 0; diff --git a/src/ImageSharp/Colors/PackedPixel/Argb.cs b/src/ImageSharp/PixelFormats/Argb32.cs similarity index 84% rename from src/ImageSharp/Colors/PackedPixel/Argb.cs rename to src/ImageSharp/PixelFormats/Argb32.cs index d03c098cdd..bd47f72f93 100644 --- a/src/ImageSharp/Colors/PackedPixel/Argb.cs +++ b/src/ImageSharp/PixelFormats/Argb32.cs @@ -1,9 +1,9 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -12,12 +12,15 @@ namespace ImageSharp /// /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. /// The color components are stored in alpha, red, green, and blue order. + /// + /// Ranges from <0, 0, 0, 0> to <1, 1, 1, 1> in vector form. + /// /// /// /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, /// as it avoids the need to create new values for modification operations. /// - public struct Argb : IPixel, IPackedVector + public struct Argb32 : IPixel, IPackedVector { /// /// The shift count for the blue component @@ -50,58 +53,58 @@ namespace ImageSharp private static readonly Vector4 Half = new Vector4(0.5F); /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The red component. /// The green component. /// The blue component. /// The alpha component. - public Argb(byte r, byte g, byte b, byte a = 255) + public Argb32(byte r, byte g, byte b, byte a = 255) { this.PackedValue = Pack(r, g, b, a); } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The red component. /// The green component. /// The blue component. /// The alpha component. - public Argb(float r, float g, float b, float a = 1) + public Argb32(float r, float g, float b, float a = 1) { this.PackedValue = Pack(r, g, b, a); } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// /// The vector containing the components for the packed vector. /// - public Argb(Vector3 vector) + public Argb32(Vector3 vector) { this.PackedValue = Pack(ref vector); } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// /// The vector containing the components for the packed vector. /// - public Argb(Vector4 vector) + public Argb32(Vector4 vector) { this.PackedValue = Pack(ref vector); } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// /// The packed value. /// - public Argb(uint packed = 0) + public Argb32(uint packed = 0) { this.PackedValue = packed; } @@ -182,33 +185,33 @@ namespace ImageSharp } /// - /// Compares two objects for equality. + /// Compares two objects for equality. /// /// - /// The on the left side of the operand. + /// The on the left side of the operand. /// /// - /// The on the right side of the operand. + /// The on the right side of the operand. /// /// /// True if the parameter is equal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Argb left, Argb right) + public static bool operator ==(Argb32 left, Argb32 right) { return left.PackedValue == right.PackedValue; } /// - /// Compares two objects for equality. + /// Compares two objects for equality. /// - /// The on the left side of the operand. - /// The on the right side of the operand. + /// The on the left side of the operand. + /// The on the right side of the operand. /// /// True if the parameter is not equal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Argb left, Argb right) + public static bool operator !=(Argb32 left, Argb32 right) { return left.PackedValue != right.PackedValue; } @@ -221,7 +224,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -239,7 +242,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { bytes[startIndex] = this.R; bytes[startIndex + 1] = this.G; @@ -248,7 +251,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { bytes[startIndex] = this.R; bytes[startIndex + 1] = this.G; @@ -258,7 +261,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { bytes[startIndex] = this.B; bytes[startIndex + 1] = this.G; @@ -267,7 +270,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { bytes[startIndex] = this.B; bytes[startIndex + 1] = this.G; @@ -278,12 +281,12 @@ namespace ImageSharp /// public override bool Equals(object obj) { - return obj is Argb && this.Equals((Argb)obj); + return obj is Argb32 && this.Equals((Argb32)obj); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Argb other) + public bool Equals(Argb32 other) { return this.PackedValue == other.PackedValue; } diff --git a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs b/src/ImageSharp/PixelFormats/Bgr565.cs similarity index 93% rename from src/ImageSharp/Colors/PackedPixel/Bgr565.cs rename to src/ImageSharp/PixelFormats/Bgr565.cs index 2975d4ad90..92bbac14cc 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/Bgr565.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -11,6 +11,9 @@ namespace ImageSharp /// /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x and z components use 5 bits, and the y component uses 6 bits. + /// + /// Ranges from <0, 0, 0, 1> to <1, 1, 1, 1> in vector form. + /// /// public struct Bgr565 : IPixel, IPackedVector { @@ -68,7 +71,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// /// Expands the packed representation into a . @@ -107,7 +110,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; bytes[startIndex] = (byte)MathF.Round(vector.X); @@ -117,7 +120,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; bytes[startIndex] = (byte)MathF.Round(vector.X); @@ -128,7 +131,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; bytes[startIndex] = (byte)MathF.Round(vector.Z); @@ -138,7 +141,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; bytes[startIndex] = (byte)MathF.Round(vector.Z); diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs b/src/ImageSharp/PixelFormats/Bgra4444.cs similarity index 93% rename from src/ImageSharp/Colors/PackedPixel/Bgra4444.cs rename to src/ImageSharp/PixelFormats/Bgra4444.cs index 1346a54efc..0bac00dfe3 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/Bgra4444.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -11,6 +11,9 @@ namespace ImageSharp /// /// Packed pixel type containing unsigned normalized values, ranging from 0 to 1, using 4 bits each for x, y, z, and w. + /// + /// Ranges from <0, 0, 0, 0> to <1, 1, 1, 1> in vector form. + /// /// public struct Bgra4444 : IPixel, IPackedVector { @@ -67,7 +70,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -98,7 +101,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; bytes[startIndex] = (byte)vector.X; @@ -108,7 +111,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; bytes[startIndex] = (byte)vector.X; @@ -119,7 +122,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; bytes[startIndex] = (byte)vector.Z; @@ -129,7 +132,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; bytes[startIndex] = (byte)vector.Z; diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs b/src/ImageSharp/PixelFormats/Bgra5551.cs similarity index 93% rename from src/ImageSharp/Colors/PackedPixel/Bgra5551.cs rename to src/ImageSharp/PixelFormats/Bgra5551.cs index 7989804cfb..f151db644a 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/Bgra5551.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -11,6 +11,9 @@ namespace ImageSharp /// /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x , y and z components use 5 bits, and the w component uses 1 bit. + /// + /// Ranges from <0, 0, 0, 0> to <1, 1, 1, 1> in vector form. + /// /// public struct Bgra5551 : IPixel, IPackedVector { @@ -69,7 +72,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -98,7 +101,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; bytes[startIndex] = (byte)vector.X; @@ -108,7 +111,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; bytes[startIndex] = (byte)vector.X; @@ -119,7 +122,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; bytes[startIndex] = (byte)vector.Z; @@ -129,7 +132,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; bytes[startIndex] = (byte)vector.Z; diff --git a/src/ImageSharp/Colors/PackedPixel/Byte4.cs b/src/ImageSharp/PixelFormats/Byte4.cs similarity index 93% rename from src/ImageSharp/Colors/PackedPixel/Byte4.cs rename to src/ImageSharp/PixelFormats/Byte4.cs index 11ec5eaf4b..264bc74972 100644 --- a/src/ImageSharp/Colors/PackedPixel/Byte4.cs +++ b/src/ImageSharp/PixelFormats/Byte4.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -11,6 +11,9 @@ namespace ImageSharp /// /// Packed pixel type containing four 8-bit unsigned integer values, ranging from 0 to 255. + /// + /// Ranges from <0, 0, 0, 0> to <1, 1, 1, 1> in vector form. + /// /// public struct Byte4 : IPixel, IPackedVector { @@ -70,7 +73,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -99,7 +102,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); bytes[startIndex] = (byte)vector.X; @@ -109,7 +112,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); bytes[startIndex] = (byte)vector.X; @@ -120,7 +123,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); bytes[startIndex] = (byte)vector.Z; @@ -130,7 +133,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); bytes[startIndex] = (byte)vector.Z; diff --git a/src/ImageSharp/Colors/ColorBuilder{TColor}.cs b/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs similarity index 77% rename from src/ImageSharp/Colors/ColorBuilder{TColor}.cs rename to src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs index f60b5c8c09..4b21130c0d 100644 --- a/src/ImageSharp/Colors/ColorBuilder{TColor}.cs +++ b/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs @@ -1,9 +1,9 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Globalization; @@ -11,19 +11,19 @@ namespace ImageSharp /// /// A set of named colors mapped to the provided Color space. /// - /// The type of the color. - public static class ColorBuilder - where TColor : struct, IPixel + /// The type of the color. + public static class ColorBuilder + where TPixel : struct, IPixel { /// - /// Creates a new representation from the string representing a color. + /// Creates a new representation from the string representing a color. /// /// /// The hexadecimal representation of the combined color components arranged /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. /// - /// Returns a that represents the color defined by the provided RGBA heax string. - public static TColor FromHex(string hex) + /// Returns a that represents the color defined by the provided RGBA heax string. + public static TPixel FromHex(string hex) { Guard.NotNullOrEmpty(hex, nameof(hex)); @@ -34,7 +34,7 @@ namespace ImageSharp throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex)); } - TColor result = default(TColor); + TPixel result = default(TPixel); result.PackFromBytes( (byte)(packedValue >> 24), @@ -45,30 +45,30 @@ namespace ImageSharp } /// - /// Creates a new representation from standard RGB bytes with 100% opacity. + /// Creates a new representation from standard RGB bytes with 100% opacity. /// /// The red intensity. /// The green intensity. /// The blue intensity. - /// Returns a that represents the color defined by the provided RGB values with 100% opacity. - public static TColor FromRGB(byte red, byte green, byte blue) + /// Returns a that represents the color defined by the provided RGB values with 100% opacity. + public static TPixel FromRGB(byte red, byte green, byte blue) { - TColor color = default(TColor); + TPixel color = default(TPixel); color.PackFromBytes(red, green, blue, 255); return color; } /// - /// Creates a new representation from standard RGBA bytes. + /// Creates a new representation from standard RGBA bytes. /// /// The red intensity. /// The green intensity. /// The blue intensity. /// The alpha intensity. - /// Returns a that represents the color defined by the provided RGBA values. - public static TColor FromRGBA(byte red, byte green, byte blue, byte alpha) + /// Returns a that represents the color defined by the provided RGBA values. + public static TPixel FromRGBA(byte red, byte green, byte blue, byte alpha) { - TColor color = default(TColor); + TPixel color = default(TPixel); color.PackFromBytes(red, green, blue, alpha); return color; } diff --git a/src/ImageSharp/PixelFormats/ColorConstants.cs b/src/ImageSharp/PixelFormats/ColorConstants.cs new file mode 100644 index 0000000000..236d3a889e --- /dev/null +++ b/src/ImageSharp/PixelFormats/ColorConstants.cs @@ -0,0 +1,179 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats +{ + using System; + using System.Collections.Generic; + + /// + /// Provides useful color definitions. + /// + public static class ColorConstants + { + /// + /// Provides a lazy, one time method of returning the colors. + /// + private static readonly Lazy SafeColors = new Lazy(GetWebSafeColors); + + /// + /// Gets a collection of named, web safe, colors as defined in the CSS Color Module Level 4. + /// + public static Rgba32[] WebSafeColors => SafeColors.Value; + + /// + /// Returns an array of web safe colors. + /// + /// The + private static Rgba32[] GetWebSafeColors() + { + return new List + { + Rgba32.AliceBlue, + Rgba32.AntiqueWhite, + Rgba32.Aqua, + Rgba32.Aquamarine, + Rgba32.Azure, + Rgba32.Beige, + Rgba32.Bisque, + Rgba32.Black, + Rgba32.BlanchedAlmond, + Rgba32.Blue, + Rgba32.BlueViolet, + Rgba32.Brown, + Rgba32.BurlyWood, + Rgba32.CadetBlue, + Rgba32.Chartreuse, + Rgba32.Chocolate, + Rgba32.Coral, + Rgba32.CornflowerBlue, + Rgba32.Cornsilk, + Rgba32.Crimson, + Rgba32.Cyan, + Rgba32.DarkBlue, + Rgba32.DarkCyan, + Rgba32.DarkGoldenrod, + Rgba32.DarkGray, + Rgba32.DarkGreen, + Rgba32.DarkKhaki, + Rgba32.DarkMagenta, + Rgba32.DarkOliveGreen, + Rgba32.DarkOrange, + Rgba32.DarkOrchid, + Rgba32.DarkRed, + Rgba32.DarkSalmon, + Rgba32.DarkSeaGreen, + Rgba32.DarkSlateBlue, + Rgba32.DarkSlateGray, + Rgba32.DarkTurquoise, + Rgba32.DarkViolet, + Rgba32.DeepPink, + Rgba32.DeepSkyBlue, + Rgba32.DimGray, + Rgba32.DodgerBlue, + Rgba32.Firebrick, + Rgba32.FloralWhite, + Rgba32.ForestGreen, + Rgba32.Fuchsia, + Rgba32.Gainsboro, + Rgba32.GhostWhite, + Rgba32.Gold, + Rgba32.Goldenrod, + Rgba32.Gray, + Rgba32.Green, + Rgba32.GreenYellow, + Rgba32.Honeydew, + Rgba32.HotPink, + Rgba32.IndianRed, + Rgba32.Indigo, + Rgba32.Ivory, + Rgba32.Khaki, + Rgba32.Lavender, + Rgba32.LavenderBlush, + Rgba32.LawnGreen, + Rgba32.LemonChiffon, + Rgba32.LightBlue, + Rgba32.LightCoral, + Rgba32.LightCyan, + Rgba32.LightGoldenrodYellow, + Rgba32.LightGray, + Rgba32.LightGreen, + Rgba32.LightPink, + Rgba32.LightSalmon, + Rgba32.LightSeaGreen, + Rgba32.LightSkyBlue, + Rgba32.LightSlateGray, + Rgba32.LightSteelBlue, + Rgba32.LightYellow, + Rgba32.Lime, + Rgba32.LimeGreen, + Rgba32.Linen, + Rgba32.Magenta, + Rgba32.Maroon, + Rgba32.MediumAquamarine, + Rgba32.MediumBlue, + Rgba32.MediumOrchid, + Rgba32.MediumPurple, + Rgba32.MediumSeaGreen, + Rgba32.MediumSlateBlue, + Rgba32.MediumSpringGreen, + Rgba32.MediumTurquoise, + Rgba32.MediumVioletRed, + Rgba32.MidnightBlue, + Rgba32.MintCream, + Rgba32.MistyRose, + Rgba32.Moccasin, + Rgba32.NavajoWhite, + Rgba32.Navy, + Rgba32.OldLace, + Rgba32.Olive, + Rgba32.OliveDrab, + Rgba32.Orange, + Rgba32.OrangeRed, + Rgba32.Orchid, + Rgba32.PaleGoldenrod, + Rgba32.PaleGreen, + Rgba32.PaleTurquoise, + Rgba32.PaleVioletRed, + Rgba32.PapayaWhip, + Rgba32.PeachPuff, + Rgba32.Peru, + Rgba32.Pink, + Rgba32.Plum, + Rgba32.PowderBlue, + Rgba32.Purple, + Rgba32.RebeccaPurple, + Rgba32.Red, + Rgba32.RosyBrown, + Rgba32.RoyalBlue, + Rgba32.SaddleBrown, + Rgba32.Salmon, + Rgba32.SandyBrown, + Rgba32.SeaGreen, + Rgba32.SeaShell, + Rgba32.Sienna, + Rgba32.Silver, + Rgba32.SkyBlue, + Rgba32.SlateBlue, + Rgba32.SlateGray, + Rgba32.Snow, + Rgba32.SpringGreen, + Rgba32.SteelBlue, + Rgba32.Tan, + Rgba32.Teal, + Rgba32.Thistle, + Rgba32.Tomato, + Rgba32.Transparent, + Rgba32.Turquoise, + Rgba32.Violet, + Rgba32.Wheat, + Rgba32.White, + Rgba32.WhiteSmoke, + Rgba32.Yellow, + Rgba32.YellowGreen + }.ToArray(); + } + } +} diff --git a/src/ImageSharp/Colors/ComponentOrder.cs b/src/ImageSharp/PixelFormats/ComponentOrder.cs similarity index 88% rename from src/ImageSharp/Colors/ComponentOrder.cs rename to src/ImageSharp/PixelFormats/ComponentOrder.cs index 03fa3bbf81..8f151b41d6 100644 --- a/src/ImageSharp/Colors/ComponentOrder.cs +++ b/src/ImageSharp/PixelFormats/ComponentOrder.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { /// /// Enumerates the various component orders. @@ -11,22 +11,22 @@ namespace ImageSharp public enum ComponentOrder { /// - /// Z-> Y-> X order. Equivalent to B-> G-> R in + /// Z-> Y-> X order. Equivalent to B-> G-> R in /// Zyx, /// - /// Z-> Y-> X-> W order. Equivalent to B-> G-> R-> A in + /// Z-> Y-> X-> W order. Equivalent to B-> G-> R-> A in /// Zyxw, /// - /// X-> Y-> Z order. Equivalent to R-> G-> B in + /// X-> Y-> Z order. Equivalent to R-> G-> B in /// Xyz, /// - /// X-> Y-> Z-> W order. Equivalent to R-> G-> B-> A in + /// X-> Y-> Z-> W order. Equivalent to R-> G-> B-> A in /// Xyzw, } diff --git a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs b/src/ImageSharp/PixelFormats/HalfSingle.cs similarity index 92% rename from src/ImageSharp/Colors/PackedPixel/HalfSingle.cs rename to src/ImageSharp/PixelFormats/HalfSingle.cs index 4c785a8636..4cc9acc222 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/HalfSingle.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -11,6 +11,9 @@ namespace ImageSharp /// /// Packed pixel type containing a single 16 bit floating point value. + /// + /// Ranges from <0, 0, 0, 1> to <1, 0, 0, 1> in vector form. + /// /// public struct HalfSingle : IPixel, IPackedVector { @@ -73,7 +76,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// /// Expands the packed representation into a . @@ -108,7 +111,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= MaxBytes; @@ -122,7 +125,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= MaxBytes; @@ -137,7 +140,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= MaxBytes; @@ -151,7 +154,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= MaxBytes; diff --git a/src/ImageSharp/Colors/PackedPixel/HalfTypeHelper.cs b/src/ImageSharp/PixelFormats/HalfTypeHelper.cs similarity index 99% rename from src/ImageSharp/Colors/PackedPixel/HalfTypeHelper.cs rename to src/ImageSharp/PixelFormats/HalfTypeHelper.cs index a19b513230..740795adc6 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfTypeHelper.cs +++ b/src/ImageSharp/PixelFormats/HalfTypeHelper.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs b/src/ImageSharp/PixelFormats/HalfVector2.cs similarity index 93% rename from src/ImageSharp/Colors/PackedPixel/HalfVector2.cs rename to src/ImageSharp/PixelFormats/HalfVector2.cs index d06ab6ba07..f490f71690 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/HalfVector2.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -11,6 +11,9 @@ namespace ImageSharp /// /// Packed pixel type containing two 16-bit floating-point values. + /// + /// Ranges from <0, 0, 0, 1> to <1, 0, 0, 1> in vector form. + /// /// public struct HalfVector2 : IPixel, IPackedVector { @@ -83,7 +86,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// /// Expands the packed representation into a . @@ -122,7 +125,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= MaxBytes; @@ -136,7 +139,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= MaxBytes; @@ -151,7 +154,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= MaxBytes; @@ -165,7 +168,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= MaxBytes; diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs b/src/ImageSharp/PixelFormats/HalfVector4.cs similarity index 93% rename from src/ImageSharp/Colors/PackedPixel/HalfVector4.cs rename to src/ImageSharp/PixelFormats/HalfVector4.cs index a5fa796e1d..7c496c161b 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/HalfVector4.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -11,6 +11,9 @@ namespace ImageSharp /// /// Packed pixel type containing four 16-bit floating-point values. + /// + /// Ranges from <0, 0, 0, 0> to <1, 1, 1, 1> in vector form. + /// /// public struct HalfVector4 : IPixel, IPackedVector { @@ -86,7 +89,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -115,7 +118,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= MaxBytes; @@ -129,7 +132,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= MaxBytes; @@ -144,7 +147,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= MaxBytes; @@ -158,7 +161,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= MaxBytes; diff --git a/src/ImageSharp/Colors/PackedPixel/IPackedVector{TPacked}.cs b/src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs similarity index 94% rename from src/ImageSharp/Colors/PackedPixel/IPackedVector{TPacked}.cs rename to src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs index 0450fb8fba..ec283e6f2a 100644 --- a/src/ImageSharp/Colors/PackedPixel/IPackedVector{TPacked}.cs +++ b/src/ImageSharp/PixelFormats/IPackedVector{TPacked}.cs @@ -3,10 +3,9 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; - using System.Numerics; /// /// This interface exists for ensuring signature compatibility to MonoGame and XNA packed color types. diff --git a/src/ImageSharp/Colors/PackedPixel/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs similarity index 81% rename from src/ImageSharp/Colors/PackedPixel/IPixel.cs rename to src/ImageSharp/PixelFormats/IPixel.cs index 67e013a422..030cb93f46 100644 --- a/src/ImageSharp/Colors/PackedPixel/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -16,11 +16,11 @@ namespace ImageSharp where TSelf : struct, IPixel { /// - /// Creates a instance for this pixel type. - /// This method is not intended to be consumed directly. Use instead. + /// Creates a instance for this pixel type. + /// This method is not intended to be consumed directly. Use instead. /// - /// The instance. - BulkPixelOperations CreateBulkOperations(); + /// The instance. + PixelOperations CreateBulkOperations(); } /// @@ -52,34 +52,34 @@ namespace ImageSharp /// /// Expands the packed representation into a given byte array. - /// Output is expanded to X-> Y-> Z order. Equivalent to R-> G-> B in + /// Output is expanded to X-> Y-> Z order. Equivalent to R-> G-> B in /// /// The bytes to set the color in. /// The starting index of the . - void ToXyzBytes(byte[] bytes, int startIndex); + void ToXyzBytes(Span bytes, int startIndex); /// /// Expands the packed representation into a given byte array. - /// Output is expanded to X-> Y-> Z-> W order. Equivalent to R-> G-> B-> A in + /// Output is expanded to X-> Y-> Z-> W order. Equivalent to R-> G-> B-> A in /// /// The bytes to set the color in. /// The starting index of the . - void ToXyzwBytes(byte[] bytes, int startIndex); + void ToXyzwBytes(Span bytes, int startIndex); /// /// Expands the packed representation into a given byte array. - /// Output is expanded to Z-> Y-> X order. Equivalent to B-> G-> R in + /// Output is expanded to Z-> Y-> X order. Equivalent to B-> G-> R in /// /// The bytes to set the color in. /// The starting index of the . - void ToZyxBytes(byte[] bytes, int startIndex); + void ToZyxBytes(Span bytes, int startIndex); /// /// Expands the packed representation into a given byte array. - /// Output is expanded to Z-> Y-> X-> W order. Equivalent to B-> G-> R-> A in + /// Output is expanded to Z-> Y-> X-> W order. Equivalent to B-> G-> R-> A in /// /// The bytes to set the color in. /// The starting index of the . - void ToZyxwBytes(byte[] bytes, int startIndex); + void ToZyxwBytes(Span bytes, int startIndex); } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs new file mode 100644 index 0000000000..0b55dcbf91 --- /dev/null +++ b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs @@ -0,0 +1,725 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats +{ + /// + /// A set of named colors mapped to the provided color space. + /// + /// The type of the color. + public static class NamedColors + where TPixel : struct, IPixel + { + /// + /// Represents a matching the W3C definition that has an hex value of #F0F8FF. + /// + public static readonly TPixel AliceBlue = ColorBuilder.FromRGBA(240, 248, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FAEBD7. + /// + public static readonly TPixel AntiqueWhite = ColorBuilder.FromRGBA(250, 235, 215, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00FFFF. + /// + public static readonly TPixel Aqua = ColorBuilder.FromRGBA(0, 255, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #7FFFD4. + /// + public static readonly TPixel Aquamarine = ColorBuilder.FromRGBA(127, 255, 212, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F0FFFF. + /// + public static readonly TPixel Azure = ColorBuilder.FromRGBA(240, 255, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F5F5DC. + /// + public static readonly TPixel Beige = ColorBuilder.FromRGBA(245, 245, 220, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4C4. + /// + public static readonly TPixel Bisque = ColorBuilder.FromRGBA(255, 228, 196, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #000000. + /// + public static readonly TPixel Black = ColorBuilder.FromRGBA(0, 0, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFEBCD. + /// + public static readonly TPixel BlanchedAlmond = ColorBuilder.FromRGBA(255, 235, 205, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #0000FF. + /// + public static readonly TPixel Blue = ColorBuilder.FromRGBA(0, 0, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #8A2BE2. + /// + public static readonly TPixel BlueViolet = ColorBuilder.FromRGBA(138, 43, 226, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #A52A2A. + /// + public static readonly TPixel Brown = ColorBuilder.FromRGBA(165, 42, 42, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DEB887. + /// + public static readonly TPixel BurlyWood = ColorBuilder.FromRGBA(222, 184, 135, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #5F9EA0. + /// + public static readonly TPixel CadetBlue = ColorBuilder.FromRGBA(95, 158, 160, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #7FFF00. + /// + public static readonly TPixel Chartreuse = ColorBuilder.FromRGBA(127, 255, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #D2691E. + /// + public static readonly TPixel Chocolate = ColorBuilder.FromRGBA(210, 105, 30, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF7F50. + /// + public static readonly TPixel Coral = ColorBuilder.FromRGBA(255, 127, 80, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #6495ED. + /// + public static readonly TPixel CornflowerBlue = ColorBuilder.FromRGBA(100, 149, 237, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF8DC. + /// + public static readonly TPixel Cornsilk = ColorBuilder.FromRGBA(255, 248, 220, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DC143C. + /// + public static readonly TPixel Crimson = ColorBuilder.FromRGBA(220, 20, 60, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00FFFF. + /// + public static readonly TPixel Cyan = ColorBuilder.FromRGBA(0, 255, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00008B. + /// + public static readonly TPixel DarkBlue = ColorBuilder.FromRGBA(0, 0, 139, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #008B8B. + /// + public static readonly TPixel DarkCyan = ColorBuilder.FromRGBA(0, 139, 139, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #B8860B. + /// + public static readonly TPixel DarkGoldenrod = ColorBuilder.FromRGBA(184, 134, 11, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #A9A9A9. + /// + public static readonly TPixel DarkGray = ColorBuilder.FromRGBA(169, 169, 169, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #006400. + /// + public static readonly TPixel DarkGreen = ColorBuilder.FromRGBA(0, 100, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #BDB76B. + /// + public static readonly TPixel DarkKhaki = ColorBuilder.FromRGBA(189, 183, 107, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #8B008B. + /// + public static readonly TPixel DarkMagenta = ColorBuilder.FromRGBA(139, 0, 139, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #556B2F. + /// + public static readonly TPixel DarkOliveGreen = ColorBuilder.FromRGBA(85, 107, 47, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF8C00. + /// + public static readonly TPixel DarkOrange = ColorBuilder.FromRGBA(255, 140, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #9932CC. + /// + public static readonly TPixel DarkOrchid = ColorBuilder.FromRGBA(153, 50, 204, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #8B0000. + /// + public static readonly TPixel DarkRed = ColorBuilder.FromRGBA(139, 0, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #E9967A. + /// + public static readonly TPixel DarkSalmon = ColorBuilder.FromRGBA(233, 150, 122, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #8FBC8B. + /// + public static readonly TPixel DarkSeaGreen = ColorBuilder.FromRGBA(143, 188, 139, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #483D8B. + /// + public static readonly TPixel DarkSlateBlue = ColorBuilder.FromRGBA(72, 61, 139, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #2F4F4F. + /// + public static readonly TPixel DarkSlateGray = ColorBuilder.FromRGBA(47, 79, 79, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00CED1. + /// + public static readonly TPixel DarkTurquoise = ColorBuilder.FromRGBA(0, 206, 209, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #9400D3. + /// + public static readonly TPixel DarkViolet = ColorBuilder.FromRGBA(148, 0, 211, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF1493. + /// + public static readonly TPixel DeepPink = ColorBuilder.FromRGBA(255, 20, 147, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00BFFF. + /// + public static readonly TPixel DeepSkyBlue = ColorBuilder.FromRGBA(0, 191, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #696969. + /// + public static readonly TPixel DimGray = ColorBuilder.FromRGBA(105, 105, 105, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #1E90FF. + /// + public static readonly TPixel DodgerBlue = ColorBuilder.FromRGBA(30, 144, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #B22222. + /// + public static readonly TPixel Firebrick = ColorBuilder.FromRGBA(178, 34, 34, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFAF0. + /// + public static readonly TPixel FloralWhite = ColorBuilder.FromRGBA(255, 250, 240, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #228B22. + /// + public static readonly TPixel ForestGreen = ColorBuilder.FromRGBA(34, 139, 34, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF00FF. + /// + public static readonly TPixel Fuchsia = ColorBuilder.FromRGBA(255, 0, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DCDCDC. + /// + public static readonly TPixel Gainsboro = ColorBuilder.FromRGBA(220, 220, 220, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F8F8FF. + /// + public static readonly TPixel GhostWhite = ColorBuilder.FromRGBA(248, 248, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFD700. + /// + public static readonly TPixel Gold = ColorBuilder.FromRGBA(255, 215, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DAA520. + /// + public static readonly TPixel Goldenrod = ColorBuilder.FromRGBA(218, 165, 32, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #808080. + /// + public static readonly TPixel Gray = ColorBuilder.FromRGBA(128, 128, 128, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #008000. + /// + public static readonly TPixel Green = ColorBuilder.FromRGBA(0, 128, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #ADFF2F. + /// + public static readonly TPixel GreenYellow = ColorBuilder.FromRGBA(173, 255, 47, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F0FFF0. + /// + public static readonly TPixel Honeydew = ColorBuilder.FromRGBA(240, 255, 240, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF69B4. + /// + public static readonly TPixel HotPink = ColorBuilder.FromRGBA(255, 105, 180, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #CD5C5C. + /// + public static readonly TPixel IndianRed = ColorBuilder.FromRGBA(205, 92, 92, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #4B0082. + /// + public static readonly TPixel Indigo = ColorBuilder.FromRGBA(75, 0, 130, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFF0. + /// + public static readonly TPixel Ivory = ColorBuilder.FromRGBA(255, 255, 240, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F0E68C. + /// + public static readonly TPixel Khaki = ColorBuilder.FromRGBA(240, 230, 140, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #E6E6FA. + /// + public static readonly TPixel Lavender = ColorBuilder.FromRGBA(230, 230, 250, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF0F5. + /// + public static readonly TPixel LavenderBlush = ColorBuilder.FromRGBA(255, 240, 245, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #7CFC00. + /// + public static readonly TPixel LawnGreen = ColorBuilder.FromRGBA(124, 252, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFACD. + /// + public static readonly TPixel LemonChiffon = ColorBuilder.FromRGBA(255, 250, 205, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #ADD8E6. + /// + public static readonly TPixel LightBlue = ColorBuilder.FromRGBA(173, 216, 230, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F08080. + /// + public static readonly TPixel LightCoral = ColorBuilder.FromRGBA(240, 128, 128, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #E0FFFF. + /// + public static readonly TPixel LightCyan = ColorBuilder.FromRGBA(224, 255, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FAFAD2. + /// + public static readonly TPixel LightGoldenrodYellow = ColorBuilder.FromRGBA(250, 250, 210, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #D3D3D3. + /// + public static readonly TPixel LightGray = ColorBuilder.FromRGBA(211, 211, 211, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #90EE90. + /// + public static readonly TPixel LightGreen = ColorBuilder.FromRGBA(144, 238, 144, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFB6C1. + /// + public static readonly TPixel LightPink = ColorBuilder.FromRGBA(255, 182, 193, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFA07A. + /// + public static readonly TPixel LightSalmon = ColorBuilder.FromRGBA(255, 160, 122, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #20B2AA. + /// + public static readonly TPixel LightSeaGreen = ColorBuilder.FromRGBA(32, 178, 170, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #87CEFA. + /// + public static readonly TPixel LightSkyBlue = ColorBuilder.FromRGBA(135, 206, 250, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #778899. + /// + public static readonly TPixel LightSlateGray = ColorBuilder.FromRGBA(119, 136, 153, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #B0C4DE. + /// + public static readonly TPixel LightSteelBlue = ColorBuilder.FromRGBA(176, 196, 222, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFE0. + /// + public static readonly TPixel LightYellow = ColorBuilder.FromRGBA(255, 255, 224, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00FF00. + /// + public static readonly TPixel Lime = ColorBuilder.FromRGBA(0, 255, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #32CD32. + /// + public static readonly TPixel LimeGreen = ColorBuilder.FromRGBA(50, 205, 50, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FAF0E6. + /// + public static readonly TPixel Linen = ColorBuilder.FromRGBA(250, 240, 230, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF00FF. + /// + public static readonly TPixel Magenta = ColorBuilder.FromRGBA(255, 0, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #800000. + /// + public static readonly TPixel Maroon = ColorBuilder.FromRGBA(128, 0, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #66CDAA. + /// + public static readonly TPixel MediumAquamarine = ColorBuilder.FromRGBA(102, 205, 170, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #0000CD. + /// + public static readonly TPixel MediumBlue = ColorBuilder.FromRGBA(0, 0, 205, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #BA55D3. + /// + public static readonly TPixel MediumOrchid = ColorBuilder.FromRGBA(186, 85, 211, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #9370DB. + /// + public static readonly TPixel MediumPurple = ColorBuilder.FromRGBA(147, 112, 219, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #3CB371. + /// + public static readonly TPixel MediumSeaGreen = ColorBuilder.FromRGBA(60, 179, 113, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #7B68EE. + /// + public static readonly TPixel MediumSlateBlue = ColorBuilder.FromRGBA(123, 104, 238, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00FA9A. + /// + public static readonly TPixel MediumSpringGreen = ColorBuilder.FromRGBA(0, 250, 154, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #48D1CC. + /// + public static readonly TPixel MediumTurquoise = ColorBuilder.FromRGBA(72, 209, 204, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #C71585. + /// + public static readonly TPixel MediumVioletRed = ColorBuilder.FromRGBA(199, 21, 133, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #191970. + /// + public static readonly TPixel MidnightBlue = ColorBuilder.FromRGBA(25, 25, 112, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F5FFFA. + /// + public static readonly TPixel MintCream = ColorBuilder.FromRGBA(245, 255, 250, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4E1. + /// + public static readonly TPixel MistyRose = ColorBuilder.FromRGBA(255, 228, 225, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4B5. + /// + public static readonly TPixel Moccasin = ColorBuilder.FromRGBA(255, 228, 181, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFDEAD. + /// + public static readonly TPixel NavajoWhite = ColorBuilder.FromRGBA(255, 222, 173, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #000080. + /// + public static readonly TPixel Navy = ColorBuilder.FromRGBA(0, 0, 128, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FDF5E6. + /// + public static readonly TPixel OldLace = ColorBuilder.FromRGBA(253, 245, 230, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #808000. + /// + public static readonly TPixel Olive = ColorBuilder.FromRGBA(128, 128, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #6B8E23. + /// + public static readonly TPixel OliveDrab = ColorBuilder.FromRGBA(107, 142, 35, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFA500. + /// + public static readonly TPixel Orange = ColorBuilder.FromRGBA(255, 165, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF4500. + /// + public static readonly TPixel OrangeRed = ColorBuilder.FromRGBA(255, 69, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DA70D6. + /// + public static readonly TPixel Orchid = ColorBuilder.FromRGBA(218, 112, 214, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #EEE8AA. + /// + public static readonly TPixel PaleGoldenrod = ColorBuilder.FromRGBA(238, 232, 170, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #98FB98. + /// + public static readonly TPixel PaleGreen = ColorBuilder.FromRGBA(152, 251, 152, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #AFEEEE. + /// + public static readonly TPixel PaleTurquoise = ColorBuilder.FromRGBA(175, 238, 238, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DB7093. + /// + public static readonly TPixel PaleVioletRed = ColorBuilder.FromRGBA(219, 112, 147, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFEFD5. + /// + public static readonly TPixel PapayaWhip = ColorBuilder.FromRGBA(255, 239, 213, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFDAB9. + /// + public static readonly TPixel PeachPuff = ColorBuilder.FromRGBA(255, 218, 185, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #CD853F. + /// + public static readonly TPixel Peru = ColorBuilder.FromRGBA(205, 133, 63, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFC0CB. + /// + public static readonly TPixel Pink = ColorBuilder.FromRGBA(255, 192, 203, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #DDA0DD. + /// + public static readonly TPixel Plum = ColorBuilder.FromRGBA(221, 160, 221, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #B0E0E6. + /// + public static readonly TPixel PowderBlue = ColorBuilder.FromRGBA(176, 224, 230, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #800080. + /// + public static readonly TPixel Purple = ColorBuilder.FromRGBA(128, 0, 128, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #663399. + /// + public static readonly TPixel RebeccaPurple = ColorBuilder.FromRGBA(102, 51, 153, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF0000. + /// + public static readonly TPixel Red = ColorBuilder.FromRGBA(255, 0, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #BC8F8F. + /// + public static readonly TPixel RosyBrown = ColorBuilder.FromRGBA(188, 143, 143, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #4169E1. + /// + public static readonly TPixel RoyalBlue = ColorBuilder.FromRGBA(65, 105, 225, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #8B4513. + /// + public static readonly TPixel SaddleBrown = ColorBuilder.FromRGBA(139, 69, 19, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FA8072. + /// + public static readonly TPixel Salmon = ColorBuilder.FromRGBA(250, 128, 114, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F4A460. + /// + public static readonly TPixel SandyBrown = ColorBuilder.FromRGBA(244, 164, 96, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #2E8B57. + /// + public static readonly TPixel SeaGreen = ColorBuilder.FromRGBA(46, 139, 87, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF5EE. + /// + public static readonly TPixel SeaShell = ColorBuilder.FromRGBA(255, 245, 238, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #A0522D. + /// + public static readonly TPixel Sienna = ColorBuilder.FromRGBA(160, 82, 45, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #C0C0C0. + /// + public static readonly TPixel Silver = ColorBuilder.FromRGBA(192, 192, 192, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #87CEEB. + /// + public static readonly TPixel SkyBlue = ColorBuilder.FromRGBA(135, 206, 235, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #6A5ACD. + /// + public static readonly TPixel SlateBlue = ColorBuilder.FromRGBA(106, 90, 205, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #708090. + /// + public static readonly TPixel SlateGray = ColorBuilder.FromRGBA(112, 128, 144, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFAFA. + /// + public static readonly TPixel Snow = ColorBuilder.FromRGBA(255, 250, 250, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #00FF7F. + /// + public static readonly TPixel SpringGreen = ColorBuilder.FromRGBA(0, 255, 127, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #4682B4. + /// + public static readonly TPixel SteelBlue = ColorBuilder.FromRGBA(70, 130, 180, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #D2B48C. + /// + public static readonly TPixel Tan = ColorBuilder.FromRGBA(210, 180, 140, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #008080. + /// + public static readonly TPixel Teal = ColorBuilder.FromRGBA(0, 128, 128, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #D8BFD8. + /// + public static readonly TPixel Thistle = ColorBuilder.FromRGBA(216, 191, 216, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FF6347. + /// + public static readonly TPixel Tomato = ColorBuilder.FromRGBA(255, 99, 71, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFFF. + /// + public static readonly TPixel Transparent = ColorBuilder.FromRGBA(255, 255, 255, 0); + + /// + /// Represents a matching the W3C definition that has an hex value of #40E0D0. + /// + public static readonly TPixel Turquoise = ColorBuilder.FromRGBA(64, 224, 208, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #EE82EE. + /// + public static readonly TPixel Violet = ColorBuilder.FromRGBA(238, 130, 238, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F5DEB3. + /// + public static readonly TPixel Wheat = ColorBuilder.FromRGBA(245, 222, 179, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFFF. + /// + public static readonly TPixel White = ColorBuilder.FromRGBA(255, 255, 255, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #F5F5F5. + /// + public static readonly TPixel WhiteSmoke = ColorBuilder.FromRGBA(245, 245, 245, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFF00. + /// + public static readonly TPixel Yellow = ColorBuilder.FromRGBA(255, 255, 0, 255); + + /// + /// Represents a matching the W3C definition that has an hex value of #9ACD32. + /// + public static readonly TPixel YellowGreen = ColorBuilder.FromRGBA(154, 205, 50, 255); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/NormalizedByte2.cs similarity index 93% rename from src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs rename to src/ImageSharp/PixelFormats/NormalizedByte2.cs index 56be64a86c..47a4f30059 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/NormalizedByte2.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -11,6 +11,9 @@ namespace ImageSharp /// /// Packed packed pixel type containing two 8-bit signed normalized values, ranging from −1 to 1. + /// + /// Ranges from <-1, -1, 0, 1> to <1, 1, 0, 1> in vector form. + /// /// public struct NormalizedByte2 : IPixel, IPackedVector { @@ -88,7 +91,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// /// Expands the packed representation into a . @@ -131,7 +134,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= Half; @@ -147,7 +150,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= Half; @@ -164,7 +167,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= Half; @@ -180,7 +183,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= Half; diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/NormalizedByte4.cs similarity index 94% rename from src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs rename to src/ImageSharp/PixelFormats/NormalizedByte4.cs index a1f9b8d847..4559bd082f 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/NormalizedByte4.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -11,6 +11,9 @@ namespace ImageSharp /// /// Packed pixel type containing four 8-bit signed normalized values, ranging from −1 to 1. + /// + /// Ranges from <-1, -1, -1, -1> to <1, 1, 1, 1> in vector form. + /// /// public struct NormalizedByte4 : IPixel, IPackedVector { @@ -90,7 +93,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -124,7 +127,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= Half; @@ -140,7 +143,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= Half; @@ -157,7 +160,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= Half; @@ -173,7 +176,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= Half; diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/NormalizedShort2.cs similarity index 94% rename from src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs rename to src/ImageSharp/PixelFormats/NormalizedShort2.cs index 46c24be6f9..648b68905a 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/NormalizedShort2.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -11,6 +11,9 @@ namespace ImageSharp /// /// Packed pixel type containing two 16-bit signed normalized values, ranging from −1 to 1. + /// + /// Ranges from <-1, -1, 0, 1> to <1, 1, 0, 1> in vector form. + /// /// public struct NormalizedShort2 : IPixel, IPackedVector { @@ -88,7 +91,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -118,7 +121,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= Half; @@ -134,7 +137,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= Half; @@ -151,7 +154,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= Half; @@ -167,7 +170,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= Half; diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/NormalizedShort4.cs similarity index 94% rename from src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs rename to src/ImageSharp/PixelFormats/NormalizedShort4.cs index 74229a914f..7b520aacef 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/NormalizedShort4.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -11,6 +11,9 @@ namespace ImageSharp /// /// Packed pixel type containing four 16-bit signed normalized values, ranging from −1 to 1. + /// + /// Ranges from <-1, -1, -1, -1> to <1, 1, 1, 1> in vector form. + /// /// public struct NormalizedShort4 : IPixel, IPackedVector { @@ -90,7 +93,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -126,7 +129,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= Half; @@ -142,7 +145,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= Half; @@ -159,7 +162,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= Half; @@ -175,7 +178,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector *= Half; diff --git a/src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs b/src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs similarity index 97% rename from src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs rename to src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs index 949e44cc01..29ef5675e7 100644 --- a/src/ImageSharp/Colors/PackedPixel/PackedPixelConverterHelper.cs +++ b/src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -22,10 +22,10 @@ namespace ImageSharp /// Returns the correct scaling function for the given types The compute scale function. /// /// The scale function. - /// The source pixel format. - /// The target pixel format. + /// The source pixel format. + /// The target pixel format. /// The - public static Func ComputeScaleFunction(Func scaleFunc) + public static Func ComputeScaleFunction(Func scaleFunc) { // Custom type with a custom function. if (scaleFunc != null) @@ -33,8 +33,8 @@ namespace ImageSharp return scaleFunc; } - Type source = typeof(TColor); - Type target = typeof(TColor2); + Type source = typeof(TPixel); + Type target = typeof(TPixel2); // Normalized standard if (IsStandardNormalizedType(source)) @@ -299,8 +299,8 @@ namespace ImageSharp /// The private static bool IsStandardNormalizedType(Type type) { - return type == typeof(Color) - || type == typeof(Argb) + return type == typeof(Rgba32) + || type == typeof(Argb32) || type == typeof(Alpha8) || type == typeof(Bgr565) || type == typeof(Bgra4444) diff --git a/src/ImageSharp/PixelFormats/PixelBlenderMode.cs b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs new file mode 100644 index 0000000000..d8031fe6e5 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenderMode.cs @@ -0,0 +1,62 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats +{ + using System; + using System.Collections.Generic; + using System.Text; + + /// + /// The various blending modes. + /// + public enum PixelBlenderMode + { + /// + /// Default blending mode, also known as "Normal" or "Alpha Blending" + /// + Normal = 0, + + /// + /// Blends the 2 values by multiplication. + /// + Multiply, + + /// + /// Blends the 2 values by addition. + /// + Add, + + /// + /// Blends the 2 values by subtraction. + /// + Substract, + + /// + /// Multiplies the complements of the backdrop and source values, then complements the result. + /// + Screen, + + /// + /// Selects the minimum of the backdrop and source values. + /// + Darken, + + /// + /// Selects the max of the backdrop and source values. + /// + Lighten, + + /// + /// Multiplies or screens the values, depending on the backdrop vector values. + /// + Overlay, + + /// + /// Multiplies or screens the colors, depending on the source value. + /// + HardLight + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs new file mode 100644 index 0000000000..261a986742 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultAddPixelBlender{TPixel}.cs @@ -0,0 +1,57 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + + /// + /// Applies an "Add" blending to pixels. + /// + /// The type of the pixel + internal class DefaultAddPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + /// Gets the static instance of this blender. + /// + public static DefaultAddPixelBlender Instance { get; } = new DefaultAddPixelBlender(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.AddFunction(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.AddFunction(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs new file mode 100644 index 0000000000..bca99e2f0d --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultDarkenPixelBlender{TPixel}.cs @@ -0,0 +1,57 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + + /// + /// Applies an "Darken" blending to pixels. + /// + /// The type of the pixel + internal class DefaultDarkenPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + /// Gets the static instance of this blender. + /// + public static DefaultDarkenPixelBlender Instance { get; } = new DefaultDarkenPixelBlender(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.DarkenFunction(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.DarkenFunction(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs new file mode 100644 index 0000000000..646423cffb --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultHardLightPixelBlender{TPixel}.cs @@ -0,0 +1,57 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + + /// + /// Applies an "Hard Light" blending to pixels. + /// + /// The type of the pixel + internal class DefaultHardLightPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + /// Gets the static instance of this blender. + /// + public static DefaultHardLightPixelBlender Instance { get; } = new DefaultHardLightPixelBlender(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.HardLightFunction(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.HardLightFunction(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs new file mode 100644 index 0000000000..55ad81e7a9 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultLightenPixelBlender{TPixel}.cs @@ -0,0 +1,57 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + + /// + /// Applies an "Lighten" blending to pixels. + /// + /// The type of the pixel + internal class DefaultLightenPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + /// Gets the static instance of this blender. + /// + public static DefaultLightenPixelBlender Instance { get; } = new DefaultLightenPixelBlender(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.LightenFunction(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.LightenFunction(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs new file mode 100644 index 0000000000..e21efaed07 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultMultiplyPixelBlender{TPixel}.cs @@ -0,0 +1,57 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + + /// + /// Applies an "Multiply" blending to pixels. + /// + /// The type of the pixel + internal class DefaultMultiplyPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + /// Gets the static instance of this blender. + /// + public static DefaultMultiplyPixelBlender Instance { get; } = new DefaultMultiplyPixelBlender(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.MultiplyFunction(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.MultiplyFunction(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs new file mode 100644 index 0000000000..9d63d11e0b --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultNormalPixelBlender{TPixel}.cs @@ -0,0 +1,57 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + + /// + /// Applies a "Normal" otherwise nown as "Alpha Blending" blending to pixels. + /// + /// The type of the pixel + internal class DefaultNormalPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + /// Gets the static instance of this blender. + /// + public static DefaultNormalPixelBlender Instance { get; } = new DefaultNormalPixelBlender(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.NormalBlendFunction(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.NormalBlendFunction(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs new file mode 100644 index 0000000000..8172909ecd --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultOverlayPixelBlender{TPixel}.cs @@ -0,0 +1,57 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + + /// + /// Applies an "Overlay" blending to pixels. + /// + /// The type of the pixel + internal class DefaultOverlayPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + /// Gets the static instance of this blender. + /// + public static DefaultOverlayPixelBlender Instance { get; } = new DefaultOverlayPixelBlender(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.OverlayFunction(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.OverlayFunction(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs new file mode 100644 index 0000000000..8405c3946b --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultScreenPixelBlender{TPixel}.cs @@ -0,0 +1,57 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + + /// + /// Applies an "Screen" blending to pixels. + /// + /// The type of the pixel + internal class DefaultScreenPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + /// Gets the static instance of this blender. + /// + public static DefaultScreenPixelBlender Instance { get; } = new DefaultScreenPixelBlender(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.ScreenFunction(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.ScreenFunction(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs new file mode 100644 index 0000000000..ab44cb7609 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultSubstractPixelBlender{TPixel}.cs @@ -0,0 +1,57 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System; + using System.Numerics; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + + /// + /// Applies an "Subtract" blending to pixels. + /// + /// The type of the pixel + internal class DefaultSubstractPixelBlender : PixelBlender + where TPixel : struct, IPixel + { + /// + /// Gets the static instance of this blender. + /// + public static DefaultSubstractPixelBlender Instance { get; } = new DefaultSubstractPixelBlender(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { + return PorterDuffFunctions.SubstractFunction(background, source, amount); + } + + /// + public override void Blend(Span destination, Span background, Span source, Span amount) + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.SubstractFunction(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + } +} diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs new file mode 100644 index 0000000000..25eb6a5c87 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs @@ -0,0 +1,242 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// Collection of Porter Duff alpha blending functions applying an the 'Over' composition model. + /// + /// + /// These functions are designed to be a general solution for all color cases, + /// that is, they take in account the alpha value of both the backdrop + /// and source, and there's no need to alpha-premultiply neither the backdrop + /// nor the source. + /// Note there are faster functions for when the backdrop color is known + /// to be opaque + /// + internal static partial class PorterDuffFunctions + { + /// + /// Source over backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 NormalBlendFunction(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + if (source.W == 0) + { + return backdrop; + } + + return Compose(backdrop, source, source); + } + + /// + /// Source multiplied by backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 MultiplyFunction(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + if (source.W == 0) + { + return backdrop; + } + + return Compose(backdrop, source, backdrop * source); + } + + /// + /// Source added to backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 AddFunction(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + if (source.W == 0) + { + return backdrop; + } + + return Compose(backdrop, source, Vector4.Min(Vector4.One, backdrop + source)); + } + + /// + /// Source substracted from backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SubstractFunction(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + if (source.W == 0) + { + return backdrop; + } + + return Compose(backdrop, source, Vector4.Max(Vector4.Zero, backdrop - source)); + } + + /// + /// Complement of source multiplied by the complement of backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 ScreenFunction(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + if (source.W == 0) + { + return backdrop; + } + + return Compose(backdrop, source, Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source))); + } + + /// + /// Per element, chooses the smallest value of source and backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 DarkenFunction(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + if (source.W == 0) + { + return backdrop; + } + + return Compose(backdrop, source, Vector4.Min(backdrop, source)); + } + + /// + /// Per element, chooses the largest value of source and backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 LightenFunction(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + if (source.W == 0) + { + return backdrop; + } + + return Compose(backdrop, source, Vector4.Max(backdrop, source)); + } + + /// + /// Overlays source over backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 OverlayFunction(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + if (source.W == 0) + { + return backdrop; + } + + float cr = OverlayValueFunction(backdrop.X, source.X); + float cg = OverlayValueFunction(backdrop.Y, source.Y); + float cb = OverlayValueFunction(backdrop.Z, source.Z); + + return Compose(backdrop, source, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); + } + + /// + /// Hard light effect + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 HardLightFunction(Vector4 backdrop, Vector4 source, float opacity) + { + source.W *= opacity; + if (source.W == 0) + { + return backdrop; + } + + float cr = OverlayValueFunction(source.X, backdrop.X); + float cg = OverlayValueFunction(source.Y, backdrop.Y); + float cb = OverlayValueFunction(source.Z, backdrop.Z); + + return Compose(backdrop, source, Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0))); + } + + /// + /// Helper function for Overlay andHardLight modes + /// + /// Backdrop color element + /// Source color element + /// Overlay value + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float OverlayValueFunction(float backdrop, float source) + { + return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop)); + } + + /// + /// General composition function for all modes, with a general solution for alpha channel + /// + /// Original backgrop color + /// Original source color + /// Desired transformed color, without taking Alpha channel in account + /// The final color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector4 Compose(Vector4 backdrop, Vector4 source, Vector4 xform) + { + DebugGuard.MustBeGreaterThan(source.W, 0, nameof(source.W)); + + // calculate weights + float xw = backdrop.W * source.W; + float bw = backdrop.W - xw; + float sw = source.W - xw; + + // calculate final alpha + float a = xw + bw + sw; + + // calculate final value + xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / a; + xform.W = a; + + return xform; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions{TPixel}.cs new file mode 100644 index 0000000000..4e829212e8 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions{TPixel}.cs @@ -0,0 +1,151 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats.PixelBlenders +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// Collection of Porter Duff alpha blending functions + /// + /// Pixel Format + /// + /// These functions are designed to be a general solution for all color cases, + /// that is, they take in account the alpha value of both the backdrop + /// and source, and there's no need to alpha-premultiply neither the backdrop + /// nor the source. + /// Note there are faster functions for when the backdrop color is known + /// to be opaque + /// + internal static class PorterDuffFunctions + where TPixel : IPixel + { + /// + /// Source over backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel NormalBlendFunction(TPixel backdrop, TPixel source, float opacity) + { + return ToPixel(PorterDuffFunctions.NormalBlendFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); + } + + /// + /// Source multiplied by backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel MultiplyFunction(TPixel backdrop, TPixel source, float opacity) + { + return ToPixel(PorterDuffFunctions.MultiplyFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); + } + + /// + /// Source added to backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel AddFunction(TPixel backdrop, TPixel source, float opacity) + { + return ToPixel(PorterDuffFunctions.AddFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); + } + + /// + /// Source substracted from backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel SubstractFunction(TPixel backdrop, TPixel source, float opacity) + { + return ToPixel(PorterDuffFunctions.SubstractFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); + } + + /// + /// Complement of source multiplied by the complement of backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel ScreenFunction(TPixel backdrop, TPixel source, float opacity) + { + return ToPixel(PorterDuffFunctions.ScreenFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); + } + + /// + /// Per element, chooses the smallest value of source and backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel DarkenFunction(TPixel backdrop, TPixel source, float opacity) + { + return ToPixel(PorterDuffFunctions.DarkenFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); + } + + /// + /// Per element, chooses the largest value of source and backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel LightenFunction(TPixel backdrop, TPixel source, float opacity) + { + return ToPixel(PorterDuffFunctions.LightenFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); + } + + /// + /// Overlays source over backdrop + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel OverlayFunction(TPixel backdrop, TPixel source, float opacity) + { + return ToPixel(PorterDuffFunctions.OverlayFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); + } + + /// + /// Hard light effect + /// + /// Backgrop color + /// Source color + /// Opacity applied to Source Alpha + /// Output color + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TPixel HardLightFunction(TPixel backdrop, TPixel source, float opacity) + { + return ToPixel(PorterDuffFunctions.HardLightFunction(backdrop.ToVector4(), source.ToVector4(), opacity)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static TPixel ToPixel(Vector4 vector) + { + TPixel p = default(TPixel); + p.PackFromVector4(vector); + return p; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs new file mode 100644 index 0000000000..1a1d1cd054 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -0,0 +1,41 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats +{ + using System; + + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal abstract class PixelBlender + where TPixel : struct, IPixel + { + /// + /// Blend 2 pixels together. + /// + /// The background color. + /// The source color. + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// + /// The final pixel value after composition + public abstract TPixel Blend(TPixel background, TPixel source, float amount); + + /// + /// Blend 2 pixels together. + /// + /// The destination span. + /// The background span. + /// The source span. + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// + public abstract void Blend(Span destination, Span background, Span source, Span amount); + } +} diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs new file mode 100644 index 0000000000..cab357c412 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs @@ -0,0 +1,47 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats +{ + using ImageSharp.PixelFormats.PixelBlenders; + + /// + /// Provides access to pixel blenders + /// + public partial class PixelOperations + where TPixel : struct, IPixel + { + /// + /// Find an instance of the pixel blender. + /// + /// The blending mode to apply + /// A . + internal virtual PixelBlender GetPixelBlender(PixelBlenderMode mode) + { + switch (mode) + { + case PixelBlenderMode.Multiply: + return DefaultMultiplyPixelBlender.Instance; + case PixelBlenderMode.Add: + return DefaultAddPixelBlender.Instance; + case PixelBlenderMode.Substract: + return DefaultSubstractPixelBlender.Instance; + case PixelBlenderMode.Screen: + return DefaultScreenPixelBlender.Instance; + case PixelBlenderMode.Darken: + return DefaultDarkenPixelBlender.Instance; + case PixelBlenderMode.Lighten: + return DefaultLightenPixelBlender.Instance; + case PixelBlenderMode.Overlay: + return DefaultOverlayPixelBlender.Instance; + case PixelBlenderMode.HardLight: + return DefaultHardLightPixelBlender.Instance; + case PixelBlenderMode.Normal: + default: + return DefaultNormalPixelBlender.Instance; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs new file mode 100644 index 0000000000..993a11232a --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -0,0 +1,253 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations + /// for pixel buffers of type . + /// + /// The pixel format. + public partial class PixelOperations + where TPixel : struct, IPixel + { + /// + /// Gets the global instance for the pixel type + /// + public static PixelOperations Instance { get; } = default(TPixel).CreateBulkOperations(); + + /// + /// Bulk version of + /// + /// The to the source vectors. + /// The to the destination colors. + /// The number of pixels to convert. + internal virtual void PackFromVector4(Span sourceVectors, Span destColors, int count) + { + Guard.MustBeSizedAtLeast(sourceVectors, count, nameof(sourceVectors)); + Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors)); + + ref Vector4 sourceRef = ref sourceVectors.DangerousGetPinnableReference(); + ref TPixel destRef = ref destColors.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i); + ref TPixel dp = ref Unsafe.Add(ref destRef, i); + dp.PackFromVector4(sp); + } + } + + /// + /// Bulk version of . + /// + /// The to the source colors. + /// The to the destination vectors. + /// The number of pixels to convert. + internal virtual void ToVector4(Span sourceColors, Span destVectors, int count) + { + Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors)); + Guard.MustBeSizedAtLeast(destVectors, count, nameof(destVectors)); + + ref TPixel sourceRef = ref sourceColors.DangerousGetPinnableReference(); + ref Vector4 destRef = ref destVectors.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceRef, i); + ref Vector4 dp = ref Unsafe.Add(ref destRef, i); + dp = sp.ToVector4(); + } + } + + /// + /// Bulk version of that converts data in . + /// + /// The to the source bytes. + /// The to the destination colors. + /// The number of pixels to convert. + internal virtual void PackFromXyzBytes(Span sourceBytes, Span destColors, int count) + { + Guard.MustBeSizedAtLeast(sourceBytes, count * 3, nameof(sourceBytes)); + Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors)); + + ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference(); + ref TPixel destRef = ref destColors.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + int i3 = i * 3; + ref TPixel dp = ref Unsafe.Add(ref destRef, i); + dp.PackFromBytes( + Unsafe.Add(ref sourceRef, i3), + Unsafe.Add(ref sourceRef, i3 + 1), + Unsafe.Add(ref sourceRef, i3 + 2), + 255); + } + } + + /// + /// Bulk version of . + /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. + internal virtual void ToXyzBytes(Span sourceColors, Span destBytes, int count) + { + Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors)); + Guard.MustBeSizedAtLeast(destBytes, count * 3, nameof(destBytes)); + + ref TPixel sourceRef = ref sourceColors.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceRef, i); + sp.ToXyzBytes(destBytes, i * 3); + } + } + + /// + /// Bulk version of that converts data in . + /// + /// The to the source bytes. + /// The to the destination colors. + /// The number of pixels to convert. + internal virtual void PackFromXyzwBytes(Span sourceBytes, Span destColors, int count) + { + Guard.MustBeSizedAtLeast(sourceBytes, count * 4, nameof(sourceBytes)); + Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors)); + + ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference(); + ref TPixel destRef = ref destColors.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + int i4 = i * 4; + ref TPixel dp = ref Unsafe.Add(ref destRef, i); + dp.PackFromBytes( + Unsafe.Add(ref sourceRef, i4), + Unsafe.Add(ref sourceRef, i4 + 1), + Unsafe.Add(ref sourceRef, i4 + 2), + Unsafe.Add(ref sourceRef, i4 + 3)); + } + } + + /// + /// Bulk version of + /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. + internal virtual void ToXyzwBytes(Span sourceColors, Span destBytes, int count) + { + Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors)); + Guard.MustBeSizedAtLeast(destBytes, count * 4, nameof(destBytes)); + + ref TPixel sourceRef = ref sourceColors.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceRef, i); + sp.ToXyzwBytes(destBytes, i * 4); + } + } + + /// + /// Bulk version of that converts data in . + /// + /// The to the source bytes. + /// The to the destination colors. + /// The number of pixels to convert. + internal virtual void PackFromZyxBytes(Span sourceBytes, Span destColors, int count) + { + Guard.MustBeSizedAtLeast(sourceBytes, count * 3, nameof(sourceBytes)); + Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors)); + + ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference(); + ref TPixel destRef = ref destColors.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + int i3 = i * 3; + ref TPixel dp = ref Unsafe.Add(ref destRef, i); + dp.PackFromBytes( + Unsafe.Add(ref sourceRef, i3 + 2), + Unsafe.Add(ref sourceRef, i3 + 1), + Unsafe.Add(ref sourceRef, i3), + 255); + } + } + + /// + /// Bulk version of . + /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. + internal virtual void ToZyxBytes(Span sourceColors, Span destBytes, int count) + { + Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors)); + Guard.MustBeSizedAtLeast(destBytes, count * 3, nameof(destBytes)); + + ref TPixel sourceRef = ref sourceColors.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceRef, i); + sp.ToZyxBytes(destBytes, i * 3); + } + } + + /// + /// Bulk version of that converts data in . + /// + /// The to the source bytes. + /// The to the destination colors. + /// The number of pixels to convert. + internal virtual void PackFromZyxwBytes(Span sourceBytes, Span destColors, int count) + { + Guard.MustBeSizedAtLeast(sourceBytes, count * 4, nameof(sourceBytes)); + Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors)); + + ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference(); + ref TPixel destRef = ref destColors.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + int i4 = i * 4; + ref TPixel dp = ref Unsafe.Add(ref destRef, i); + dp.PackFromBytes( + Unsafe.Add(ref sourceRef, i4 + 2), + Unsafe.Add(ref sourceRef, i4 + 1), + Unsafe.Add(ref sourceRef, i4), + Unsafe.Add(ref sourceRef, i4 + 3)); + } + } + + /// + /// Bulk version of . + /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. + internal virtual void ToZyxwBytes(Span sourceColors, Span destBytes, int count) + { + Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors)); + Guard.MustBeSizedAtLeast(destBytes, count * 4, nameof(destBytes)); + + ref TPixel sourceRef = ref sourceColors.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceRef, i); + sp.ToZyxwBytes(destBytes, i * 4); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/README.md b/src/ImageSharp/PixelFormats/README.md new file mode 100644 index 0000000000..c7aa012954 --- /dev/null +++ b/src/ImageSharp/PixelFormats/README.md @@ -0,0 +1,7 @@ +Pixel formats adapted and extended from: + +https://github.com/MonoGame/MonoGame + +Rgba32 is our default format. As such it positioned within the ImageSharp root namespace to ensure visibility of the format. + +All other pixel formats should be positioned within ImageSharp.PixelFormats to reduce intellisense burden. \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/Rg32.cs b/src/ImageSharp/PixelFormats/Rg32.cs similarity index 92% rename from src/ImageSharp/Colors/PackedPixel/Rg32.cs rename to src/ImageSharp/PixelFormats/Rg32.cs index f8486f7f29..ea7d8729b4 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rg32.cs +++ b/src/ImageSharp/PixelFormats/Rg32.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -11,6 +11,9 @@ namespace ImageSharp /// /// Packed pixel type containing two 16-bit unsigned normalized values ranging from 0 to 1. + /// + /// Ranges from <0, 0, 0, 1> to <1, 1, 0, 1> in vector form. + /// /// public struct Rg32 : IPixel, IPackedVector { @@ -73,7 +76,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// /// Expands the packed representation into a . @@ -111,7 +114,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -122,7 +125,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -134,7 +137,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -145,7 +148,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs b/src/ImageSharp/PixelFormats/Rgba1010102.cs similarity index 93% rename from src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs rename to src/ImageSharp/PixelFormats/Rgba1010102.cs index 65a5e7a5f6..ca7b74fbbd 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/Rgba1010102.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -12,6 +12,9 @@ namespace ImageSharp /// /// Packed vector type containing unsigned normalized values ranging from 0 to 1. /// The x, y and z components use 10 bits, and the w component uses 2 bits. + /// + /// Ranges from <0, 0, 0, 0> to <1, 1, 1, 1> in vector form. + /// /// public struct Rgba1010102 : IPixel, IPackedVector { @@ -76,7 +79,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -105,7 +108,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -116,7 +119,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -128,7 +131,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -139,7 +142,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; diff --git a/src/ImageSharp/Colors/ColorspaceTransforms.cs b/src/ImageSharp/PixelFormats/Rgba32.ColorspaceTransforms.cs similarity index 79% rename from src/ImageSharp/Colors/ColorspaceTransforms.cs rename to src/ImageSharp/PixelFormats/Rgba32.ColorspaceTransforms.cs index 74f5cb7175..1dc2292b12 100644 --- a/src/ImageSharp/Colors/ColorspaceTransforms.cs +++ b/src/ImageSharp/PixelFormats/Rgba32.ColorspaceTransforms.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -9,54 +9,49 @@ namespace ImageSharp using System.Numerics; using Colors.Spaces; - /// - /// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255. - /// The color components are stored in red, green, blue, and alpha order. - /// - /// - /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, - /// as it avoids the need to create new values for modification operations. - /// - public partial struct Color + /// + /// Provides implicit colorspace transformation. + /// + public partial struct Rgba32 { /// - /// Allows the implicit conversion of an instance of to a + /// Allows the implicit conversion of an instance of to a /// . /// - /// The instance of to convert. + /// The instance of to convert. /// /// An instance of . /// - public static implicit operator Color(Bgra32 color) + public static implicit operator Rgba32(Bgra32 color) { - return new Color(color.R, color.G, color.B, color.A); + return new Rgba32(color.R, color.G, color.B, color.A); } /// /// Allows the implicit conversion of an instance of to a - /// . + /// . /// /// The instance of to convert. /// - /// An instance of . + /// An instance of . /// - public static implicit operator Color(Cmyk cmykColor) + public static implicit operator Rgba32(Cmyk cmykColor) { float r = (1 - cmykColor.C) * (1 - cmykColor.K); float g = (1 - cmykColor.M) * (1 - cmykColor.K); float b = (1 - cmykColor.Y) * (1 - cmykColor.K); - return new Color(r, g, b, 1); + return new Rgba32(r, g, b, 1); } /// /// Allows the implicit conversion of an instance of to a - /// . + /// . /// /// The instance of to convert. /// - /// An instance of . + /// An instance of . /// - public static implicit operator Color(YCbCr color) + public static implicit operator Rgba32(YCbCr color) { float y = color.Y; float cb = color.Cb - 128; @@ -66,18 +61,18 @@ namespace ImageSharp byte g = (byte)(y - (0.34414F * cb) - (0.71414F * cr)).Clamp(0, 255); byte b = (byte)(y + (1.772F * cb)).Clamp(0, 255); - return new Color(r, g, b); + return new Rgba32(r, g, b); } /// /// Allows the implicit conversion of an instance of to a - /// . + /// . /// /// The instance of to convert. /// - /// An instance of . + /// An instance of . /// - public static implicit operator Color(CieXyz color) + public static implicit operator Rgba32(CieXyz color) { float x = color.X / 100F; float y = color.Y / 100F; @@ -89,25 +84,25 @@ namespace ImageSharp float b = (x * 0.0557F) + (y * -0.2040F) + (z * 1.0570F); Vector4 vector = new Vector4(r, g, b, 1).Compress(); - return new Color(vector); + return new Rgba32(vector); } /// /// Allows the implicit conversion of an instance of to a - /// . + /// . /// /// The instance of to convert. /// - /// An instance of . + /// An instance of . /// - public static implicit operator Color(Hsv color) + public static implicit operator Rgba32(Hsv color) { float s = color.S; float v = color.V; if (MathF.Abs(s) < Constants.Epsilon) { - return new Color(v, v, v, 1); + return new Rgba32(v, v, v, 1); } float h = (MathF.Abs(color.H - 360) < Constants.Epsilon) ? 0 : color.H / 60; @@ -158,18 +153,18 @@ namespace ImageSharp break; } - return new Color(r, g, b, 1); + return new Rgba32(r, g, b, 1); } /// /// Allows the implicit conversion of an instance of to a - /// . + /// . /// /// The instance of to convert. /// - /// An instance of . + /// An instance of . /// - public static implicit operator Color(Hsl color) + public static implicit operator Rgba32(Hsl color) { float rangedH = color.H / 360F; float r = 0; @@ -195,18 +190,18 @@ namespace ImageSharp } } - return new Color(r, g, b, 1); + return new Rgba32(r, g, b, 1); } /// /// Allows the implicit conversion of an instance of to a - /// . + /// . /// /// The instance of to convert. /// - /// An instance of . + /// An instance of . /// - public static implicit operator Color(CieLab cieLabColor) + public static implicit operator Rgba32(CieLab cieLabColor) { // First convert back to XYZ... float y = (cieLabColor.L + 16F) / 116F; @@ -229,7 +224,7 @@ namespace ImageSharp float g = (x * -0.9689F) + (y * 1.8758F) + (z * 0.0415F); float b = (x * 0.0557F) + (y * -0.2040F) + (z * 1.0570F); - return new Color(new Vector4(r, g, b, 1F).Compress()); + return new Rgba32(new Vector4(r, g, b, 1F).Compress()); } /// diff --git a/src/ImageSharp/PixelFormats/Rgba32.Definitions.cs b/src/ImageSharp/PixelFormats/Rgba32.Definitions.cs new file mode 100644 index 0000000000..ab4c2ea606 --- /dev/null +++ b/src/ImageSharp/PixelFormats/Rgba32.Definitions.cs @@ -0,0 +1,725 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using ImageSharp.PixelFormats; + + /// + /// Provides standardized deifinitions for named colors. + /// + public partial struct Rgba32 + { + /// + /// Represents a matching the W3C definition that has an hex value of #F0F8FF. + /// + public static readonly Rgba32 AliceBlue = NamedColors.AliceBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #FAEBD7. + /// + public static readonly Rgba32 AntiqueWhite = NamedColors.AntiqueWhite; + + /// + /// Represents a matching the W3C definition that has an hex value of #00FFFF. + /// + public static readonly Rgba32 Aqua = NamedColors.Aqua; + + /// + /// Represents a matching the W3C definition that has an hex value of #7FFFD4. + /// + public static readonly Rgba32 Aquamarine = NamedColors.Aquamarine; + + /// + /// Represents a matching the W3C definition that has an hex value of #F0FFFF. + /// + public static readonly Rgba32 Azure = NamedColors.Azure; + + /// + /// Represents a matching the W3C definition that has an hex value of #F5F5DC. + /// + public static readonly Rgba32 Beige = NamedColors.Beige; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4C4. + /// + public static readonly Rgba32 Bisque = NamedColors.Bisque; + + /// + /// Represents a matching the W3C definition that has an hex value of #000000. + /// + public static readonly Rgba32 Black = NamedColors.Black; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFEBCD. + /// + public static readonly Rgba32 BlanchedAlmond = NamedColors.BlanchedAlmond; + + /// + /// Represents a matching the W3C definition that has an hex value of #0000FF. + /// + public static readonly Rgba32 Blue = NamedColors.Blue; + + /// + /// Represents a matching the W3C definition that has an hex value of #8A2BE2. + /// + public static readonly Rgba32 BlueViolet = NamedColors.BlueViolet; + + /// + /// Represents a matching the W3C definition that has an hex value of #A52A2A. + /// + public static readonly Rgba32 Brown = NamedColors.Brown; + + /// + /// Represents a matching the W3C definition that has an hex value of #DEB887. + /// + public static readonly Rgba32 BurlyWood = NamedColors.BurlyWood; + + /// + /// Represents a matching the W3C definition that has an hex value of #5F9EA0. + /// + public static readonly Rgba32 CadetBlue = NamedColors.CadetBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #7FFF00. + /// + public static readonly Rgba32 Chartreuse = NamedColors.Chartreuse; + + /// + /// Represents a matching the W3C definition that has an hex value of #D2691E. + /// + public static readonly Rgba32 Chocolate = NamedColors.Chocolate; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF7F50. + /// + public static readonly Rgba32 Coral = NamedColors.Coral; + + /// + /// Represents a matching the W3C definition that has an hex value of #6495ED. + /// + public static readonly Rgba32 CornflowerBlue = NamedColors.CornflowerBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF8DC. + /// + public static readonly Rgba32 Cornsilk = NamedColors.Cornsilk; + + /// + /// Represents a matching the W3C definition that has an hex value of #DC143C. + /// + public static readonly Rgba32 Crimson = NamedColors.Crimson; + + /// + /// Represents a matching the W3C definition that has an hex value of #00FFFF. + /// + public static readonly Rgba32 Cyan = NamedColors.Cyan; + + /// + /// Represents a matching the W3C definition that has an hex value of #00008B. + /// + public static readonly Rgba32 DarkBlue = NamedColors.DarkBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #008B8B. + /// + public static readonly Rgba32 DarkCyan = NamedColors.DarkCyan; + + /// + /// Represents a matching the W3C definition that has an hex value of #B8860B. + /// + public static readonly Rgba32 DarkGoldenrod = NamedColors.DarkGoldenrod; + + /// + /// Represents a matching the W3C definition that has an hex value of #A9A9A9. + /// + public static readonly Rgba32 DarkGray = NamedColors.DarkGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #006400. + /// + public static readonly Rgba32 DarkGreen = NamedColors.DarkGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #BDB76B. + /// + public static readonly Rgba32 DarkKhaki = NamedColors.DarkKhaki; + + /// + /// Represents a matching the W3C definition that has an hex value of #8B008B. + /// + public static readonly Rgba32 DarkMagenta = NamedColors.DarkMagenta; + + /// + /// Represents a matching the W3C definition that has an hex value of #556B2F. + /// + public static readonly Rgba32 DarkOliveGreen = NamedColors.DarkOliveGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF8C00. + /// + public static readonly Rgba32 DarkOrange = NamedColors.DarkOrange; + + /// + /// Represents a matching the W3C definition that has an hex value of #9932CC. + /// + public static readonly Rgba32 DarkOrchid = NamedColors.DarkOrchid; + + /// + /// Represents a matching the W3C definition that has an hex value of #8B0000. + /// + public static readonly Rgba32 DarkRed = NamedColors.DarkRed; + + /// + /// Represents a matching the W3C definition that has an hex value of #E9967A. + /// + public static readonly Rgba32 DarkSalmon = NamedColors.DarkSalmon; + + /// + /// Represents a matching the W3C definition that has an hex value of #8FBC8B. + /// + public static readonly Rgba32 DarkSeaGreen = NamedColors.DarkSeaGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #483D8B. + /// + public static readonly Rgba32 DarkSlateBlue = NamedColors.DarkSlateBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #2F4F4F. + /// + public static readonly Rgba32 DarkSlateGray = NamedColors.DarkSlateGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #00CED1. + /// + public static readonly Rgba32 DarkTurquoise = NamedColors.DarkTurquoise; + + /// + /// Represents a matching the W3C definition that has an hex value of #9400D3. + /// + public static readonly Rgba32 DarkViolet = NamedColors.DarkViolet; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF1493. + /// + public static readonly Rgba32 DeepPink = NamedColors.DeepPink; + + /// + /// Represents a matching the W3C definition that has an hex value of #00BFFF. + /// + public static readonly Rgba32 DeepSkyBlue = NamedColors.DeepSkyBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #696969. + /// + public static readonly Rgba32 DimGray = NamedColors.DimGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #1E90FF. + /// + public static readonly Rgba32 DodgerBlue = NamedColors.DodgerBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #B22222. + /// + public static readonly Rgba32 Firebrick = NamedColors.Firebrick; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFAF0. + /// + public static readonly Rgba32 FloralWhite = NamedColors.FloralWhite; + + /// + /// Represents a matching the W3C definition that has an hex value of #228B22. + /// + public static readonly Rgba32 ForestGreen = NamedColors.ForestGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF00FF. + /// + public static readonly Rgba32 Fuchsia = NamedColors.Fuchsia; + + /// + /// Represents a matching the W3C definition that has an hex value of #DCDCDC. + /// + public static readonly Rgba32 Gainsboro = NamedColors.Gainsboro; + + /// + /// Represents a matching the W3C definition that has an hex value of #F8F8FF. + /// + public static readonly Rgba32 GhostWhite = NamedColors.GhostWhite; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFD700. + /// + public static readonly Rgba32 Gold = NamedColors.Gold; + + /// + /// Represents a matching the W3C definition that has an hex value of #DAA520. + /// + public static readonly Rgba32 Goldenrod = NamedColors.Goldenrod; + + /// + /// Represents a matching the W3C definition that has an hex value of #808080. + /// + public static readonly Rgba32 Gray = NamedColors.Gray; + + /// + /// Represents a matching the W3C definition that has an hex value of #008000. + /// + public static readonly Rgba32 Green = NamedColors.Green; + + /// + /// Represents a matching the W3C definition that has an hex value of #ADFF2F. + /// + public static readonly Rgba32 GreenYellow = NamedColors.GreenYellow; + + /// + /// Represents a matching the W3C definition that has an hex value of #F0FFF0. + /// + public static readonly Rgba32 Honeydew = NamedColors.Honeydew; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF69B4. + /// + public static readonly Rgba32 HotPink = NamedColors.HotPink; + + /// + /// Represents a matching the W3C definition that has an hex value of #CD5C5C. + /// + public static readonly Rgba32 IndianRed = NamedColors.IndianRed; + + /// + /// Represents a matching the W3C definition that has an hex value of #4B0082. + /// + public static readonly Rgba32 Indigo = NamedColors.Indigo; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFF0. + /// + public static readonly Rgba32 Ivory = NamedColors.Ivory; + + /// + /// Represents a matching the W3C definition that has an hex value of #F0E68C. + /// + public static readonly Rgba32 Khaki = NamedColors.Khaki; + + /// + /// Represents a matching the W3C definition that has an hex value of #E6E6FA. + /// + public static readonly Rgba32 Lavender = NamedColors.Lavender; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF0F5. + /// + public static readonly Rgba32 LavenderBlush = NamedColors.LavenderBlush; + + /// + /// Represents a matching the W3C definition that has an hex value of #7CFC00. + /// + public static readonly Rgba32 LawnGreen = NamedColors.LawnGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFACD. + /// + public static readonly Rgba32 LemonChiffon = NamedColors.LemonChiffon; + + /// + /// Represents a matching the W3C definition that has an hex value of #ADD8E6. + /// + public static readonly Rgba32 LightBlue = NamedColors.LightBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #F08080. + /// + public static readonly Rgba32 LightCoral = NamedColors.LightCoral; + + /// + /// Represents a matching the W3C definition that has an hex value of #E0FFFF. + /// + public static readonly Rgba32 LightCyan = NamedColors.LightCyan; + + /// + /// Represents a matching the W3C definition that has an hex value of #FAFAD2. + /// + public static readonly Rgba32 LightGoldenrodYellow = NamedColors.LightGoldenrodYellow; + + /// + /// Represents a matching the W3C definition that has an hex value of #D3D3D3. + /// + public static readonly Rgba32 LightGray = NamedColors.LightGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #90EE90. + /// + public static readonly Rgba32 LightGreen = NamedColors.LightGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFB6C1. + /// + public static readonly Rgba32 LightPink = NamedColors.LightPink; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFA07A. + /// + public static readonly Rgba32 LightSalmon = NamedColors.LightSalmon; + + /// + /// Represents a matching the W3C definition that has an hex value of #20B2AA. + /// + public static readonly Rgba32 LightSeaGreen = NamedColors.LightSeaGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #87CEFA. + /// + public static readonly Rgba32 LightSkyBlue = NamedColors.LightSkyBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #778899. + /// + public static readonly Rgba32 LightSlateGray = NamedColors.LightSlateGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #B0C4DE. + /// + public static readonly Rgba32 LightSteelBlue = NamedColors.LightSteelBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFE0. + /// + public static readonly Rgba32 LightYellow = NamedColors.LightYellow; + + /// + /// Represents a matching the W3C definition that has an hex value of #00FF00. + /// + public static readonly Rgba32 Lime = NamedColors.Lime; + + /// + /// Represents a matching the W3C definition that has an hex value of #32CD32. + /// + public static readonly Rgba32 LimeGreen = NamedColors.LimeGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FAF0E6. + /// + public static readonly Rgba32 Linen = NamedColors.Linen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF00FF. + /// + public static readonly Rgba32 Magenta = NamedColors.Magenta; + + /// + /// Represents a matching the W3C definition that has an hex value of #800000. + /// + public static readonly Rgba32 Maroon = NamedColors.Maroon; + + /// + /// Represents a matching the W3C definition that has an hex value of #66CDAA. + /// + public static readonly Rgba32 MediumAquamarine = NamedColors.MediumAquamarine; + + /// + /// Represents a matching the W3C definition that has an hex value of #0000CD. + /// + public static readonly Rgba32 MediumBlue = NamedColors.MediumBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #BA55D3. + /// + public static readonly Rgba32 MediumOrchid = NamedColors.MediumOrchid; + + /// + /// Represents a matching the W3C definition that has an hex value of #9370DB. + /// + public static readonly Rgba32 MediumPurple = NamedColors.MediumPurple; + + /// + /// Represents a matching the W3C definition that has an hex value of #3CB371. + /// + public static readonly Rgba32 MediumSeaGreen = NamedColors.MediumSeaGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #7B68EE. + /// + public static readonly Rgba32 MediumSlateBlue = NamedColors.MediumSlateBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #00FA9A. + /// + public static readonly Rgba32 MediumSpringGreen = NamedColors.MediumSpringGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #48D1CC. + /// + public static readonly Rgba32 MediumTurquoise = NamedColors.MediumTurquoise; + + /// + /// Represents a matching the W3C definition that has an hex value of #C71585. + /// + public static readonly Rgba32 MediumVioletRed = NamedColors.MediumVioletRed; + + /// + /// Represents a matching the W3C definition that has an hex value of #191970. + /// + public static readonly Rgba32 MidnightBlue = NamedColors.MidnightBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #F5FFFA. + /// + public static readonly Rgba32 MintCream = NamedColors.MintCream; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4E1. + /// + public static readonly Rgba32 MistyRose = NamedColors.MistyRose; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4B5. + /// + public static readonly Rgba32 Moccasin = NamedColors.Moccasin; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFDEAD. + /// + public static readonly Rgba32 NavajoWhite = NamedColors.NavajoWhite; + + /// + /// Represents a matching the W3C definition that has an hex value of #000080. + /// + public static readonly Rgba32 Navy = NamedColors.Navy; + + /// + /// Represents a matching the W3C definition that has an hex value of #FDF5E6. + /// + public static readonly Rgba32 OldLace = NamedColors.OldLace; + + /// + /// Represents a matching the W3C definition that has an hex value of #808000. + /// + public static readonly Rgba32 Olive = NamedColors.Olive; + + /// + /// Represents a matching the W3C definition that has an hex value of #6B8E23. + /// + public static readonly Rgba32 OliveDrab = NamedColors.OliveDrab; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFA500. + /// + public static readonly Rgba32 Orange = NamedColors.Orange; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF4500. + /// + public static readonly Rgba32 OrangeRed = NamedColors.OrangeRed; + + /// + /// Represents a matching the W3C definition that has an hex value of #DA70D6. + /// + public static readonly Rgba32 Orchid = NamedColors.Orchid; + + /// + /// Represents a matching the W3C definition that has an hex value of #EEE8AA. + /// + public static readonly Rgba32 PaleGoldenrod = NamedColors.PaleGoldenrod; + + /// + /// Represents a matching the W3C definition that has an hex value of #98FB98. + /// + public static readonly Rgba32 PaleGreen = NamedColors.PaleGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #AFEEEE. + /// + public static readonly Rgba32 PaleTurquoise = NamedColors.PaleTurquoise; + + /// + /// Represents a matching the W3C definition that has an hex value of #DB7093. + /// + public static readonly Rgba32 PaleVioletRed = NamedColors.PaleVioletRed; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFEFD5. + /// + public static readonly Rgba32 PapayaWhip = NamedColors.PapayaWhip; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFDAB9. + /// + public static readonly Rgba32 PeachPuff = NamedColors.PeachPuff; + + /// + /// Represents a matching the W3C definition that has an hex value of #CD853F. + /// + public static readonly Rgba32 Peru = NamedColors.Peru; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFC0CB. + /// + public static readonly Rgba32 Pink = NamedColors.Pink; + + /// + /// Represents a matching the W3C definition that has an hex value of #DDA0DD. + /// + public static readonly Rgba32 Plum = NamedColors.Plum; + + /// + /// Represents a matching the W3C definition that has an hex value of #B0E0E6. + /// + public static readonly Rgba32 PowderBlue = NamedColors.PowderBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #800080. + /// + public static readonly Rgba32 Purple = NamedColors.Purple; + + /// + /// Represents a matching the W3C definition that has an hex value of #663399. + /// + public static readonly Rgba32 RebeccaPurple = NamedColors.RebeccaPurple; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF0000. + /// + public static readonly Rgba32 Red = NamedColors.Red; + + /// + /// Represents a matching the W3C definition that has an hex value of #BC8F8F. + /// + public static readonly Rgba32 RosyBrown = NamedColors.RosyBrown; + + /// + /// Represents a matching the W3C definition that has an hex value of #4169E1. + /// + public static readonly Rgba32 RoyalBlue = NamedColors.RoyalBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #8B4513. + /// + public static readonly Rgba32 SaddleBrown = NamedColors.SaddleBrown; + + /// + /// Represents a matching the W3C definition that has an hex value of #FA8072. + /// + public static readonly Rgba32 Salmon = NamedColors.Salmon; + + /// + /// Represents a matching the W3C definition that has an hex value of #F4A460. + /// + public static readonly Rgba32 SandyBrown = NamedColors.SandyBrown; + + /// + /// Represents a matching the W3C definition that has an hex value of #2E8B57. + /// + public static readonly Rgba32 SeaGreen = NamedColors.SeaGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF5EE. + /// + public static readonly Rgba32 SeaShell = NamedColors.SeaShell; + + /// + /// Represents a matching the W3C definition that has an hex value of #A0522D. + /// + public static readonly Rgba32 Sienna = NamedColors.Sienna; + + /// + /// Represents a matching the W3C definition that has an hex value of #C0C0C0. + /// + public static readonly Rgba32 Silver = NamedColors.Silver; + + /// + /// Represents a matching the W3C definition that has an hex value of #87CEEB. + /// + public static readonly Rgba32 SkyBlue = NamedColors.SkyBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #6A5ACD. + /// + public static readonly Rgba32 SlateBlue = NamedColors.SlateBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #708090. + /// + public static readonly Rgba32 SlateGray = NamedColors.SlateGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFAFA. + /// + public static readonly Rgba32 Snow = NamedColors.Snow; + + /// + /// Represents a matching the W3C definition that has an hex value of #00FF7F. + /// + public static readonly Rgba32 SpringGreen = NamedColors.SpringGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #4682B4. + /// + public static readonly Rgba32 SteelBlue = NamedColors.SteelBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #D2B48C. + /// + public static readonly Rgba32 Tan = NamedColors.Tan; + + /// + /// Represents a matching the W3C definition that has an hex value of #008080. + /// + public static readonly Rgba32 Teal = NamedColors.Teal; + + /// + /// Represents a matching the W3C definition that has an hex value of #D8BFD8. + /// + public static readonly Rgba32 Thistle = NamedColors.Thistle; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF6347. + /// + public static readonly Rgba32 Tomato = NamedColors.Tomato; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFFF. + /// + public static readonly Rgba32 Transparent = NamedColors.Transparent; + + /// + /// Represents a matching the W3C definition that has an hex value of #40E0D0. + /// + public static readonly Rgba32 Turquoise = NamedColors.Turquoise; + + /// + /// Represents a matching the W3C definition that has an hex value of #EE82EE. + /// + public static readonly Rgba32 Violet = NamedColors.Violet; + + /// + /// Represents a matching the W3C definition that has an hex value of #F5DEB3. + /// + public static readonly Rgba32 Wheat = NamedColors.Wheat; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFFF. + /// + public static readonly Rgba32 White = NamedColors.White; + + /// + /// Represents a matching the W3C definition that has an hex value of #F5F5F5. + /// + public static readonly Rgba32 WhiteSmoke = NamedColors.WhiteSmoke; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFF00. + /// + public static readonly Rgba32 Yellow = NamedColors.Yellow; + + /// + /// Represents a matching the W3C definition that has an hex value of #9ACD32. + /// + public static readonly Rgba32 YellowGreen = NamedColors.YellowGreen; + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs new file mode 100644 index 0000000000..2ba663603e --- /dev/null +++ b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs @@ -0,0 +1,309 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Rgba32 + { + /// + /// implementation optimized for . + /// + internal class PixelOperations : PixelOperations + { + /// + /// SIMD optimized bulk implementation of + /// that works only with `count` divisible by . + /// + /// The to the source colors. + /// The to the dstination vectors. + /// The number of pixels to convert. + /// + /// Implementation adapted from: + /// + /// http://stackoverflow.com/a/5362789 + /// + /// TODO: We can replace this implementation in the future using new Vector API-s: + /// + /// https://github.com/dotnet/corefx/issues/15957 + /// + /// + internal static void ToVector4SimdAligned(Span sourceColors, Span destVectors, int count) + { + if (!Vector.IsHardwareAccelerated) + { + throw new InvalidOperationException( + "Rgba32.PixelOperations.ToVector4SimdAligned() should not be called when Vector.IsHardwareAccelerated == false!"); + } + + DebugGuard.IsTrue( + count % Vector.Count == 0, + nameof(count), + "Argument 'count' should divisible by Vector.Count!"); + + Vector bVec = new Vector(256.0f / 255.0f); + Vector magicFloat = new Vector(32768.0f); + Vector magicInt = new Vector(1191182336); // reinterpreded value of 32768.0f + Vector mask = new Vector(255); + + int unpackedRawCount = count * 4; + + ref uint sourceBase = ref Unsafe.As(ref sourceColors.DangerousGetPinnableReference()); + ref UnpackedRGBA destBaseAsUnpacked = ref Unsafe.As(ref destVectors.DangerousGetPinnableReference()); + ref Vector destBaseAsUInt = ref Unsafe.As>(ref destBaseAsUnpacked); + ref Vector destBaseAsFloat = ref Unsafe.As>(ref destBaseAsUnpacked); + + for (int i = 0; i < count; i++) + { + uint sVal = Unsafe.Add(ref sourceBase, i); + ref UnpackedRGBA dst = ref Unsafe.Add(ref destBaseAsUnpacked, i); + + // This call is the bottleneck now: + dst.Load(sVal); + } + + int numOfVectors = unpackedRawCount / Vector.Count; + + for (int i = 0; i < numOfVectors; i++) + { + Vector vi = Unsafe.Add(ref destBaseAsUInt, i); + + vi &= mask; + vi |= magicInt; + + Vector vf = Vector.AsVectorSingle(vi); + vf = (vf - magicFloat) * bVec; + + Unsafe.Add(ref destBaseAsFloat, i) = vf; + } + } + + /// + internal override void ToVector4(Span sourceColors, Span destVectors, int count) + { + Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors)); + Guard.MustBeSizedAtLeast(destVectors, count, nameof(destVectors)); + + if (count < 256 || !Vector.IsHardwareAccelerated) + { + // Doesn't worth to bother with SIMD: + base.ToVector4(sourceColors, destVectors, count); + return; + } + + int remainder = count % Vector.Count; + + int alignedCount = count - remainder; + + if (alignedCount > 0) + { + ToVector4SimdAligned(sourceColors, destVectors, alignedCount); + } + + if (remainder > 0) + { + sourceColors = sourceColors.Slice(alignedCount); + destVectors = destVectors.Slice(alignedCount); + base.ToVector4(sourceColors, destVectors, remainder); + } + } + + /// + internal override void PackFromXyzBytes(Span sourceBytes, Span destColors, int count) + { + Guard.MustBeSizedAtLeast(sourceBytes, count * 3, nameof(sourceBytes)); + Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors)); + + ref RGB24 sourceRef = ref Unsafe.As(ref sourceBytes.DangerousGetPinnableReference()); + ref Rgba32 destRef = ref destColors.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref RGB24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); + + Unsafe.As(ref dp) = sp; + dp.A = 255; + } + } + + /// + internal override void ToXyzBytes(Span sourceColors, Span destBytes, int count) + { + Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors)); + Guard.MustBeSizedAtLeast(destBytes, count * 3, nameof(destBytes)); + + ref Rgba32 sourceRef = ref sourceColors.DangerousGetPinnableReference(); + ref RGB24 destRef = ref Unsafe.As(ref destBytes.DangerousGetPinnableReference()); + + for (int i = 0; i < count; i++) + { + ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); + ref RGB24 dp = ref Unsafe.Add(ref destRef, i); + + dp = Unsafe.As(ref sp); + } + } + + /// + internal override unsafe void PackFromXyzwBytes(Span sourceBytes, Span destColors, int count) + { + Guard.MustBeSizedAtLeast(sourceBytes, count * 4, nameof(sourceBytes)); + Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors)); + + SpanHelper.Copy(sourceBytes, destColors.AsBytes(), count * sizeof(Rgba32)); + } + + /// + internal override unsafe void ToXyzwBytes(Span sourceColors, Span destBytes, int count) + { + Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors)); + Guard.MustBeSizedAtLeast(destBytes, count * 4, nameof(destBytes)); + + SpanHelper.Copy(sourceColors.AsBytes(), destBytes, count * sizeof(Rgba32)); + } + + /// + internal override void PackFromZyxBytes(Span sourceBytes, Span destColors, int count) + { + Guard.MustBeSizedAtLeast(sourceBytes, count * 3, nameof(sourceBytes)); + Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors)); + + ref RGB24 sourceRef = ref Unsafe.As(ref sourceBytes.DangerousGetPinnableReference()); + ref Rgba32 destRef = ref destColors.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref RGB24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); + + Unsafe.As(ref dp) = sp.ToZyx(); + dp.A = 255; + } + } + + /// + internal override void ToZyxBytes(Span sourceColors, Span destBytes, int count) + { + Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors)); + Guard.MustBeSizedAtLeast(destBytes, count * 3, nameof(destBytes)); + + ref Rgba32 sourceRef = ref sourceColors.DangerousGetPinnableReference(); + ref RGB24 destRef = ref Unsafe.As(ref destBytes.DangerousGetPinnableReference()); + + for (int i = 0; i < count; i++) + { + ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); + ref RGB24 dp = ref Unsafe.Add(ref destRef, i); + + dp = Unsafe.As(ref sp).ToZyx(); + } + } + + /// + internal override void PackFromZyxwBytes(Span sourceBytes, Span destColors, int count) + { + Guard.MustBeSizedAtLeast(sourceBytes, count * 4, nameof(sourceBytes)); + Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors)); + + ref RGBA32 sourceRef = ref Unsafe.As(ref sourceBytes.DangerousGetPinnableReference()); + ref Rgba32 destRef = ref destColors.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref RGBA32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); + RGBA32 zyxw = sp.ToZyxw(); + dp = Unsafe.As(ref zyxw); + } + } + + /// + internal override void ToZyxwBytes(Span sourceColors, Span destBytes, int count) + { + Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors)); + Guard.MustBeSizedAtLeast(destBytes, count * 4, nameof(destBytes)); + + ref Rgba32 sourceRef = ref sourceColors.DangerousGetPinnableReference(); + ref RGBA32 destRef = ref Unsafe.As(ref destBytes.DangerousGetPinnableReference()); + + for (int i = 0; i < count; i++) + { + ref RGBA32 sp = ref Unsafe.As(ref Unsafe.Add(ref sourceRef, i)); + ref RGBA32 dp = ref Unsafe.Add(ref destRef, i); + dp = sp.ToZyxw(); + } + } + + /// + /// Helper struct to manipulate 3-byte RGB data. + /// + [StructLayout(LayoutKind.Sequential)] + private struct RGB24 + { + private byte x; + + private byte y; + + private byte z; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RGB24 ToZyx() => new RGB24 { x = this.z, y = this.y, z = this.x }; + } + + /// + /// Helper struct to manipulate 4-byte RGBA data. + /// + [StructLayout(LayoutKind.Sequential)] + private struct RGBA32 + { + private byte x; + + private byte y; + + private byte z; + + private byte w; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RGBA32 ToZyxw() => new RGBA32 { x = this.z, y = this.y, z = this.x, w = this.w }; + } + + /// + /// Value type to store -s unpacked into multiple -s. + /// + [StructLayout(LayoutKind.Sequential)] + private struct UnpackedRGBA + { + private uint r; + + private uint g; + + private uint b; + + private uint a; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Load(uint p) + { + this.r = p; + this.g = p >> GreenShift; + this.b = p >> BlueShift; + this.a = p >> AlphaShift; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/PixelFormats/Rgba32.cs similarity index 61% rename from src/ImageSharp/Colors/Color.cs rename to src/ImageSharp/PixelFormats/Rgba32.cs index 5977309373..9b82a37010 100644 --- a/src/ImageSharp/Colors/Color.cs +++ b/src/ImageSharp/PixelFormats/Rgba32.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -6,20 +6,56 @@ namespace ImageSharp { using System; - using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + using ImageSharp.PixelFormats; /// /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. /// The color components are stored in red, green, blue, and alpha order. + /// + /// Ranges from <0, 0, 0, 0> to <1, 1, 1, 1> in vector form. + /// /// /// /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, /// as it avoids the need to create new values for modification operations. /// - public partial struct Color : IPixel, IPackedVector + [StructLayout(LayoutKind.Explicit)] + public partial struct Rgba32 : IPixel, IPackedVector { + /// + /// Gets or sets the red component. + /// + [FieldOffset(0)] + public byte R; + + /// + /// Gets or sets the green component. + /// + [FieldOffset(1)] + public byte G; + + /// + /// Gets or sets the blue component. + /// + [FieldOffset(2)] + public byte B; + + /// + /// Gets or sets the alpha component. + /// + [FieldOffset(3)] + public byte A; + + /// + /// The packed representation of the value. + /// + [FieldOffset(0)] + public uint Rgba; + /// /// The shift count for the red component /// @@ -51,208 +87,136 @@ namespace ImageSharp private static readonly Vector4 Half = new Vector4(0.5F); /// - /// The packed value. - /// - private uint packedValue; - - /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The red component. /// The green component. /// The blue component. /// The alpha component. - public Color(byte r, byte g, byte b, byte a = 255) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgba32(byte r, byte g, byte b, byte a = 255) + : this() { - this.packedValue = Pack(r, g, b, a); + this.R = r; + this.G = g; + this.B = b; + this.A = a; } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// The red component. /// The green component. /// The blue component. /// The alpha component. - public Color(float r, float g, float b, float a = 1) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgba32(float r, float g, float b, float a = 1) + : this() { - this.packedValue = Pack(r, g, b, a); + this.Pack(r, g, b, a); } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// /// The vector containing the components for the packed vector. /// - public Color(Vector3 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgba32(Vector3 vector) + : this() { - this.packedValue = Pack(ref vector); + this.Pack(ref vector); } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// /// The vector containing the components for the packed vector. /// - public Color(Vector4 vector) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgba32(Vector4 vector) + : this() { - this.packedValue = Pack(ref vector); + this = PackNew(ref vector); } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct. /// /// /// The packed value. /// - public Color(uint packed) - { - this.packedValue = packed; - } - - /// - /// Gets or sets the red component. - /// - public byte R - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return (byte)(this.packedValue >> RedShift); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.packedValue = this.packedValue & 0xFFFFFF00 | (uint)value << RedShift; - } - } - - /// - /// Gets or sets the green component. - /// - public byte G - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return (byte)(this.packedValue >> GreenShift); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.packedValue = this.packedValue & 0xFFFF00FF | (uint)value << GreenShift; - } - } - - /// - /// Gets or sets the blue component. - /// - public byte B - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return (byte)(this.packedValue >> BlueShift); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.packedValue = this.packedValue & 0xFF00FFFF | (uint)value << BlueShift; - } - } - - /// - /// Gets or sets the alpha component. - /// - public byte A + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgba32(uint packed) + : this() { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return (byte)(this.packedValue >> AlphaShift); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.packedValue = this.packedValue & 0x00FFFFFF | (uint)value << AlphaShift; - } + this.Rgba = packed; } /// - public uint PackedValue - { - get - { - return this.packedValue; - } - - set - { - this.packedValue = value; - } - } + public uint PackedValue { get => this.Rgba; set => this.Rgba = value; } /// - /// Compares two objects for equality. + /// Compares two objects for equality. /// /// - /// The on the left side of the operand. + /// The on the left side of the operand. /// /// - /// The on the right side of the operand. + /// The on the right side of the operand. /// /// /// True if the parameter is equal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Color left, Color right) + public static bool operator ==(Rgba32 left, Rgba32 right) { - return left.packedValue == right.packedValue; + return left.Rgba == right.Rgba; } /// - /// Compares two objects for equality. + /// Compares two objects for equality. /// - /// The on the left side of the operand. - /// The on the right side of the operand. + /// The on the left side of the operand. + /// The on the right side of the operand. /// /// True if the parameter is not equal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Color left, Color right) + public static bool operator !=(Rgba32 left, Rgba32 right) { - return left.packedValue != right.packedValue; + return left.Rgba != right.Rgba; } /// - /// Creates a new instance of the struct. + /// Creates a new instance of the struct. /// /// /// The hexadecimal representation of the combined color components arranged /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. /// /// - /// The . + /// The . /// - public static Color FromHex(string hex) + public static Rgba32 FromHex(string hex) { - return ColorBuilder.FromHex(hex); + return ColorBuilder.FromHex(hex); } /// - public BulkPixelOperations CreateBulkOperations() => new Color.BulkOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) { - this.packedValue = Pack(x, y, z, w); + this.R = x; + this.G = y; + this.B = z; + this.A = w; } /// @@ -267,7 +231,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { bytes[startIndex] = this.R; bytes[startIndex + 1] = this.G; @@ -276,7 +240,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { bytes[startIndex] = this.R; bytes[startIndex + 1] = this.G; @@ -286,7 +250,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { bytes[startIndex] = this.B; bytes[startIndex + 1] = this.G; @@ -295,7 +259,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { bytes[startIndex] = this.B; bytes[startIndex + 1] = this.G; @@ -307,7 +271,7 @@ namespace ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) { - this.packedValue = Pack(ref vector); + this.Pack(ref vector); } /// @@ -320,14 +284,14 @@ namespace ImageSharp /// public override bool Equals(object obj) { - return (obj is Color) && this.Equals((Color)obj); + return (obj is Rgba32) && this.Equals((Rgba32)obj); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Color other) + public bool Equals(Rgba32 other) { - return this.packedValue == other.packedValue; + return this.Rgba == other.Rgba; } /// @@ -342,65 +306,85 @@ namespace ImageSharp /// public override int GetHashCode() { - return this.packedValue.GetHashCode(); + unchecked + { + int hashCode = this.R.GetHashCode(); + hashCode = (hashCode * 397) ^ this.G.GetHashCode(); + hashCode = (hashCode * 397) ^ this.B.GetHashCode(); + hashCode = (hashCode * 397) ^ this.A.GetHashCode(); + return hashCode; + } } /// - /// Packs a into a uint. + /// Packs the four floats into a . /// - /// The vector containing the values to pack. - /// The containing the packed values. + /// The x-component + /// The y-component + /// The z-component + /// The w-component + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(ref Vector4 vector) + private static uint Pack(byte x, byte y, byte z, byte w) { - vector *= MaxBytes; - vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - return (uint)(((byte)vector.X << RedShift) - | ((byte)vector.Y << GreenShift) - | ((byte)vector.Z << BlueShift) - | (byte)vector.W << AlphaShift); + return (uint)(x << RedShift | y << GreenShift | z << BlueShift | w << AlphaShift); } /// - /// Packs a into a uint. + /// Packs a into a color returning a new instance as a result. /// /// The vector containing the values to pack. - /// The containing the packed values. + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(ref Vector3 vector) + private static Rgba32 PackNew(ref Vector4 vector) { - Vector4 value = new Vector4(vector, 1); - return Pack(ref value); + vector *= MaxBytes; + vector += Half; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + + return new Rgba32((byte)vector.X, (byte)vector.Y, (byte)vector.Z, (byte)vector.W); } /// - /// Packs the four floats into a . + /// Packs the four floats into a color. /// /// The x-component /// The y-component /// The z-component /// The w-component - /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(float x, float y, float z, float w) + private void Pack(float x, float y, float z, float w) { Vector4 value = new Vector4(x, y, z, w); - return Pack(ref value); + this.Pack(ref value); } /// - /// Packs the four floats into a . + /// Packs a into a uint. /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - /// The + /// The vector containing the values to pack. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(byte x, byte y, byte z, byte w) + private void Pack(ref Vector3 vector) { - return (uint)(x << RedShift | y << GreenShift | z << BlueShift | w << AlphaShift); + Vector4 value = new Vector4(vector, 1); + this.Pack(ref value); + } + + /// + /// Packs a into a color. + /// + /// The vector containing the values to pack. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Pack(ref Vector4 vector) + { + vector *= MaxBytes; + vector += Half; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + + this.R = (byte)vector.X; + this.G = (byte)vector.Y; + this.B = (byte)vector.Z; + this.A = (byte)vector.W; } } } \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs b/src/ImageSharp/PixelFormats/Rgba64.cs similarity index 93% rename from src/ImageSharp/Colors/PackedPixel/Rgba64.cs rename to src/ImageSharp/PixelFormats/Rgba64.cs index becc4d072a..4178283686 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/Rgba64.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -11,6 +11,9 @@ namespace ImageSharp /// /// Packed pixel type containing four 16-bit unsigned normalized values ranging from 0 to 1. + /// + /// Ranges from <0, 0, 0, 0> to <1, 1, 1, 1> in vector form. + /// /// public struct Rgba64 : IPixel, IPackedVector { @@ -75,7 +78,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -104,7 +107,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -115,7 +118,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -127,7 +130,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; @@ -138,7 +141,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4() * 255F; diff --git a/src/ImageSharp/Colors/RgbaComponent.cs b/src/ImageSharp/PixelFormats/RgbaComponent.cs similarity index 100% rename from src/ImageSharp/Colors/RgbaComponent.cs rename to src/ImageSharp/PixelFormats/RgbaComponent.cs diff --git a/src/ImageSharp/PixelFormats/RgbaVector.Definitions.cs b/src/ImageSharp/PixelFormats/RgbaVector.Definitions.cs new file mode 100644 index 0000000000..dc965f9ff5 --- /dev/null +++ b/src/ImageSharp/PixelFormats/RgbaVector.Definitions.cs @@ -0,0 +1,723 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats +{ + /// + /// Provides operators and composition algorithms. + /// + public partial struct RgbaVector + { + /// + /// Represents a matching the W3C definition that has an hex value of #F0F8FF. + /// + public static readonly RgbaVector AliceBlue = NamedColors.AliceBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #FAEBD7. + /// + public static readonly RgbaVector AntiqueWhite = NamedColors.AntiqueWhite; + + /// + /// Represents a matching the W3C definition that has an hex value of #00FFFF. + /// + public static readonly RgbaVector Aqua = NamedColors.Aqua; + + /// + /// Represents a matching the W3C definition that has an hex value of #7FFFD4. + /// + public static readonly RgbaVector Aquamarine = NamedColors.Aquamarine; + + /// + /// Represents a matching the W3C definition that has an hex value of #F0FFFF. + /// + public static readonly RgbaVector Azure = NamedColors.Azure; + + /// + /// Represents a matching the W3C definition that has an hex value of #F5F5DC. + /// + public static readonly RgbaVector Beige = NamedColors.Beige; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4C4. + /// + public static readonly RgbaVector Bisque = NamedColors.Bisque; + + /// + /// Represents a matching the W3C definition that has an hex value of #000000. + /// + public static readonly RgbaVector Black = NamedColors.Black; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFEBCD. + /// + public static readonly RgbaVector BlanchedAlmond = NamedColors.BlanchedAlmond; + + /// + /// Represents a matching the W3C definition that has an hex value of #0000FF. + /// + public static readonly RgbaVector Blue = NamedColors.Blue; + + /// + /// Represents a matching the W3C definition that has an hex value of #8A2BE2. + /// + public static readonly RgbaVector BlueViolet = NamedColors.BlueViolet; + + /// + /// Represents a matching the W3C definition that has an hex value of #A52A2A. + /// + public static readonly RgbaVector Brown = NamedColors.Brown; + + /// + /// Represents a matching the W3C definition that has an hex value of #DEB887. + /// + public static readonly RgbaVector BurlyWood = NamedColors.BurlyWood; + + /// + /// Represents a matching the W3C definition that has an hex value of #5F9EA0. + /// + public static readonly RgbaVector CadetBlue = NamedColors.CadetBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #7FFF00. + /// + public static readonly RgbaVector Chartreuse = NamedColors.Chartreuse; + + /// + /// Represents a matching the W3C definition that has an hex value of #D2691E. + /// + public static readonly RgbaVector Chocolate = NamedColors.Chocolate; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF7F50. + /// + public static readonly RgbaVector Coral = NamedColors.Coral; + + /// + /// Represents a matching the W3C definition that has an hex value of #6495ED. + /// + public static readonly RgbaVector CornflowerBlue = NamedColors.CornflowerBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF8DC. + /// + public static readonly RgbaVector Cornsilk = NamedColors.Cornsilk; + + /// + /// Represents a matching the W3C definition that has an hex value of #DC143C. + /// + public static readonly RgbaVector Crimson = NamedColors.Crimson; + + /// + /// Represents a matching the W3C definition that has an hex value of #00FFFF. + /// + public static readonly RgbaVector Cyan = NamedColors.Cyan; + + /// + /// Represents a matching the W3C definition that has an hex value of #00008B. + /// + public static readonly RgbaVector DarkBlue = NamedColors.DarkBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #008B8B. + /// + public static readonly RgbaVector DarkCyan = NamedColors.DarkCyan; + + /// + /// Represents a matching the W3C definition that has an hex value of #B8860B. + /// + public static readonly RgbaVector DarkGoldenrod = NamedColors.DarkGoldenrod; + + /// + /// Represents a matching the W3C definition that has an hex value of #A9A9A9. + /// + public static readonly RgbaVector DarkGray = NamedColors.DarkGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #006400. + /// + public static readonly RgbaVector DarkGreen = NamedColors.DarkGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #BDB76B. + /// + public static readonly RgbaVector DarkKhaki = NamedColors.DarkKhaki; + + /// + /// Represents a matching the W3C definition that has an hex value of #8B008B. + /// + public static readonly RgbaVector DarkMagenta = NamedColors.DarkMagenta; + + /// + /// Represents a matching the W3C definition that has an hex value of #556B2F. + /// + public static readonly RgbaVector DarkOliveGreen = NamedColors.DarkOliveGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF8C00. + /// + public static readonly RgbaVector DarkOrange = NamedColors.DarkOrange; + + /// + /// Represents a matching the W3C definition that has an hex value of #9932CC. + /// + public static readonly RgbaVector DarkOrchid = NamedColors.DarkOrchid; + + /// + /// Represents a matching the W3C definition that has an hex value of #8B0000. + /// + public static readonly RgbaVector DarkRed = NamedColors.DarkRed; + + /// + /// Represents a matching the W3C definition that has an hex value of #E9967A. + /// + public static readonly RgbaVector DarkSalmon = NamedColors.DarkSalmon; + + /// + /// Represents a matching the W3C definition that has an hex value of #8FBC8B. + /// + public static readonly RgbaVector DarkSeaGreen = NamedColors.DarkSeaGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #483D8B. + /// + public static readonly RgbaVector DarkSlateBlue = NamedColors.DarkSlateBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #2F4F4F. + /// + public static readonly RgbaVector DarkSlateGray = NamedColors.DarkSlateGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #00CED1. + /// + public static readonly RgbaVector DarkTurquoise = NamedColors.DarkTurquoise; + + /// + /// Represents a matching the W3C definition that has an hex value of #9400D3. + /// + public static readonly RgbaVector DarkViolet = NamedColors.DarkViolet; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF1493. + /// + public static readonly RgbaVector DeepPink = NamedColors.DeepPink; + + /// + /// Represents a matching the W3C definition that has an hex value of #00BFFF. + /// + public static readonly RgbaVector DeepSkyBlue = NamedColors.DeepSkyBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #696969. + /// + public static readonly RgbaVector DimGray = NamedColors.DimGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #1E90FF. + /// + public static readonly RgbaVector DodgerBlue = NamedColors.DodgerBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #B22222. + /// + public static readonly RgbaVector Firebrick = NamedColors.Firebrick; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFAF0. + /// + public static readonly RgbaVector FloralWhite = NamedColors.FloralWhite; + + /// + /// Represents a matching the W3C definition that has an hex value of #228B22. + /// + public static readonly RgbaVector ForestGreen = NamedColors.ForestGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF00FF. + /// + public static readonly RgbaVector Fuchsia = NamedColors.Fuchsia; + + /// + /// Represents a matching the W3C definition that has an hex value of #DCDCDC. + /// + public static readonly RgbaVector Gainsboro = NamedColors.Gainsboro; + + /// + /// Represents a matching the W3C definition that has an hex value of #F8F8FF. + /// + public static readonly RgbaVector GhostWhite = NamedColors.GhostWhite; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFD700. + /// + public static readonly RgbaVector Gold = NamedColors.Gold; + + /// + /// Represents a matching the W3C definition that has an hex value of #DAA520. + /// + public static readonly RgbaVector Goldenrod = NamedColors.Goldenrod; + + /// + /// Represents a matching the W3C definition that has an hex value of #808080. + /// + public static readonly RgbaVector Gray = NamedColors.Gray; + + /// + /// Represents a matching the W3C definition that has an hex value of #008000. + /// + public static readonly RgbaVector Green = NamedColors.Green; + + /// + /// Represents a matching the W3C definition that has an hex value of #ADFF2F. + /// + public static readonly RgbaVector GreenYellow = NamedColors.GreenYellow; + + /// + /// Represents a matching the W3C definition that has an hex value of #F0FFF0. + /// + public static readonly RgbaVector Honeydew = NamedColors.Honeydew; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF69B4. + /// + public static readonly RgbaVector HotPink = NamedColors.HotPink; + + /// + /// Represents a matching the W3C definition that has an hex value of #CD5C5C. + /// + public static readonly RgbaVector IndianRed = NamedColors.IndianRed; + + /// + /// Represents a matching the W3C definition that has an hex value of #4B0082. + /// + public static readonly RgbaVector Indigo = NamedColors.Indigo; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFF0. + /// + public static readonly RgbaVector Ivory = NamedColors.Ivory; + + /// + /// Represents a matching the W3C definition that has an hex value of #F0E68C. + /// + public static readonly RgbaVector Khaki = NamedColors.Khaki; + + /// + /// Represents a matching the W3C definition that has an hex value of #E6E6FA. + /// + public static readonly RgbaVector Lavender = NamedColors.Lavender; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF0F5. + /// + public static readonly RgbaVector LavenderBlush = NamedColors.LavenderBlush; + + /// + /// Represents a matching the W3C definition that has an hex value of #7CFC00. + /// + public static readonly RgbaVector LawnGreen = NamedColors.LawnGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFACD. + /// + public static readonly RgbaVector LemonChiffon = NamedColors.LemonChiffon; + + /// + /// Represents a matching the W3C definition that has an hex value of #ADD8E6. + /// + public static readonly RgbaVector LightBlue = NamedColors.LightBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #F08080. + /// + public static readonly RgbaVector LightCoral = NamedColors.LightCoral; + + /// + /// Represents a matching the W3C definition that has an hex value of #E0FFFF. + /// + public static readonly RgbaVector LightCyan = NamedColors.LightCyan; + + /// + /// Represents a matching the W3C definition that has an hex value of #FAFAD2. + /// + public static readonly RgbaVector LightGoldenrodYellow = NamedColors.LightGoldenrodYellow; + + /// + /// Represents a matching the W3C definition that has an hex value of #D3D3D3. + /// + public static readonly RgbaVector LightGray = NamedColors.LightGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #90EE90. + /// + public static readonly RgbaVector LightGreen = NamedColors.LightGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFB6C1. + /// + public static readonly RgbaVector LightPink = NamedColors.LightPink; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFA07A. + /// + public static readonly RgbaVector LightSalmon = NamedColors.LightSalmon; + + /// + /// Represents a matching the W3C definition that has an hex value of #20B2AA. + /// + public static readonly RgbaVector LightSeaGreen = NamedColors.LightSeaGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #87CEFA. + /// + public static readonly RgbaVector LightSkyBlue = NamedColors.LightSkyBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #778899. + /// + public static readonly RgbaVector LightSlateGray = NamedColors.LightSlateGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #B0C4DE. + /// + public static readonly RgbaVector LightSteelBlue = NamedColors.LightSteelBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFE0. + /// + public static readonly RgbaVector LightYellow = NamedColors.LightYellow; + + /// + /// Represents a matching the W3C definition that has an hex value of #00FF00. + /// + public static readonly RgbaVector Lime = NamedColors.Lime; + + /// + /// Represents a matching the W3C definition that has an hex value of #32CD32. + /// + public static readonly RgbaVector LimeGreen = NamedColors.LimeGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FAF0E6. + /// + public static readonly RgbaVector Linen = NamedColors.Linen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF00FF. + /// + public static readonly RgbaVector Magenta = NamedColors.Magenta; + + /// + /// Represents a matching the W3C definition that has an hex value of #800000. + /// + public static readonly RgbaVector Maroon = NamedColors.Maroon; + + /// + /// Represents a matching the W3C definition that has an hex value of #66CDAA. + /// + public static readonly RgbaVector MediumAquamarine = NamedColors.MediumAquamarine; + + /// + /// Represents a matching the W3C definition that has an hex value of #0000CD. + /// + public static readonly RgbaVector MediumBlue = NamedColors.MediumBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #BA55D3. + /// + public static readonly RgbaVector MediumOrchid = NamedColors.MediumOrchid; + + /// + /// Represents a matching the W3C definition that has an hex value of #9370DB. + /// + public static readonly RgbaVector MediumPurple = NamedColors.MediumPurple; + + /// + /// Represents a matching the W3C definition that has an hex value of #3CB371. + /// + public static readonly RgbaVector MediumSeaGreen = NamedColors.MediumSeaGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #7B68EE. + /// + public static readonly RgbaVector MediumSlateBlue = NamedColors.MediumSlateBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #00FA9A. + /// + public static readonly RgbaVector MediumSpringGreen = NamedColors.MediumSpringGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #48D1CC. + /// + public static readonly RgbaVector MediumTurquoise = NamedColors.MediumTurquoise; + + /// + /// Represents a matching the W3C definition that has an hex value of #C71585. + /// + public static readonly RgbaVector MediumVioletRed = NamedColors.MediumVioletRed; + + /// + /// Represents a matching the W3C definition that has an hex value of #191970. + /// + public static readonly RgbaVector MidnightBlue = NamedColors.MidnightBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #F5FFFA. + /// + public static readonly RgbaVector MintCream = NamedColors.MintCream; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4E1. + /// + public static readonly RgbaVector MistyRose = NamedColors.MistyRose; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFE4B5. + /// + public static readonly RgbaVector Moccasin = NamedColors.Moccasin; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFDEAD. + /// + public static readonly RgbaVector NavajoWhite = NamedColors.NavajoWhite; + + /// + /// Represents a matching the W3C definition that has an hex value of #000080. + /// + public static readonly RgbaVector Navy = NamedColors.Navy; + + /// + /// Represents a matching the W3C definition that has an hex value of #FDF5E6. + /// + public static readonly RgbaVector OldLace = NamedColors.OldLace; + + /// + /// Represents a matching the W3C definition that has an hex value of #808000. + /// + public static readonly RgbaVector Olive = NamedColors.Olive; + + /// + /// Represents a matching the W3C definition that has an hex value of #6B8E23. + /// + public static readonly RgbaVector OliveDrab = NamedColors.OliveDrab; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFA500. + /// + public static readonly RgbaVector Orange = NamedColors.Orange; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF4500. + /// + public static readonly RgbaVector OrangeRed = NamedColors.OrangeRed; + + /// + /// Represents a matching the W3C definition that has an hex value of #DA70D6. + /// + public static readonly RgbaVector Orchid = NamedColors.Orchid; + + /// + /// Represents a matching the W3C definition that has an hex value of #EEE8AA. + /// + public static readonly RgbaVector PaleGoldenrod = NamedColors.PaleGoldenrod; + + /// + /// Represents a matching the W3C definition that has an hex value of #98FB98. + /// + public static readonly RgbaVector PaleGreen = NamedColors.PaleGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #AFEEEE. + /// + public static readonly RgbaVector PaleTurquoise = NamedColors.PaleTurquoise; + + /// + /// Represents a matching the W3C definition that has an hex value of #DB7093. + /// + public static readonly RgbaVector PaleVioletRed = NamedColors.PaleVioletRed; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFEFD5. + /// + public static readonly RgbaVector PapayaWhip = NamedColors.PapayaWhip; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFDAB9. + /// + public static readonly RgbaVector PeachPuff = NamedColors.PeachPuff; + + /// + /// Represents a matching the W3C definition that has an hex value of #CD853F. + /// + public static readonly RgbaVector Peru = NamedColors.Peru; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFC0CB. + /// + public static readonly RgbaVector Pink = NamedColors.Pink; + + /// + /// Represents a matching the W3C definition that has an hex value of #DDA0DD. + /// + public static readonly RgbaVector Plum = NamedColors.Plum; + + /// + /// Represents a matching the W3C definition that has an hex value of #B0E0E6. + /// + public static readonly RgbaVector PowderBlue = NamedColors.PowderBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #800080. + /// + public static readonly RgbaVector Purple = NamedColors.Purple; + + /// + /// Represents a matching the W3C definition that has an hex value of #663399. + /// + public static readonly RgbaVector RebeccaPurple = NamedColors.RebeccaPurple; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF0000. + /// + public static readonly RgbaVector Red = NamedColors.Red; + + /// + /// Represents a matching the W3C definition that has an hex value of #BC8F8F. + /// + public static readonly RgbaVector RosyBrown = NamedColors.RosyBrown; + + /// + /// Represents a matching the W3C definition that has an hex value of #4169E1. + /// + public static readonly RgbaVector RoyalBlue = NamedColors.RoyalBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #8B4513. + /// + public static readonly RgbaVector SaddleBrown = NamedColors.SaddleBrown; + + /// + /// Represents a matching the W3C definition that has an hex value of #FA8072. + /// + public static readonly RgbaVector Salmon = NamedColors.Salmon; + + /// + /// Represents a matching the W3C definition that has an hex value of #F4A460. + /// + public static readonly RgbaVector SandyBrown = NamedColors.SandyBrown; + + /// + /// Represents a matching the W3C definition that has an hex value of #2E8B57. + /// + public static readonly RgbaVector SeaGreen = NamedColors.SeaGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFF5EE. + /// + public static readonly RgbaVector SeaShell = NamedColors.SeaShell; + + /// + /// Represents a matching the W3C definition that has an hex value of #A0522D. + /// + public static readonly RgbaVector Sienna = NamedColors.Sienna; + + /// + /// Represents a matching the W3C definition that has an hex value of #C0C0C0. + /// + public static readonly RgbaVector Silver = NamedColors.Silver; + + /// + /// Represents a matching the W3C definition that has an hex value of #87CEEB. + /// + public static readonly RgbaVector SkyBlue = NamedColors.SkyBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #6A5ACD. + /// + public static readonly RgbaVector SlateBlue = NamedColors.SlateBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #708090. + /// + public static readonly RgbaVector SlateGray = NamedColors.SlateGray; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFAFA. + /// + public static readonly RgbaVector Snow = NamedColors.Snow; + + /// + /// Represents a matching the W3C definition that has an hex value of #00FF7F. + /// + public static readonly RgbaVector SpringGreen = NamedColors.SpringGreen; + + /// + /// Represents a matching the W3C definition that has an hex value of #4682B4. + /// + public static readonly RgbaVector SteelBlue = NamedColors.SteelBlue; + + /// + /// Represents a matching the W3C definition that has an hex value of #D2B48C. + /// + public static readonly RgbaVector Tan = NamedColors.Tan; + + /// + /// Represents a matching the W3C definition that has an hex value of #008080. + /// + public static readonly RgbaVector Teal = NamedColors.Teal; + + /// + /// Represents a matching the W3C definition that has an hex value of #D8BFD8. + /// + public static readonly RgbaVector Thistle = NamedColors.Thistle; + + /// + /// Represents a matching the W3C definition that has an hex value of #FF6347. + /// + public static readonly RgbaVector Tomato = NamedColors.Tomato; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFFF. + /// + public static readonly RgbaVector Transparent = NamedColors.Transparent; + + /// + /// Represents a matching the W3C definition that has an hex value of #40E0D0. + /// + public static readonly RgbaVector Turquoise = NamedColors.Turquoise; + + /// + /// Represents a matching the W3C definition that has an hex value of #EE82EE. + /// + public static readonly RgbaVector Violet = NamedColors.Violet; + + /// + /// Represents a matching the W3C definition that has an hex value of #F5DEB3. + /// + public static readonly RgbaVector Wheat = NamedColors.Wheat; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFFFF. + /// + public static readonly RgbaVector White = NamedColors.White; + + /// + /// Represents a matching the W3C definition that has an hex value of #F5F5F5. + /// + public static readonly RgbaVector WhiteSmoke = NamedColors.WhiteSmoke; + + /// + /// Represents a matching the W3C definition that has an hex value of #FFFF00. + /// + public static readonly RgbaVector Yellow = NamedColors.Yellow; + + /// + /// Represents a matching the W3C definition that has an hex value of #9ACD32. + /// + public static readonly RgbaVector YellowGreen = NamedColors.YellowGreen; + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs new file mode 100644 index 0000000000..ac25b7f149 --- /dev/null +++ b/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs @@ -0,0 +1,33 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats +{ + using System; + using System.Numerics; + + using ImageSharp.Memory; + + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct RgbaVector + { + /// + /// implementation optimized for . + /// + internal class PixelOperations : PixelOperations + { + /// + internal override unsafe void ToVector4(Span sourceColors, Span destVectors, int count) + { + Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors)); + Guard.MustBeSizedAtLeast(destVectors, count, nameof(destVectors)); + + SpanHelper.Copy(sourceColors.AsBytes(), destVectors.AsBytes(), count * sizeof(Vector4)); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/RgbaVector.cs b/src/ImageSharp/PixelFormats/RgbaVector.cs new file mode 100644 index 0000000000..5332f4a8e7 --- /dev/null +++ b/src/ImageSharp/PixelFormats/RgbaVector.cs @@ -0,0 +1,324 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.PixelFormats +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// Unpacked pixel type containing four 16-bit floating-point values typically ranging from 0 to 1. + /// The color components are stored in red, green, blue, and alpha order. + /// + /// Ranges from <0, 0, 0, 0> to <1, 1, 1, 1> in vector form. + /// + /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// + public partial struct RgbaVector : IPixel + { + /// + /// The maximum byte value. + /// + private static readonly Vector4 MaxBytes = new Vector4(255); + + /// + /// The half vector value. + /// + private static readonly Vector4 Half = new Vector4(0.5F); + + /// + /// The backing vector for SIMD support. + /// + private Vector4 backingVector; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The alpha component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RgbaVector(byte r, byte g, byte b, byte a = 255) + : this() + { + this.backingVector = new Vector4(r, g, b, a) / MaxBytes; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The alpha component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RgbaVector(float r, float g, float b, float a = 1) + : this() + { + this.backingVector = new Vector4(r, g, b, a); + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RgbaVector(Vector3 vector) + : this() + { + this.backingVector = new Vector4(vector, 1); + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public RgbaVector(Vector4 vector) + : this() + { + this.backingVector = vector; + } + + /// + /// Gets or sets the red component. + /// + public float R + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.backingVector.X; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.backingVector.X = value; + } + } + + /// + /// Gets or sets the green component. + /// + public float G + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.backingVector.Y; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.backingVector.Y = value; + } + } + + /// + /// Gets or sets the blue component. + /// + public float B + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.backingVector.Z; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.backingVector.Z = value; + } + } + + /// + /// Gets or sets the alpha component. + /// + public float A + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.backingVector.W; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.backingVector.W = value; + } + } + + /// + /// Compares two objects for equality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(RgbaVector left, RgbaVector right) + { + return left.backingVector == right.backingVector; + } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(RgbaVector left, RgbaVector right) + { + return left.backingVector != right.backingVector; + } + + /// + /// Creates a new instance of the struct. + /// + /// + /// The hexadecimal representation of the combined color components arranged + /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. + /// + /// + /// The . + /// + public static RgbaVector FromHex(string hex) + { + return ColorBuilder.FromHex(hex); + } + + /// + public PixelOperations CreateBulkOperations() => new RgbaVector.PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PackFromBytes(byte x, byte y, byte z, byte w) + { + this.backingVector = new Vector4(x, y, z, w) / MaxBytes; + } + + /// + /// Converts the value of this instance to a hexadecimal string. + /// + /// A hexadecimal string representation of the value. + public string ToHex() + { + // Hex is RRGGBBAA + Vector4 vector = this.backingVector * MaxBytes; + vector += Half; + uint hexOrder = (uint)((byte)vector.W | (byte)vector.Z << 8 | (byte)vector.Y << 16 | (byte)vector.X << 24); + return hexOrder.ToString("X8"); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToXyzBytes(Span bytes, int startIndex) + { + Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes; + vector += Half; + bytes[startIndex] = (byte)vector.X; + bytes[startIndex + 1] = (byte)vector.Y; + bytes[startIndex + 2] = (byte)vector.Z; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToXyzwBytes(Span bytes, int startIndex) + { + Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes; + vector += Half; + bytes[startIndex] = (byte)vector.X; + bytes[startIndex + 1] = (byte)vector.Y; + bytes[startIndex + 2] = (byte)vector.Z; + bytes[startIndex + 3] = (byte)vector.W; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToZyxBytes(Span bytes, int startIndex) + { + Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes; + vector += Half; + bytes[startIndex] = (byte)vector.Z; + bytes[startIndex + 1] = (byte)vector.Y; + bytes[startIndex + 2] = (byte)vector.X; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToZyxwBytes(Span bytes, int startIndex) + { + Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes; + vector += Half; + bytes[startIndex] = (byte)vector.Z; + bytes[startIndex + 1] = (byte)vector.Y; + bytes[startIndex + 2] = (byte)vector.X; + bytes[startIndex + 3] = (byte)vector.W; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PackFromVector4(Vector4 vector) + { + this.backingVector = vector; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToVector4() + { + return this.backingVector; + } + + /// + public override bool Equals(object obj) + { + return (obj is RgbaVector) && this.Equals((RgbaVector)obj); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(RgbaVector other) + { + return this.backingVector == other.backingVector; + } + + /// + /// Gets a string representation of the packed vector. + /// + /// A string representation of the packed vector. + public override string ToString() + { + return this.ToVector4().ToString(); + } + + /// + public override int GetHashCode() + { + return this.backingVector.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/Short2.cs b/src/ImageSharp/PixelFormats/Short2.cs similarity index 93% rename from src/ImageSharp/Colors/PackedPixel/Short2.cs rename to src/ImageSharp/PixelFormats/Short2.cs index 167a1e786f..b848b55053 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short2.cs +++ b/src/ImageSharp/PixelFormats/Short2.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -11,6 +11,9 @@ namespace ImageSharp /// /// Packed pixel type containing two 16-bit signed integer values. + /// + /// Ranges from <-32767, -32767, 0, 1> to <32767, 32767, 0, 1> in vector form. + /// /// public struct Short2 : IPixel, IPackedVector { @@ -88,7 +91,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -116,7 +119,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { Vector2 vector = this.ToVector2(); vector /= 65534; @@ -132,7 +135,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { Vector2 vector = this.ToVector2(); vector /= 65534; @@ -149,7 +152,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { Vector2 vector = this.ToVector2(); vector /= 65534; @@ -165,7 +168,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { Vector2 vector = this.ToVector2(); vector /= 65534; diff --git a/src/ImageSharp/Colors/PackedPixel/Short4.cs b/src/ImageSharp/PixelFormats/Short4.cs similarity index 94% rename from src/ImageSharp/Colors/PackedPixel/Short4.cs rename to src/ImageSharp/PixelFormats/Short4.cs index e1a559c326..763de19bc3 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short4.cs +++ b/src/ImageSharp/PixelFormats/Short4.cs @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp +namespace ImageSharp.PixelFormats { using System; using System.Numerics; @@ -11,6 +11,9 @@ namespace ImageSharp /// /// Packed pixel type containing four 16-bit signed integer values. + /// + /// Ranges from <-37267, -37267, -37267, -37267> to <37267, 37267, 37267, 37267> in vector form. + /// /// public struct Short4 : IPixel, IPackedVector { @@ -90,7 +93,7 @@ namespace ImageSharp } /// - public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + public PixelOperations CreateBulkOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -122,7 +125,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(byte[] bytes, int startIndex) + public void ToXyzBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector /= 65534; @@ -138,7 +141,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(byte[] bytes, int startIndex) + public void ToXyzwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector /= 65534; @@ -155,7 +158,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(byte[] bytes, int startIndex) + public void ToZyxBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector /= 65534; @@ -171,7 +174,7 @@ namespace ImageSharp /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(byte[] bytes, int startIndex) + public void ToZyxwBytes(Span bytes, int startIndex) { Vector4 vector = this.ToVector4(); vector /= 65534; diff --git a/src/ImageSharp/Processing/Binarization/BinaryThreshold.cs b/src/ImageSharp/Processing/Binarization/BinaryThreshold.cs index 672726d929..f50616aa30 100644 --- a/src/ImageSharp/Processing/Binarization/BinaryThreshold.cs +++ b/src/ImageSharp/Processing/Binarization/BinaryThreshold.cs @@ -7,22 +7,24 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Applies binarization to the image splitting the pixels at the given threshold. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// The . - public static Image BinaryThreshold(this Image source, float threshold) - where TColor : struct, IPixel + /// The . + public static Image BinaryThreshold(this Image source, float threshold) + where TPixel : struct, IPixel { return BinaryThreshold(source, threshold, source.Bounds); } @@ -30,17 +32,17 @@ namespace ImageSharp /// /// Applies binarization to the image splitting the pixels at the given threshold. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image BinaryThreshold(this Image source, float threshold, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image BinaryThreshold(this Image source, float threshold, Rectangle rectangle) + where TPixel : struct, IPixel { - source.ApplyProcessor(new BinaryThresholdProcessor(threshold), rectangle); + source.ApplyProcessor(new BinaryThresholdProcessor(threshold), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/Binarization/Dither.cs b/src/ImageSharp/Processing/Binarization/Dither.cs index dd6dfe8a14..2a359c0898 100644 --- a/src/ImageSharp/Processing/Binarization/Dither.cs +++ b/src/ImageSharp/Processing/Binarization/Dither.cs @@ -8,23 +8,24 @@ namespace ImageSharp using System; using ImageSharp.Dithering; + using ImageSharp.PixelFormats; using ImageSharp.Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Dithers the image reducing it to two colors using ordered dithering. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The ordered ditherer. /// The component index to test the threshold against. Must range from 0 to 3. - /// The . - public static Image Dither(this Image source, IOrderedDither dither, int index = 0) - where TColor : struct, IPixel + /// The . + public static Image Dither(this Image source, IOrderedDither dither, int index = 0) + where TPixel : struct, IPixel { return Dither(source, dither, source.Bounds, index); } @@ -32,31 +33,31 @@ namespace ImageSharp /// /// Dithers the image reducing it to two colors using ordered dithering. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The ordered ditherer. /// /// The structure that specifies the portion of the image object to alter. /// /// The component index to test the threshold against. Must range from 0 to 3. - /// The . - public static Image Dither(this Image source, IOrderedDither dither, Rectangle rectangle, int index = 0) - where TColor : struct, IPixel + /// The . + public static Image Dither(this Image source, IOrderedDither dither, Rectangle rectangle, int index = 0) + where TPixel : struct, IPixel { - source.ApplyProcessor(new OrderedDitherProcessor(dither, index), rectangle); + source.ApplyProcessor(new OrderedDitherProcessor(dither, index), rectangle); return source; } /// /// Dithers the image reducing it to two colors using error diffusion. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// The . - public static Image Dither(this Image source, IErrorDiffuser diffuser, float threshold) - where TColor : struct, IPixel + /// The . + public static Image Dither(this Image source, IErrorDiffuser diffuser, float threshold) + where TPixel : struct, IPixel { return Dither(source, diffuser, threshold, source.Bounds); } @@ -64,18 +65,18 @@ namespace ImageSharp /// /// Dithers the image reducing it to two colors using error diffusion. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image Dither(this Image source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image Dither(this Image source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) + where TPixel : struct, IPixel { - source.ApplyProcessor(new ErrorDiffusionDitherProcessor(diffuser, threshold), rectangle); + source.ApplyProcessor(new ErrorDiffusionDitherProcessor(diffuser, threshold), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs b/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs index 63d6dd33c5..76977455a4 100644 --- a/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs +++ b/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs @@ -7,22 +7,24 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Applies black and white toning to the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. - /// The . - public static Image BlackWhite(this Image source) - where TColor : struct, IPixel + /// The . + public static Image BlackWhite(this Image source) + where TPixel : struct, IPixel { return BlackWhite(source, source.Bounds); } @@ -30,16 +32,16 @@ namespace ImageSharp /// /// Applies black and white toning to the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image BlackWhite(this Image source, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image BlackWhite(this Image source, Rectangle rectangle) + where TPixel : struct, IPixel { - source.ApplyProcessor(new BlackWhiteProcessor(), rectangle); + source.ApplyProcessor(new BlackWhiteProcessor(), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs b/src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs index 36a139d0ea..d012d6fe2b 100644 --- a/src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs +++ b/src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs @@ -7,23 +7,25 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Applies the given colorblindness simulator to the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The type of color blindness simulator to apply. - /// The . - public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness) - where TColor : struct, IPixel + /// The . + public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness) + where TPixel : struct, IPixel { return ColorBlindness(source, colorBlindness, source.Bounds); } @@ -31,50 +33,50 @@ namespace ImageSharp /// /// Applies the given colorblindness simulator to the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The type of color blindness simulator to apply. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image ColorBlindness(this Image source, ColorBlindness colorBlindness, Rectangle rectangle) + where TPixel : struct, IPixel { - IImageProcessor processor; + IImageProcessor processor; switch (colorBlindness) { case ImageSharp.Processing.ColorBlindness.Achromatomaly: - processor = new AchromatomalyProcessor(); + processor = new AchromatomalyProcessor(); break; case ImageSharp.Processing.ColorBlindness.Achromatopsia: - processor = new AchromatopsiaProcessor(); + processor = new AchromatopsiaProcessor(); break; case ImageSharp.Processing.ColorBlindness.Deuteranomaly: - processor = new DeuteranomalyProcessor(); + processor = new DeuteranomalyProcessor(); break; case ImageSharp.Processing.ColorBlindness.Deuteranopia: - processor = new DeuteranopiaProcessor(); + processor = new DeuteranopiaProcessor(); break; case ImageSharp.Processing.ColorBlindness.Protanomaly: - processor = new ProtanomalyProcessor(); + processor = new ProtanomalyProcessor(); break; case ImageSharp.Processing.ColorBlindness.Protanopia: - processor = new ProtanopiaProcessor(); + processor = new ProtanopiaProcessor(); break; case ImageSharp.Processing.ColorBlindness.Tritanomaly: - processor = new TritanomalyProcessor(); + processor = new TritanomalyProcessor(); break; default: - processor = new TritanopiaProcessor(); + processor = new TritanopiaProcessor(); break; } diff --git a/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs b/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs index 613b999d44..8700b63e88 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs @@ -7,23 +7,25 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Applies Grayscale toning to the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The formula to apply to perform the operation. - /// The . - public static Image Grayscale(this Image source, GrayscaleMode mode = GrayscaleMode.Bt709) - where TColor : struct, IPixel + /// The . + public static Image Grayscale(this Image source, GrayscaleMode mode = GrayscaleMode.Bt709) + where TPixel : struct, IPixel { return Grayscale(source, source.Bounds, mode); } @@ -31,19 +33,19 @@ namespace ImageSharp /// /// Applies Grayscale toning to the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The formula to apply to perform the operation. - /// The . - public static Image Grayscale(this Image source, Rectangle rectangle, GrayscaleMode mode = GrayscaleMode.Bt709) - where TColor : struct, IPixel + /// The . + public static Image Grayscale(this Image source, Rectangle rectangle, GrayscaleMode mode = GrayscaleMode.Bt709) + where TPixel : struct, IPixel { - IImageProcessor processor = mode == GrayscaleMode.Bt709 - ? (IImageProcessor)new GrayscaleBt709Processor() - : new GrayscaleBt601Processor(); + IImageProcessor processor = mode == GrayscaleMode.Bt709 + ? (IImageProcessor)new GrayscaleBt709Processor() + : new GrayscaleBt601Processor(); source.ApplyProcessor(processor, rectangle); return source; diff --git a/src/ImageSharp/Processing/ColorMatrix/Hue.cs b/src/ImageSharp/Processing/ColorMatrix/Hue.cs index 8edeb2ff31..8dbc555307 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Hue.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Hue.cs @@ -7,23 +7,25 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Alters the hue component of the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The angle in degrees to adjust the image. - /// The . - public static Image Hue(this Image source, float degrees) - where TColor : struct, IPixel + /// The . + public static Image Hue(this Image source, float degrees) + where TPixel : struct, IPixel { return Hue(source, degrees, source.Bounds); } @@ -31,17 +33,17 @@ namespace ImageSharp /// /// Alters the hue component of the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The angle in degrees to adjust the image. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image Hue(this Image source, float degrees, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image Hue(this Image source, float degrees, Rectangle rectangle) + where TPixel : struct, IPixel { - source.ApplyProcessor(new HueProcessor(degrees), rectangle); + source.ApplyProcessor(new HueProcessor(degrees), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs b/src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs index 5084c96b25..13a71a71ef 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs @@ -7,22 +7,24 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Alters the colors of the image recreating an old Kodachrome camera effect. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. - /// The . - public static Image Kodachrome(this Image source) - where TColor : struct, IPixel + /// The . + public static Image Kodachrome(this Image source) + where TPixel : struct, IPixel { return Kodachrome(source, source.Bounds); } @@ -30,16 +32,16 @@ namespace ImageSharp /// /// Alters the colors of the image recreating an old Kodachrome camera effect. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image Kodachrome(this Image source, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image Kodachrome(this Image source, Rectangle rectangle) + where TPixel : struct, IPixel { - source.ApplyProcessor(new KodachromeProcessor(), rectangle); + source.ApplyProcessor(new KodachromeProcessor(), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs b/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs index ef6b23d5da..937dca9ba8 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs @@ -7,39 +7,70 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Alters the colors of the image recreating an old Lomograph camera effect. /// - /// The pixel format. + /// The pixel format. + /// The image this method extends. + /// The . + public static Image Lomograph(this Image source) + where TPixel : struct, IPixel + { + return Lomograph(source, source.Bounds, GraphicsOptions.Default); + } + + /// + /// Alters the colors of the image recreating an old Lomograph camera effect. + /// + /// The pixel format. + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static Image Lomograph(this Image source, Rectangle rectangle) + where TPixel : struct, IPixel + { + return Lomograph(source, rectangle, GraphicsOptions.Default); + } + + /// + /// Alters the colors of the image recreating an old Lomograph camera effect. + /// + /// The pixel format. /// The image this method extends. - /// The . - public static Image Lomograph(this Image source) - where TColor : struct, IPixel + /// The options effecting pixel blending. + /// The . + public static Image Lomograph(this Image source, GraphicsOptions options) + where TPixel : struct, IPixel { - return Lomograph(source, source.Bounds); + return Lomograph(source, source.Bounds, options); } /// /// Alters the colors of the image recreating an old Lomograph camera effect. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image Lomograph(this Image source, Rectangle rectangle) - where TColor : struct, IPixel + /// The options effecting pixel blending. + /// The . + public static Image Lomograph(this Image source, Rectangle rectangle, GraphicsOptions options) + where TPixel : struct, IPixel { - source.ApplyProcessor(new LomographProcessor(), rectangle); + source.ApplyProcessor(new LomographProcessor(options), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs b/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs index 68b10173cc..f1a573c05d 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs @@ -7,39 +7,70 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Alters the colors of the image recreating an old Polaroid camera effect. /// - /// The pixel format. + /// The pixel format. + /// The image this method extends. + /// The . + public static Image Polaroid(this Image source) + where TPixel : struct, IPixel + { + return Polaroid(source, GraphicsOptions.Default); + } + + /// + /// Alters the colors of the image recreating an old Polaroid camera effect. + /// + /// The pixel format. + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static Image Polaroid(this Image source, Rectangle rectangle) + where TPixel : struct, IPixel + { + return Polaroid(source, rectangle, GraphicsOptions.Default); + } + + /// + /// Alters the colors of the image recreating an old Polaroid camera effect. + /// + /// The pixel format. /// The image this method extends. - /// The . - public static Image Polaroid(this Image source) - where TColor : struct, IPixel + /// The options effecting pixel blending. + /// The . + public static Image Polaroid(this Image source, GraphicsOptions options) + where TPixel : struct, IPixel { - return Polaroid(source, source.Bounds); + return Polaroid(source, source.Bounds, options); } /// /// Alters the colors of the image recreating an old Polaroid camera effect. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image Polaroid(this Image source, Rectangle rectangle) - where TColor : struct, IPixel + /// The options effecting pixel blending. + /// The . + public static Image Polaroid(this Image source, Rectangle rectangle, GraphicsOptions options) + where TPixel : struct, IPixel { - source.ApplyProcessor(new PolaroidProcessor(), rectangle); + source.ApplyProcessor(new PolaroidProcessor(options), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/ColorMatrix/Saturation.cs b/src/ImageSharp/Processing/ColorMatrix/Saturation.cs index 7a6359744b..c41f304b4e 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Saturation.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Saturation.cs @@ -7,23 +7,25 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Alters the saturation component of the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The new saturation of the image. Must be between -100 and 100. - /// The . - public static Image Saturation(this Image source, int amount) - where TColor : struct, IPixel + /// The . + public static Image Saturation(this Image source, int amount) + where TPixel : struct, IPixel { return Saturation(source, amount, source.Bounds); } @@ -31,17 +33,17 @@ namespace ImageSharp /// /// Alters the saturation component of the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The new saturation of the image. Must be between -100 and 100. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image Saturation(this Image source, int amount, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image Saturation(this Image source, int amount, Rectangle rectangle) + where TPixel : struct, IPixel { - source.ApplyProcessor(new SaturationProcessor(amount), rectangle); + source.ApplyProcessor(new SaturationProcessor(amount), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/ColorMatrix/Sepia.cs b/src/ImageSharp/Processing/ColorMatrix/Sepia.cs index 4943635e06..39eca4e8ef 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Sepia.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Sepia.cs @@ -7,22 +7,24 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Applies sepia toning to the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. - /// The . - public static Image Sepia(this Image source) - where TColor : struct, IPixel + /// The . + public static Image Sepia(this Image source) + where TPixel : struct, IPixel { return Sepia(source, source.Bounds); } @@ -30,16 +32,16 @@ namespace ImageSharp /// /// Applies sepia toning to the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image Sepia(this Image source, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image Sepia(this Image source, Rectangle rectangle) + where TPixel : struct, IPixel { - source.ApplyProcessor(new SepiaProcessor(), rectangle); + source.ApplyProcessor(new SepiaProcessor(), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/Convolution/BoxBlur.cs b/src/ImageSharp/Processing/Convolution/BoxBlur.cs index 428142ffa8..46c134ee20 100644 --- a/src/ImageSharp/Processing/Convolution/BoxBlur.cs +++ b/src/ImageSharp/Processing/Convolution/BoxBlur.cs @@ -7,22 +7,24 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Applies a box blur to the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The 'radius' value representing the size of the area to sample. - /// The . - public static Image BoxBlur(this Image source, int radius = 7) - where TColor : struct, IPixel + /// The . + public static Image BoxBlur(this Image source, int radius = 7) + where TPixel : struct, IPixel { return BoxBlur(source, radius, source.Bounds); } @@ -30,17 +32,17 @@ namespace ImageSharp /// /// Applies a box blur to the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The 'radius' value representing the size of the area to sample. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image BoxBlur(this Image source, int radius, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image BoxBlur(this Image source, int radius, Rectangle rectangle) + where TPixel : struct, IPixel { - source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); + source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/Convolution/DetectEdges.cs b/src/ImageSharp/Processing/Convolution/DetectEdges.cs index dba062b563..3aa1d0b513 100644 --- a/src/ImageSharp/Processing/Convolution/DetectEdges.cs +++ b/src/ImageSharp/Processing/Convolution/DetectEdges.cs @@ -7,53 +7,55 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// - /// Detects any edges within the image. Uses the filter + /// Detects any edges within the image. Uses the filter /// operating in Grayscale mode. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. - /// The . - public static Image DetectEdges(this Image source) - where TColor : struct, IPixel + /// The . + public static Image DetectEdges(this Image source) + where TPixel : struct, IPixel { - return DetectEdges(source, source.Bounds, new SobelProcessor { Grayscale = true }); + return DetectEdges(source, source.Bounds, new SobelProcessor { Grayscale = true }); } /// - /// Detects any edges within the image. Uses the filter + /// Detects any edges within the image. Uses the filter /// operating in Grayscale mode. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image DetectEdges(this Image source, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image DetectEdges(this Image source, Rectangle rectangle) + where TPixel : struct, IPixel { - return DetectEdges(source, rectangle, new SobelProcessor { Grayscale = true }); + return DetectEdges(source, rectangle, new SobelProcessor { Grayscale = true }); } /// /// Detects any edges within the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The filter for detecting edges. /// Whether to convert the image to Grayscale first. Defaults to true. - /// The . - public static Image DetectEdges(this Image source, EdgeDetection filter, bool grayscale = true) - where TColor : struct, IPixel + /// The . + public static Image DetectEdges(this Image source, EdgeDetection filter, bool grayscale = true) + where TPixel : struct, IPixel { return DetectEdges(source, filter, source.Bounds, grayscale); } @@ -61,59 +63,59 @@ namespace ImageSharp /// /// Detects any edges within the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The filter for detecting edges. /// /// The structure that specifies the portion of the image object to alter. /// /// Whether to convert the image to Grayscale first. Defaults to true. - /// The . - public static Image DetectEdges(this Image source, EdgeDetection filter, Rectangle rectangle, bool grayscale = true) - where TColor : struct, IPixel + /// The . + public static Image DetectEdges(this Image source, EdgeDetection filter, Rectangle rectangle, bool grayscale = true) + where TPixel : struct, IPixel { - IEdgeDetectorProcessor processor; + IEdgeDetectorProcessor processor; switch (filter) { case EdgeDetection.Kayyali: - processor = new KayyaliProcessor { Grayscale = grayscale }; + processor = new KayyaliProcessor { Grayscale = grayscale }; break; case EdgeDetection.Kirsch: - processor = new KirschProcessor { Grayscale = grayscale }; + processor = new KirschProcessor { Grayscale = grayscale }; break; case EdgeDetection.Lapacian3X3: - processor = new Laplacian3X3Processor { Grayscale = grayscale }; + processor = new Laplacian3X3Processor { Grayscale = grayscale }; break; case EdgeDetection.Lapacian5X5: - processor = new Laplacian5X5Processor { Grayscale = grayscale }; + processor = new Laplacian5X5Processor { Grayscale = grayscale }; break; case EdgeDetection.LaplacianOfGaussian: - processor = new LaplacianOfGaussianProcessor { Grayscale = grayscale }; + processor = new LaplacianOfGaussianProcessor { Grayscale = grayscale }; break; case EdgeDetection.Prewitt: - processor = new PrewittProcessor { Grayscale = grayscale }; + processor = new PrewittProcessor { Grayscale = grayscale }; break; case EdgeDetection.RobertsCross: - processor = new RobertsCrossProcessor { Grayscale = grayscale }; + processor = new RobertsCrossProcessor { Grayscale = grayscale }; break; case EdgeDetection.Robinson: - processor = new RobinsonProcessor { Grayscale = grayscale }; + processor = new RobinsonProcessor { Grayscale = grayscale }; break; case EdgeDetection.Scharr: - processor = new ScharrProcessor { Grayscale = grayscale }; + processor = new ScharrProcessor { Grayscale = grayscale }; break; default: - processor = new SobelProcessor { Grayscale = grayscale }; + processor = new SobelProcessor { Grayscale = grayscale }; break; } @@ -123,12 +125,12 @@ namespace ImageSharp /// /// Detects any edges within the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The filter for detecting edges. - /// The . - public static Image DetectEdges(this Image source, IEdgeDetectorProcessor filter) - where TColor : struct, IPixel + /// The . + public static Image DetectEdges(this Image source, IEdgeDetectorProcessor filter) + where TPixel : struct, IPixel { return DetectEdges(source, source.Bounds, filter); } @@ -136,15 +138,15 @@ namespace ImageSharp /// /// Detects any edges within the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The filter for detecting edges. - /// The . - public static Image DetectEdges(this Image source, Rectangle rectangle, IEdgeDetectorProcessor filter) - where TColor : struct, IPixel + /// The . + public static Image DetectEdges(this Image source, Rectangle rectangle, IEdgeDetectorProcessor filter) + where TPixel : struct, IPixel { source.ApplyProcessor(filter, rectangle); return source; diff --git a/src/ImageSharp/Processing/Convolution/GaussianBlur.cs b/src/ImageSharp/Processing/Convolution/GaussianBlur.cs index 81f8546380..72abec6df9 100644 --- a/src/ImageSharp/Processing/Convolution/GaussianBlur.cs +++ b/src/ImageSharp/Processing/Convolution/GaussianBlur.cs @@ -7,23 +7,25 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Applies a Gaussian blur to the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The 'sigma' value representing the weight of the blur. - /// The . - public static Image GaussianBlur(this Image source, float sigma = 3f) - where TColor : struct, IPixel + /// The . + public static Image GaussianBlur(this Image source, float sigma = 3f) + where TPixel : struct, IPixel { return GaussianBlur(source, sigma, source.Bounds); } @@ -31,17 +33,17 @@ namespace ImageSharp /// /// Applies a Gaussian blur to the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image GaussianBlur(this Image source, float sigma, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image GaussianBlur(this Image source, float sigma, Rectangle rectangle) + where TPixel : struct, IPixel { - source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); + source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/Convolution/GaussianSharpen.cs b/src/ImageSharp/Processing/Convolution/GaussianSharpen.cs index 61816198a5..2ed99ea260 100644 --- a/src/ImageSharp/Processing/Convolution/GaussianSharpen.cs +++ b/src/ImageSharp/Processing/Convolution/GaussianSharpen.cs @@ -7,23 +7,25 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Applies a Gaussian sharpening filter to the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The 'sigma' value representing the weight of the blur. - /// The . - public static Image GaussianSharpen(this Image source, float sigma = 3f) - where TColor : struct, IPixel + /// The . + public static Image GaussianSharpen(this Image source, float sigma = 3f) + where TPixel : struct, IPixel { return GaussianSharpen(source, sigma, source.Bounds); } @@ -31,17 +33,17 @@ namespace ImageSharp /// /// Applies a Gaussian sharpening filter to the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image GaussianSharpen(this Image source, float sigma, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image GaussianSharpen(this Image source, float sigma, Rectangle rectangle) + where TPixel : struct, IPixel { - source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle); + source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/Effects/Alpha.cs b/src/ImageSharp/Processing/Effects/Alpha.cs index 39849d4d4b..73b682b93b 100644 --- a/src/ImageSharp/Processing/Effects/Alpha.cs +++ b/src/ImageSharp/Processing/Effects/Alpha.cs @@ -7,22 +7,24 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Alters the alpha component of the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. - /// The new opacity of the image. Must be between 0 and 100. - /// The . - public static Image Alpha(this Image source, int percent) - where TColor : struct, IPixel + /// The new opacity of the image. Must be between 0 and 1. + /// The . + public static Image Alpha(this Image source, float percent) + where TPixel : struct, IPixel { return Alpha(source, percent, source.Bounds); } @@ -30,17 +32,17 @@ namespace ImageSharp /// /// Alters the alpha component of the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. - /// The new opacity of the image. Must be between 0 and 100. + /// The new opacity of the image. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image Alpha(this Image source, int percent, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image Alpha(this Image source, float percent, Rectangle rectangle) + where TPixel : struct, IPixel { - source.ApplyProcessor(new AlphaProcessor(percent), rectangle); + source.ApplyProcessor(new AlphaProcessor(percent), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/Effects/BackgroundColor.cs b/src/ImageSharp/Processing/Effects/BackgroundColor.cs index 2e621172e1..975d2c24b2 100644 --- a/src/ImageSharp/Processing/Effects/BackgroundColor.cs +++ b/src/ImageSharp/Processing/Effects/BackgroundColor.cs @@ -7,25 +7,74 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Replaces the background color of image with the given one. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The color to set as the background. - /// The . - public static Image BackgroundColor(this Image source, TColor color) - where TColor : struct, IPixel + /// The options effecting pixel blending. + /// The . + public static Image BackgroundColor(this Image source, TPixel color, GraphicsOptions options) + where TPixel : struct, IPixel { - source.ApplyProcessor(new BackgroundColorProcessor(color), source.Bounds); + return BackgroundColor(source, color, source.Bounds, options); + } + + /// + /// Replaces the background color of image with the given one. + /// + /// The pixel format. + /// The image this method extends. + /// The color to set as the background. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The options effecting pixel blending. + /// The . + public static Image BackgroundColor(this Image source, TPixel color, Rectangle rectangle, GraphicsOptions options) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new BackgroundColorProcessor(color, options), rectangle); return source; } + + /// + /// Replaces the background color of image with the given one. + /// + /// The pixel format. + /// The image this method extends. + /// The color to set as the background. + /// The . + public static Image BackgroundColor(this Image source, TPixel color) + where TPixel : struct, IPixel + { + return BackgroundColor(source, color, GraphicsOptions.Default); + } + + /// + /// Replaces the background color of image with the given one. + /// + /// The pixel format. + /// The image this method extends. + /// The color to set as the background. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static Image BackgroundColor(this Image source, TPixel color, Rectangle rectangle) + where TPixel : struct, IPixel + { + return BackgroundColor(source, color, rectangle, GraphicsOptions.Default); + } } } diff --git a/src/ImageSharp/Processing/Effects/Brightness.cs b/src/ImageSharp/Processing/Effects/Brightness.cs index 8ba702c4f5..a28df82c09 100644 --- a/src/ImageSharp/Processing/Effects/Brightness.cs +++ b/src/ImageSharp/Processing/Effects/Brightness.cs @@ -7,40 +7,42 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Alters the brightness component of the image. /// - /// The pixel format. - /// The image this method extends. + /// The pixel format. + /// The image this method extends. /// The new brightness of the image. Must be between -100 and 100. - /// The . - public static Image Brightness(this Image source, int amount) - where TColor : struct, IPixel - { + /// The . + public static Image Brightness(this Image source, int amount) + where TPixel : struct, IPixel + { return Brightness(source, amount, source.Bounds); } /// /// Alters the brightness component of the image. /// - /// The pixel format. - /// The image this method extends. + /// The pixel format. + /// The image this method extends. /// The new brightness of the image. Must be between -100 and 100. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image Brightness(this Image source, int amount, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image Brightness(this Image source, int amount, Rectangle rectangle) + where TPixel : struct, IPixel { - source.ApplyProcessor(new BrightnessProcessor(amount), rectangle); + source.ApplyProcessor(new BrightnessProcessor(amount), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/Effects/Contrast.cs b/src/ImageSharp/Processing/Effects/Contrast.cs index 0228f4fe37..8f226d08e2 100644 --- a/src/ImageSharp/Processing/Effects/Contrast.cs +++ b/src/ImageSharp/Processing/Effects/Contrast.cs @@ -7,22 +7,24 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Alters the contrast component of the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The new contrast of the image. Must be between -100 and 100. - /// The . - public static Image Contrast(this Image source, int amount) - where TColor : struct, IPixel + /// The . + public static Image Contrast(this Image source, int amount) + where TPixel : struct, IPixel { return Contrast(source, amount, source.Bounds); } @@ -30,17 +32,17 @@ namespace ImageSharp /// /// Alters the contrast component of the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The new contrast of the image. Must be between -100 and 100. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image Contrast(this Image source, int amount, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image Contrast(this Image source, int amount, Rectangle rectangle) + where TPixel : struct, IPixel { - source.ApplyProcessor(new ContrastProcessor(amount), rectangle); + source.ApplyProcessor(new ContrastProcessor(amount), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/Effects/Invert.cs b/src/ImageSharp/Processing/Effects/Invert.cs index 6c51ad3eb3..fe3bb7dc98 100644 --- a/src/ImageSharp/Processing/Effects/Invert.cs +++ b/src/ImageSharp/Processing/Effects/Invert.cs @@ -7,21 +7,23 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Inverts the colors of the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. - /// The . - public static Image Invert(this Image source) - where TColor : struct, IPixel + /// The . + public static Image Invert(this Image source) + where TPixel : struct, IPixel { return Invert(source, source.Bounds); } @@ -29,16 +31,16 @@ namespace ImageSharp /// /// Inverts the colors of the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image Invert(this Image source, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image Invert(this Image source, Rectangle rectangle) + where TPixel : struct, IPixel { - source.ApplyProcessor(new InvertProcessor(), rectangle); + source.ApplyProcessor(new InvertProcessor(), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/Effects/OilPainting.cs b/src/ImageSharp/Processing/Effects/OilPainting.cs index d7d8444c01..d4528b55ba 100644 --- a/src/ImageSharp/Processing/Effects/OilPainting.cs +++ b/src/ImageSharp/Processing/Effects/OilPainting.cs @@ -7,23 +7,25 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Alters the colors of the image recreating an oil painting effect. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image. /// The number of neighboring pixels used in calculating each individual pixel value. - /// The . - public static Image OilPaint(this Image source, int levels = 10, int brushSize = 15) - where TColor : struct, IPixel + /// The . + public static Image OilPaint(this Image source, int levels = 10, int brushSize = 15) + where TPixel : struct, IPixel { return OilPaint(source, levels, brushSize, source.Bounds); } @@ -31,16 +33,16 @@ namespace ImageSharp /// /// Alters the colors of the image recreating an oil painting effect. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image. /// The number of neighboring pixels used in calculating each individual pixel value. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image OilPaint(this Image source, int levels, int brushSize, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image OilPaint(this Image source, int levels, int brushSize, Rectangle rectangle) + where TPixel : struct, IPixel { Guard.MustBeGreaterThan(levels, 0, nameof(levels)); @@ -49,7 +51,7 @@ namespace ImageSharp throw new ArgumentOutOfRangeException(nameof(brushSize)); } - source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize), rectangle); + source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/Effects/Pixelate.cs b/src/ImageSharp/Processing/Effects/Pixelate.cs index 721dd930b0..eeffff0925 100644 --- a/src/ImageSharp/Processing/Effects/Pixelate.cs +++ b/src/ImageSharp/Processing/Effects/Pixelate.cs @@ -7,22 +7,24 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Pixelates an image with the given pixel size. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The size of the pixels. - /// The . - public static Image Pixelate(this Image source, int size = 4) - where TColor : struct, IPixel + /// The . + public static Image Pixelate(this Image source, int size = 4) + where TPixel : struct, IPixel { return Pixelate(source, size, source.Bounds); } @@ -30,22 +32,22 @@ namespace ImageSharp /// /// Pixelates an image with the given pixel size. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The size of the pixels. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image Pixelate(this Image source, int size, Rectangle rectangle) - where TColor : struct, IPixel + /// The . + public static Image Pixelate(this Image source, int size, Rectangle rectangle) + where TPixel : struct, IPixel { if (size <= 0 || size > source.Height || size > source.Width) { throw new ArgumentOutOfRangeException(nameof(size)); } - source.ApplyProcessor(new PixelateProcessor(size), rectangle); + source.ApplyProcessor(new PixelateProcessor(size), rectangle); return source; } } diff --git a/src/ImageSharp/ImageProcessor.cs b/src/ImageSharp/Processing/ImageProcessor.cs similarity index 79% rename from src/ImageSharp/ImageProcessor.cs rename to src/ImageSharp/Processing/ImageProcessor.cs index 79525a8e83..d42650e56f 100644 --- a/src/ImageSharp/ImageProcessor.cs +++ b/src/ImageSharp/Processing/ImageProcessor.cs @@ -8,12 +8,14 @@ namespace ImageSharp.Processing using System; using System.Threading.Tasks; + using ImageSharp.PixelFormats; + /// /// Allows the application of processors to images. /// - /// The pixel format. - internal abstract class ImageProcessor : IImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal abstract class ImageProcessor : IImageProcessor + where TPixel : struct, IPixel { /// public virtual ParallelOptions ParallelOptions { get; set; } @@ -22,7 +24,7 @@ namespace ImageSharp.Processing public virtual bool Compand { get; set; } = false; /// - public void Apply(ImageBase source, Rectangle sourceRectangle) + public void Apply(ImageBase source, Rectangle sourceRectangle) { if (this.ParallelOptions == null) { @@ -37,9 +39,15 @@ namespace ImageSharp.Processing this.AfterApply(source, sourceRectangle); } +#if DEBUG + catch (Exception) + { + throw; +#else catch (Exception ex) { throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); +#endif } } @@ -50,19 +58,19 @@ namespace ImageSharp.Processing /// /// The structure that specifies the portion of the image object to draw. /// - protected virtual void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected virtual void BeforeApply(ImageBase source, Rectangle sourceRectangle) { } /// - /// Applies the process to the specified portion of the specified at the specified location + /// Applies the process to the specified portion of the specified at the specified location /// and with the specified size. /// /// The source image. Cannot be null. /// /// The structure that specifies the portion of the image object to draw. /// - protected abstract void OnApply(ImageBase source, Rectangle sourceRectangle); + protected abstract void OnApply(ImageBase source, Rectangle sourceRectangle); /// /// This method is called after the process is applied to prepare the processor. @@ -71,7 +79,7 @@ namespace ImageSharp.Processing /// /// The structure that specifies the portion of the image object to draw. /// - protected virtual void AfterApply(ImageBase source, Rectangle sourceRectangle) + protected virtual void AfterApply(ImageBase source, Rectangle sourceRectangle) { } } diff --git a/src/ImageSharp/Processing/Overlays/Glow.cs b/src/ImageSharp/Processing/Overlays/Glow.cs index e8dfbdf0ef..587bbe6104 100644 --- a/src/ImageSharp/Processing/Overlays/Glow.cs +++ b/src/ImageSharp/Processing/Overlays/Glow.cs @@ -7,81 +7,158 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Applies a radial glow effect to an image. /// - /// The pixel format. + /// The pixel format. + /// The image this method extends. + /// The . + public static Image Glow(this Image source) + where TPixel : struct, IPixel + { + return Glow(source, GraphicsOptions.Default); + } + + /// + /// Applies a radial glow effect to an image. + /// + /// The pixel format. + /// The image this method extends. + /// The color to set as the glow. + /// The . + public static Image Glow(this Image source, TPixel color) + where TPixel : struct, IPixel + { + return Glow(source, color, GraphicsOptions.Default); + } + + /// + /// Applies a radial glow effect to an image. + /// + /// The pixel format. + /// The image this method extends. + /// The the radius. + /// The . + public static Image Glow(this Image source, float radius) + where TPixel : struct, IPixel + { + return Glow(source, radius, GraphicsOptions.Default); + } + + /// + /// Applies a radial glow effect to an image. + /// + /// The pixel format. + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static Image Glow(this Image source, Rectangle rectangle) + where TPixel : struct, IPixel + { + return Glow(source, rectangle, GraphicsOptions.Default); + } + + /// + /// Applies a radial glow effect to an image. + /// + /// The pixel format. + /// The image this method extends. + /// The color to set as the glow. + /// The the radius. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static Image Glow(this Image source, TPixel color, float radius, Rectangle rectangle) + where TPixel : struct, IPixel + { + return Glow(source, color, radius, rectangle, GraphicsOptions.Default); + } + + /// + /// Applies a radial glow effect to an image. + /// + /// The pixel format. /// The image this method extends. - /// The . - public static Image Glow(this Image source) - where TColor : struct, IPixel + /// The options effecting things like blending. + /// The . + public static Image Glow(this Image source, GraphicsOptions options) + where TPixel : struct, IPixel { - return Glow(source, NamedColors.Black, source.Bounds.Width * .5F, source.Bounds); + return Glow(source, NamedColors.Black, source.Bounds.Width * .5F, source.Bounds, options); } /// /// Applies a radial glow effect to an image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The color to set as the glow. - /// The . - public static Image Glow(this Image source, TColor color) - where TColor : struct, IPixel + /// The options effecting things like blending. + /// The . + public static Image Glow(this Image source, TPixel color, GraphicsOptions options) + where TPixel : struct, IPixel { - return Glow(source, color, source.Bounds.Width * .5F, source.Bounds); + return Glow(source, color, source.Bounds.Width * .5F, source.Bounds, options); } /// /// Applies a radial glow effect to an image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The the radius. - /// The . - public static Image Glow(this Image source, float radius) - where TColor : struct, IPixel + /// The options effecting things like blending. + /// The . + public static Image Glow(this Image source, float radius, GraphicsOptions options) + where TPixel : struct, IPixel { - return Glow(source, NamedColors.Black, radius, source.Bounds); + return Glow(source, NamedColors.Black, radius, source.Bounds, options); } /// /// Applies a radial glow effect to an image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image Glow(this Image source, Rectangle rectangle) - where TColor : struct, IPixel + /// The options effecting things like blending. + /// The . + public static Image Glow(this Image source, Rectangle rectangle, GraphicsOptions options) + where TPixel : struct, IPixel { - return Glow(source, NamedColors.Black, 0, rectangle); + return Glow(source, NamedColors.Black, 0, rectangle, options); } /// /// Applies a radial glow effect to an image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The color to set as the glow. /// The the radius. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image Glow(this Image source, TColor color, float radius, Rectangle rectangle) - where TColor : struct, IPixel + /// The options effecting things like blending. + /// The . + public static Image Glow(this Image source, TPixel color, float radius, Rectangle rectangle, GraphicsOptions options) + where TPixel : struct, IPixel { - GlowProcessor processor = new GlowProcessor(color) { Radius = radius, }; + GlowProcessor processor = new GlowProcessor(color, options) { Radius = radius, }; source.ApplyProcessor(processor, rectangle); return source; } diff --git a/src/ImageSharp/Processing/Overlays/Vignette.cs b/src/ImageSharp/Processing/Overlays/Vignette.cs index e42ead8d3a..2eaf2e1efa 100644 --- a/src/ImageSharp/Processing/Overlays/Vignette.cs +++ b/src/ImageSharp/Processing/Overlays/Vignette.cs @@ -7,71 +7,149 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Applies a radial vignette effect to an image. /// - /// The pixel format. + /// The pixel format. + /// The image this method extends. + /// The . + public static Image Vignette(this Image source) + where TPixel : struct, IPixel + { + return Vignette(source, GraphicsOptions.Default); + } + + /// + /// Applies a radial vignette effect to an image. + /// + /// The pixel format. + /// The image this method extends. + /// The color to set as the vignette. + /// The . + public static Image Vignette(this Image source, TPixel color) + where TPixel : struct, IPixel + { + return Vignette(source, color, GraphicsOptions.Default); + } + + /// + /// Applies a radial vignette effect to an image. + /// + /// The pixel format. + /// The image this method extends. + /// The the x-radius. + /// The the y-radius. + /// The . + public static Image Vignette(this Image source, float radiusX, float radiusY) + where TPixel : struct, IPixel + { + return Vignette(source, radiusX, radiusY, GraphicsOptions.Default); + } + + /// + /// Applies a radial vignette effect to an image. + /// + /// The pixel format. + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static Image Vignette(this Image source, Rectangle rectangle) + where TPixel : struct, IPixel + { + return Vignette(source, rectangle, GraphicsOptions.Default); + } + + /// + /// Applies a radial vignette effect to an image. + /// + /// The pixel format. + /// The image this method extends. + /// The color to set as the vignette. + /// The the x-radius. + /// The the y-radius. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static Image Vignette(this Image source, TPixel color, float radiusX, float radiusY, Rectangle rectangle) + where TPixel : struct, IPixel + { + return Vignette(source, color, radiusX, radiusY, rectangle, GraphicsOptions.Default); + } + + /// + /// Applies a radial vignette effect to an image. + /// + /// The pixel format. /// The image this method extends. - /// The . - public static Image Vignette(this Image source) - where TColor : struct, IPixel + /// The options effecting pixel blending. + /// The . + public static Image Vignette(this Image source, GraphicsOptions options) + where TPixel : struct, IPixel { - return Vignette(source, NamedColors.Black, source.Bounds.Width * .5F, source.Bounds.Height * .5F, source.Bounds); + return Vignette(source, NamedColors.Black, source.Bounds.Width * .5F, source.Bounds.Height * .5F, source.Bounds, options); } /// /// Applies a radial vignette effect to an image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The color to set as the vignette. - /// The . - public static Image Vignette(this Image source, TColor color) - where TColor : struct, IPixel + /// The options effecting pixel blending. + /// The . + public static Image Vignette(this Image source, TPixel color, GraphicsOptions options) + where TPixel : struct, IPixel { - return Vignette(source, color, source.Bounds.Width * .5F, source.Bounds.Height * .5F, source.Bounds); + return Vignette(source, color, source.Bounds.Width * .5F, source.Bounds.Height * .5F, source.Bounds, options); } /// /// Applies a radial vignette effect to an image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The the x-radius. /// The the y-radius. - /// The . - public static Image Vignette(this Image source, float radiusX, float radiusY) - where TColor : struct, IPixel + /// The options effecting pixel blending. + /// The . + public static Image Vignette(this Image source, float radiusX, float radiusY, GraphicsOptions options) + where TPixel : struct, IPixel { - return Vignette(source, NamedColors.Black, radiusX, radiusY, source.Bounds); + return Vignette(source, NamedColors.Black, radiusX, radiusY, source.Bounds, options); } /// /// Applies a radial vignette effect to an image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image Vignette(this Image source, Rectangle rectangle) - where TColor : struct, IPixel + /// The options effecting pixel blending. + /// The . + public static Image Vignette(this Image source, Rectangle rectangle, GraphicsOptions options) + where TPixel : struct, IPixel { - return Vignette(source, NamedColors.Black, 0, 0, rectangle); + return Vignette(source, NamedColors.Black, 0, 0, rectangle, options); } /// /// Applies a radial vignette effect to an image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The color to set as the vignette. /// The the x-radius. @@ -79,11 +157,12 @@ namespace ImageSharp /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static Image Vignette(this Image source, TColor color, float radiusX, float radiusY, Rectangle rectangle) - where TColor : struct, IPixel + /// The options effecting pixel blending. + /// The . + public static Image Vignette(this Image source, TPixel color, float radiusX, float radiusY, Rectangle rectangle, GraphicsOptions options) + where TPixel : struct, IPixel { - VignetteProcessor processor = new VignetteProcessor(color) { RadiusX = radiusX, RadiusY = radiusY }; + VignetteProcessor processor = new VignetteProcessor(color, options) { RadiusX = radiusX, RadiusY = radiusY }; source.ApplyProcessor(processor, rectangle); return source; } diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs index 5555463418..5cd67f053e 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs @@ -8,16 +8,18 @@ namespace ImageSharp.Processing.Processors using System; using System.Threading.Tasks; + using ImageSharp.PixelFormats; + /// - /// An to perform binary threshold filtering against an - /// . The image will be converted to grayscale before thresholding occurs. + /// An to perform binary threshold filtering against an + /// . The image will be converted to grayscale before thresholding occurs. /// - /// The pixel format. - internal class BinaryThresholdProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class BinaryThresholdProcessor : ImageProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The threshold to split the image. Must be between 0 and 1. public BinaryThresholdProcessor(float threshold) @@ -27,8 +29,8 @@ namespace ImageSharp.Processing.Processors this.Threshold = threshold; // Default to white/black for upper/lower. - this.UpperColor = NamedColors.White; - this.LowerColor = NamedColors.Black; + this.UpperColor = NamedColors.White; + this.LowerColor = NamedColors.Black; } /// @@ -39,25 +41,25 @@ namespace ImageSharp.Processing.Processors /// /// Gets or sets the color to use for pixels that are above the threshold. /// - public TColor UpperColor { get; set; } + public TPixel UpperColor { get; set; } /// /// Gets or sets the color to use for pixels that fall below the threshold. /// - public TColor LowerColor { get; set; } + public TPixel LowerColor { get; set; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle); } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { float threshold = this.Threshold; - TColor upper = this.UpperColor; - TColor lower = this.LowerColor; + TPixel upper = this.UpperColor; + TPixel lower = this.LowerColor; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -81,7 +83,7 @@ namespace ImageSharp.Processing.Processors startY = 0; } - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { Parallel.For( minY, @@ -93,7 +95,7 @@ namespace ImageSharp.Processing.Processors for (int x = minX; x < maxX; x++) { int offsetX = x - startX; - TColor color = sourcePixels[offsetX, offsetY]; + TPixel color = sourcePixels[offsetX, offsetY]; // Any channel will do since it's Grayscale. sourcePixels[offsetX, offsetY] = color.ToVector4().X >= threshold ? upper : lower; diff --git a/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs index 50f042bd69..af2d9f760a 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/ErrorDiffusionDitherProcessor.cs @@ -8,16 +8,17 @@ namespace ImageSharp.Processing.Processors using System; using ImageSharp.Dithering; + using ImageSharp.PixelFormats; /// - /// An that dithers an image using error diffusion. + /// An that dithers an image using error diffusion. /// - /// The pixel format. - internal class ErrorDiffusionDitherProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class ErrorDiffusionDitherProcessor : ImageProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The error diffuser /// The threshold to split the image. Must be between 0 and 1. @@ -29,8 +30,8 @@ namespace ImageSharp.Processing.Processors this.Threshold = threshold; // Default to white/black for upper/lower. - this.UpperColor = NamedColors.White; - this.LowerColor = NamedColors.Black; + this.UpperColor = NamedColors.White; + this.LowerColor = NamedColors.Black; } /// @@ -46,21 +47,21 @@ namespace ImageSharp.Processing.Processors /// /// Gets or sets the color to use for pixels that are above the threshold. /// - public TColor UpperColor { get; set; } + public TPixel UpperColor { get; set; } /// /// Gets or sets the color to use for pixels that fall below the threshold. /// - public TColor LowerColor { get; set; } + public TPixel LowerColor { get; set; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle); } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -84,7 +85,7 @@ namespace ImageSharp.Processing.Processors startY = 0; } - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { for (int y = minY; y < maxY; y++) { @@ -92,8 +93,8 @@ namespace ImageSharp.Processing.Processors for (int x = minX; x < maxX; x++) { int offsetX = x - startX; - TColor sourceColor = sourcePixels[offsetX, offsetY]; - TColor transformedColor = sourceColor.ToVector4().X >= this.Threshold ? this.UpperColor : this.LowerColor; + TPixel sourceColor = sourcePixels[offsetX, offsetY]; + TPixel transformedColor = sourceColor.ToVector4().X >= this.Threshold ? this.UpperColor : this.LowerColor; this.Diffuser.Dither(sourcePixels, sourceColor, transformedColor, offsetX, offsetY, maxX, maxY); } } diff --git a/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs index c7f4d20ace..c4d71d9afe 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/OrderedDitherProcessor.cs @@ -9,16 +9,17 @@ namespace ImageSharp.Processing.Processors using System.Buffers; using ImageSharp.Dithering; + using ImageSharp.PixelFormats; /// - /// An that dithers an image using error diffusion. + /// An that dithers an image using error diffusion. /// - /// The pixel format. - internal class OrderedDitherProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class OrderedDitherProcessor : ImageProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The ordered ditherer. /// The component index to test the threshold against. Must range from 0 to 3. @@ -28,7 +29,7 @@ namespace ImageSharp.Processing.Processors Guard.MustBeBetweenOrEqualTo(index, 0, 3, nameof(index)); // Alpha8 only stores the pixel data in the alpha channel. - if (typeof(TColor) == typeof(Alpha8)) + if (typeof(TPixel) == typeof(Alpha8)) { index = 3; } @@ -37,8 +38,8 @@ namespace ImageSharp.Processing.Processors this.Index = index; // Default to white/black for upper/lower. - this.UpperColor = NamedColors.White; - this.LowerColor = NamedColors.Black; + this.UpperColor = NamedColors.White; + this.LowerColor = NamedColors.Black; } /// @@ -54,21 +55,21 @@ namespace ImageSharp.Processing.Processors /// /// Gets or sets the color to use for pixels that are above the threshold. /// - public TColor UpperColor { get; set; } + public TPixel UpperColor { get; set; } /// /// Gets or sets the color to use for pixels that fall below the threshold. /// - public TColor LowerColor { get; set; } + public TPixel LowerColor { get; set; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle); } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -92,7 +93,7 @@ namespace ImageSharp.Processing.Processors startY = 0; } - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { for (int y = minY; y < maxY; y++) { @@ -102,7 +103,7 @@ namespace ImageSharp.Processing.Processors for (int x = minX; x < maxX; x++) { int offsetX = x - startX; - TColor sourceColor = sourcePixels[offsetX, offsetY]; + TPixel sourceColor = sourcePixels[offsetX, offsetY]; this.Dither.Dither(sourcePixels, sourceColor, this.UpperColor, this.LowerColor, bytes, this.Index, offsetX, offsetY, maxX, maxY); } diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs index 0214af72de..d37d119a41 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/BlackWhiteProcessor.cs @@ -8,12 +8,14 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Converts the colors of the image to their black and white equivalent. /// - /// The pixel format. - internal class BlackWhiteProcessor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class BlackWhiteProcessor : ColorMatrixProcessor + where TPixel : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs index d1e986a9df..a91bd19467 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatomalyProcessor.cs @@ -8,12 +8,14 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness. /// - /// The pixel format. - internal class AchromatomalyProcessor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class AchromatomalyProcessor : ColorMatrixProcessor + where TPixel : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs index d17e28dcaf..d543c4edca 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/AchromatopsiaProcessor.cs @@ -8,12 +8,14 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness. /// - /// The pixel format. - internal class AchromatopsiaProcessor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class AchromatopsiaProcessor : ColorMatrixProcessor + where TPixel : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs index 7f4529ba47..ea73d0c662 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranomalyProcessor.cs @@ -8,12 +8,14 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness. /// - /// The pixel format. - internal class DeuteranomalyProcessor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class DeuteranomalyProcessor : ColorMatrixProcessor + where TPixel : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs index 493ed2caed..4b5129a8bf 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/DeuteranopiaProcessor.cs @@ -8,12 +8,14 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness. /// - /// The pixel format. - internal class DeuteranopiaProcessor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class DeuteranopiaProcessor : ColorMatrixProcessor + where TPixel : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs index ddea24be01..14eea08126 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanomalyProcessor.cs @@ -8,12 +8,14 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Converts the colors of the image recreating Protanopia (Red-Weak) color blindness. /// - /// The pixel format. - internal class ProtanomalyProcessor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class ProtanomalyProcessor : ColorMatrixProcessor + where TPixel : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs index c5446dbe1a..39cb715bde 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/ProtanopiaProcessor.cs @@ -8,12 +8,14 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness. /// - /// The pixel format. - internal class ProtanopiaProcessor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class ProtanopiaProcessor : ColorMatrixProcessor + where TPixel : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs index 846e9c61a7..2b402197bc 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanomalyProcessor.cs @@ -8,12 +8,14 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness. /// - /// The pixel format. - internal class TritanomalyProcessor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class TritanomalyProcessor : ColorMatrixProcessor + where TPixel : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs index a0094f71f0..5d228afa79 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorBlindness/TritanopiaProcessor.cs @@ -8,12 +8,14 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness. /// - /// The pixel format. - internal class TritanopiaProcessor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class TritanopiaProcessor : ColorMatrixProcessor + where TPixel : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs index c75da00037..cfe50150fd 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs @@ -7,14 +7,17 @@ namespace ImageSharp.Processing.Processors { using System; using System.Numerics; + using System.Runtime.CompilerServices; using System.Threading.Tasks; + using ImageSharp.PixelFormats; + /// /// The color matrix filter. Inherit from this class to perform operation involving color matrices. /// - /// The pixel format. - internal abstract class ColorMatrixProcessor : ImageProcessor, IColorMatrixFilter - where TColor : struct, IPixel + /// The pixel format. + internal abstract class ColorMatrixProcessor : ImageProcessor, IColorMatrixFilter + where TPixel : struct, IPixel { /// public abstract Matrix4x4 Matrix { get; } @@ -23,7 +26,7 @@ namespace ImageSharp.Processing.Processors public override bool Compand { get; set; } = true; /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -50,7 +53,7 @@ namespace ImageSharp.Processing.Processors Matrix4x4 matrix = this.Matrix; bool compand = this.Compand; - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { Parallel.For( minY, @@ -75,9 +78,10 @@ namespace ImageSharp.Processing.Processors /// The matrix. /// Whether to compand the color during processing. /// - /// The . + /// The . /// - private TColor ApplyMatrix(TColor color, Matrix4x4 matrix, bool compand) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private TPixel ApplyMatrix(TPixel color, Matrix4x4 matrix, bool compand) { Vector4 vector = color.ToVector4(); @@ -87,7 +91,7 @@ namespace ImageSharp.Processing.Processors } vector = Vector4.Transform(vector, matrix); - TColor packed = default(TColor); + TPixel packed = default(TPixel); packed.PackFromVector4(compand ? vector.Compress() : vector); return packed; } diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs index 1f5a0fa7e9..65de8a66d2 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt601Processor.cs @@ -8,13 +8,15 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Converts the colors of the image to Grayscale applying the formula as specified by ITU-R Recommendation BT.601 /// . /// - /// The pixel format. - internal class GrayscaleBt601Processor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class GrayscaleBt601Processor : ColorMatrixProcessor + where TPixel : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs index 048462696a..5f71e357b7 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/GrayscaleBt709Processor.cs @@ -8,13 +8,15 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Converts the colors of the image to Grayscale applying the formula as specified by ITU-R Recommendation BT.709 /// . /// - /// The pixel format. - internal class GrayscaleBt709Processor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class GrayscaleBt709Processor : ColorMatrixProcessor + where TPixel : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs index 0d06c58682..8995663a3b 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/HueProcessor.cs @@ -8,15 +8,17 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// - /// An to change the hue of an . + /// An to change the hue of an . /// - /// The pixel format. - internal class HueProcessor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class HueProcessor : ColorMatrixProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The new brightness of the image. Must be between -100 and 100. public HueProcessor(float angle) diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixFilter.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixFilter.cs index 57296a0c3b..0c29c65a10 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixFilter.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/IColorMatrixFilter.cs @@ -8,13 +8,15 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Encapsulates properties and methods for creating processors that utilize a matrix to /// alter the image pixels. /// - /// The pixel format. - internal interface IColorMatrixFilter : IImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal interface IColorMatrixFilter : IImageProcessor + where TPixel : struct, IPixel { /// /// Gets the used to alter the image. diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/KodachromeProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/KodachromeProcessor.cs index 8df8efcd19..18514236f7 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/KodachromeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/KodachromeProcessor.cs @@ -8,12 +8,14 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Converts the colors of the image recreating an old Kodachrome camera effect. /// - /// The pixel format. - internal class KodachromeProcessor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class KodachromeProcessor : ColorMatrixProcessor + where TPixel : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4() diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs index b89caec863..f6480c1837 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/LomographProcessor.cs @@ -8,14 +8,26 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Converts the colors of the image recreating an old Lomograph effect. /// - /// The pixel format. - internal class LomographProcessor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class LomographProcessor : ColorMatrixProcessor + where TPixel : struct, IPixel { - private static readonly TColor VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); + private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); + private GraphicsOptions options; + + /// + /// Initializes a new instance of the class. + /// + /// The options effecting blending and composition. + public LomographProcessor(GraphicsOptions options) + { + this.options = options; + } /// public override Matrix4x4 Matrix => new Matrix4x4() @@ -30,9 +42,9 @@ namespace ImageSharp.Processing.Processors }; /// - protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) + protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) { - new VignetteProcessor(VeryDarkGreen).Apply(source, sourceRectangle); + new VignetteProcessor(VeryDarkGreen, this.options).Apply(source, sourceRectangle); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs index b5a23f8557..5df034add2 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/PolaroidProcessor.cs @@ -8,15 +8,27 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Converts the colors of the image recreating an old Polaroid effect. /// - /// The pixel format. - internal class PolaroidProcessor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class PolaroidProcessor : ColorMatrixProcessor + where TPixel : struct, IPixel { - private static TColor veryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); - private static TColor lightOrange = ColorBuilder.FromRGBA(255, 153, 102, 178); + private static TPixel veryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); + private static TPixel lightOrange = ColorBuilder.FromRGBA(255, 153, 102, 178); + private GraphicsOptions options; + + /// + /// Initializes a new instance of the class. + /// + /// The options effecting blending and composition. + public PolaroidProcessor(GraphicsOptions options) + { + this.options = options; + } /// public override Matrix4x4 Matrix => new Matrix4x4() @@ -37,10 +49,10 @@ namespace ImageSharp.Processing.Processors }; /// - protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) + protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) { - new VignetteProcessor(veryDarkOrange).Apply(source, sourceRectangle); - new GlowProcessor(lightOrange) { Radius = source.Width / 4F }.Apply(source, sourceRectangle); + new VignetteProcessor(veryDarkOrange, this.options).Apply(source, sourceRectangle); + new GlowProcessor(lightOrange, this.options) { Radius = source.Width / 4F }.Apply(source, sourceRectangle); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs index 371294dd56..3adfb83114 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/SaturationProcessor.cs @@ -8,15 +8,17 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// - /// An to change the saturation of an . + /// An to change the saturation of an . /// - /// The pixel format. - internal class SaturationProcessor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class SaturationProcessor : ColorMatrixProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The new saturation of the image. Must be between -100 and 100. /// diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs index 49a071bd98..89be3acad5 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/SepiaProcessor.cs @@ -8,13 +8,15 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Converts the colors of the image to their sepia equivalent. /// The formula used matches the svg specification. /// - /// The pixel format. - internal class SepiaProcessor : ColorMatrixProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class SepiaProcessor : ColorMatrixProcessor + where TPixel : struct, IPixel { /// public override Matrix4x4 Matrix => new Matrix4x4 diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs index 7ffca534cd..b97e070791 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -7,12 +7,15 @@ namespace ImageSharp.Processing.Processors { using System; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// Applies a Box blur sampler to the image. /// - /// The pixel format. - internal class BoxBlurProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class BoxBlurProcessor : ImageProcessor + where TPixel : struct, IPixel { /// /// The maximum size of the kernel in either direction. @@ -20,7 +23,7 @@ namespace ImageSharp.Processing.Processors private readonly int kernelSize; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. @@ -43,9 +46,9 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); + new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); } /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs index fa06a863ec..29086b53fd 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs @@ -5,19 +5,21 @@ namespace ImageSharp.Processing.Processors { - using System; using System.Numerics; using System.Threading.Tasks; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// Defines a sampler that uses two one-dimensional matrices to perform convolution against an image. /// - /// The pixel format. - internal class Convolution2DProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class Convolution2DProcessor : ImageProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The horizontal gradient operator. /// The vertical gradient operator. @@ -38,7 +40,7 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { int kernelYHeight = this.KernelY.Height; int kernelYWidth = this.KernelY.Width; @@ -54,11 +56,12 @@ namespace ImageSharp.Processing.Processors int maxY = endY - 1; int maxX = endX - 1; - using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) + using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) + using (PixelAccessor sourcePixels = source.Lock()) { - using (PixelAccessor sourcePixels = source.Lock()) - { - Parallel.For( + sourcePixels.CopyTo(targetPixels); + + Parallel.For( startY, endY, this.ParallelOptions, @@ -112,12 +115,11 @@ namespace ImageSharp.Processing.Processors float green = MathF.Sqrt((gX * gX) + (gY * gY)); float blue = MathF.Sqrt((bX * bX) + (bY * bY)); - TColor packed = default(TColor); + TPixel packed = default(TPixel); packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); targetPixels[x, y] = packed; } }); - } source.SwapPixelsBuffers(targetPixels); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs index 45906a46fc..e391047931 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs @@ -9,15 +9,18 @@ namespace ImageSharp.Processing.Processors using System.Numerics; using System.Threading.Tasks; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// Defines a sampler that uses two one-dimensional matrices to perform two-pass convolution against an image. /// - /// The pixel format. - internal class Convolution2PassProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class Convolution2PassProcessor : ImageProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The horizontal gradient operator. /// The vertical gradient operator. @@ -38,26 +41,21 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { int width = source.Width; int height = source.Height; - using (PixelAccessor targetPixels = new PixelAccessor(width, height)) + using (PixelAccessor firstPassPixels = new PixelAccessor(width, height)) + using (PixelAccessor sourcePixels = source.Lock()) { - using (PixelAccessor firstPassPixels = new PixelAccessor(width, height)) - using (PixelAccessor sourcePixels = source.Lock()) - { - this.ApplyConvolution(firstPassPixels, sourcePixels, sourceRectangle, this.KernelX); - this.ApplyConvolution(targetPixels, firstPassPixels, sourceRectangle, this.KernelY); - } - - source.SwapPixelsBuffers(targetPixels); + this.ApplyConvolution(firstPassPixels, sourcePixels, source.Bounds, this.KernelX); + this.ApplyConvolution(sourcePixels, firstPassPixels, sourceRectangle, this.KernelY); } } /// - /// Applies the process to the specified portion of the specified at the specified location + /// Applies the process to the specified portion of the specified at the specified location /// and with the specified size. /// /// The target pixels to apply the process to. @@ -66,7 +64,7 @@ namespace ImageSharp.Processing.Processors /// The structure that specifies the portion of the image object to draw. /// /// The kernel operator. - private void ApplyConvolution(PixelAccessor targetPixels, PixelAccessor sourcePixels, Rectangle sourceRectangle, Fast2DArray kernel) + private void ApplyConvolution(PixelAccessor targetPixels, PixelAccessor sourcePixels, Rectangle sourceRectangle, Fast2DArray kernel) { int kernelHeight = kernel.Height; int kernelWidth = kernel.Width; @@ -110,7 +108,7 @@ namespace ImageSharp.Processing.Processors } } - TColor packed = default(TColor); + TPixel packed = default(TPixel); packed.PackFromVector4(destination); targetPixels[x, y] = packed; } diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs index 3ab95c4ce9..17d5e32432 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs @@ -9,15 +9,18 @@ namespace ImageSharp.Processing.Processors using System.Numerics; using System.Threading.Tasks; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// Defines a sampler that uses a 2 dimensional matrix to perform convolution against an image. /// - /// The pixel format. - internal class ConvolutionProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class ConvolutionProcessor : ImageProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The 2d gradient operator. public ConvolutionProcessor(Fast2DArray kernelXY) @@ -31,7 +34,7 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelXY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { int kernelLength = this.KernelXY.Height; int radius = kernelLength >> 1; @@ -43,52 +46,52 @@ namespace ImageSharp.Processing.Processors int maxY = endY - 1; int maxX = endX - 1; - using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) + using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) + using (PixelAccessor sourcePixels = source.Lock()) { - using (PixelAccessor sourcePixels = source.Lock()) - { - Parallel.For( - startY, - endY, - this.ParallelOptions, - y => - { - for (int x = startX; x < endX; x++) - { - float red = 0; - float green = 0; - float blue = 0; - - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelLength; fy++) - { - int fyr = fy - radius; - int offsetY = y + fyr; - - offsetY = offsetY.Clamp(0, maxY); - - for (int fx = 0; fx < kernelLength; fx++) - { - int fxr = fx - radius; - int offsetX = x + fxr; - - offsetX = offsetX.Clamp(0, maxX); - - Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); - currentColor *= this.KernelXY[fy, fx]; - - red += currentColor.X; - green += currentColor.Y; - blue += currentColor.Z; - } - } - - TColor packed = default(TColor); - packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); - targetPixels[x, y] = packed; - } - }); - } + sourcePixels.CopyTo(targetPixels); + + Parallel.For( + startY, + endY, + this.ParallelOptions, + y => + { + for (int x = startX; x < endX; x++) + { + float red = 0; + float green = 0; + float blue = 0; + + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernelLength; fy++) + { + int fyr = fy - radius; + int offsetY = y + fyr; + + offsetY = offsetY.Clamp(0, maxY); + + for (int fx = 0; fx < kernelLength; fx++) + { + int fxr = fx - radius; + int offsetX = x + fxr; + + offsetX = offsetX.Clamp(0, maxX); + + Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); + currentColor *= this.KernelXY[fy, fx]; + + red += currentColor.X; + green += currentColor.Y; + blue += currentColor.Z; + } + } + + TPixel packed = default(TPixel); + packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); + targetPixels[x, y] = packed; + } + }); source.SwapPixelsBuffers(targetPixels); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs index b5c6816569..6f1057e007 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetector2DProcessor.cs @@ -7,15 +7,18 @@ namespace ImageSharp.Processing.Processors { using System; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// Defines a sampler that detects edges within an image using two one-dimensional matrices. /// - /// The pixel format. - internal abstract class EdgeDetector2DProcessor : ImageProcessor, IEdgeDetectorProcessor - where TColor : struct, IPixel + /// The pixel format. + internal abstract class EdgeDetector2DProcessor : ImageProcessor, IEdgeDetectorProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The horizontal gradient operator. /// The vertical gradient operator. @@ -39,17 +42,17 @@ namespace ImageSharp.Processing.Processors public bool Grayscale { get; set; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - new Convolution2DProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); + new Convolution2DProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) { if (this.Grayscale) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs index e92c2d1093..a03d126775 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorCompassProcessor.cs @@ -9,12 +9,15 @@ namespace ImageSharp.Processing.Processors using System.Numerics; using System.Threading.Tasks; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// Defines a sampler that detects edges within an image using a eight two dimensional matrices. /// - /// The pixel format. - internal abstract class EdgeDetectorCompassProcessor : ImageProcessor, IEdgeDetectorProcessor - where TColor : struct, IPixel + /// The pixel format. + internal abstract class EdgeDetectorCompassProcessor : ImageProcessor, IEdgeDetectorProcessor + where TPixel : struct, IPixel { /// /// Gets the North gradient operator @@ -60,16 +63,16 @@ namespace ImageSharp.Processing.Processors public bool Grayscale { get; set; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) { if (this.Grayscale) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle); } } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { Fast2DArray[] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast }; @@ -85,9 +88,9 @@ namespace ImageSharp.Processing.Processors int maxY = Math.Min(source.Height, endY); // we need a clean copy for each pass to start from - using (ImageBase cleanCopy = new Image(source)) + using (ImageBase cleanCopy = new Image(source)) { - new ConvolutionProcessor(kernels[0]).Apply(source, sourceRectangle); + new ConvolutionProcessor(kernels[0]).Apply(source, sourceRectangle); if (kernels.Length == 1) { @@ -112,12 +115,12 @@ namespace ImageSharp.Processing.Processors // ReSharper disable once ForCanBeConvertedToForeach for (int i = 1; i < kernels.Length; i++) { - using (ImageBase pass = new Image(cleanCopy)) + using (ImageBase pass = new Image(cleanCopy)) { - new ConvolutionProcessor(kernels[i]).Apply(pass, sourceRectangle); + new ConvolutionProcessor(kernels[i]).Apply(pass, sourceRectangle); - using (PixelAccessor passPixels = pass.Lock()) - using (PixelAccessor targetPixels = source.Lock()) + using (PixelAccessor passPixels = pass.Lock()) + using (PixelAccessor targetPixels = source.Lock()) { Parallel.For( minY, @@ -131,7 +134,7 @@ namespace ImageSharp.Processing.Processors int offsetX = x - shiftX; // Grab the max components of the two pixels - TColor packed = default(TColor); + TPixel packed = default(TPixel); packed.PackFromVector4(Vector4.Max(passPixels[offsetX, offsetY].ToVector4(), targetPixels[offsetX, offsetY].ToVector4())); targetPixels[offsetX, offsetY] = packed; } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs index d8b491faf5..415b574b8a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/EdgeDetectorProcessor.cs @@ -7,15 +7,18 @@ namespace ImageSharp.Processing.Processors { using System; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// Defines a sampler that detects edges within an image using a single two dimensional matrix. /// - /// The pixel format. - internal abstract class EdgeDetectorProcessor : ImageProcessor, IEdgeDetectorProcessor - where TColor : struct, IPixel + /// The pixel format. + internal abstract class EdgeDetectorProcessor : ImageProcessor, IEdgeDetectorProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The 2d gradient operator. protected EdgeDetectorProcessor(Fast2DArray kernelXY) @@ -32,18 +35,18 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelXY { get; } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) { if (this.Grayscale) { - new GrayscaleBt709Processor().Apply(source, sourceRectangle); + new GrayscaleBt709Processor().Apply(source, sourceRectangle); } } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - new ConvolutionProcessor(this.KernelXY).Apply(source, sourceRectangle); + new ConvolutionProcessor(this.KernelXY).Apply(source, sourceRectangle); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/IEdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/IEdgeDetectorProcessor.cs index 7c0923bbbd..c7c126794d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/IEdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/IEdgeDetectorProcessor.cs @@ -7,12 +7,14 @@ namespace ImageSharp.Processing.Processors { using System; + using ImageSharp.PixelFormats; + /// /// Provides properties and methods allowing the detection of edges within an image. /// - /// The pixel format. - public interface IEdgeDetectorProcessor : IImageProcessor, IEdgeDetectorProcessor - where TColor : struct, IPixel + /// The pixel format. + public interface IEdgeDetectorProcessor : IImageProcessor, IEdgeDetectorProcessor + where TPixel : struct, IPixel { } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs index 20e7b1b176..b1361b514f 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KayyaliProcessor.cs @@ -8,14 +8,17 @@ namespace ImageSharp.Processing.Processors using System; using System.Diagnostics.CodeAnalysis; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// The Kayyali operator filter. /// /// - /// The pixel format. + /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] - internal class KayyaliProcessor : EdgeDetector2DProcessor - where TColor : struct, IPixel + internal class KayyaliProcessor : EdgeDetector2DProcessor + where TPixel : struct, IPixel { /// /// The horizontal gradient operator. @@ -40,7 +43,7 @@ namespace ImageSharp.Processing.Processors }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public KayyaliProcessor() : base(KayyaliX, KayyaliY) diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs index 1b88a2200e..af4700bb97 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/KirschProcessor.cs @@ -8,14 +8,17 @@ namespace ImageSharp.Processing.Processors using System; using System.Diagnostics.CodeAnalysis; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// The Kirsch operator filter. /// /// - /// The pixel format. + /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] - internal class KirschProcessor : EdgeDetectorCompassProcessor - where TColor : struct, IPixel + internal class KirschProcessor : EdgeDetectorCompassProcessor + where TPixel : struct, IPixel { /// /// The North gradient operator diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs index ec6963b1ea..5f58d56f8c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian3X3Processor.cs @@ -8,14 +8,17 @@ namespace ImageSharp.Processing.Processors using System; using System.Diagnostics.CodeAnalysis; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// The Laplacian 3 x 3 operator filter. /// /// - /// The pixel format. + /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] - internal class Laplacian3X3Processor : EdgeDetectorProcessor - where TColor : struct, IPixel + internal class Laplacian3X3Processor : EdgeDetectorProcessor + where TPixel : struct, IPixel { /// /// The 2d gradient operator. @@ -29,7 +32,7 @@ namespace ImageSharp.Processing.Processors }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public Laplacian3X3Processor() : base(Laplacian3X3XY) diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs index cc68c4fb71..2e365374cf 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/Laplacian5X5Processor.cs @@ -8,14 +8,17 @@ namespace ImageSharp.Processing.Processors using System; using System.Diagnostics.CodeAnalysis; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// The Laplacian 5 x 5 operator filter. /// /// - /// The pixel format. + /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] - internal class Laplacian5X5Processor : EdgeDetectorProcessor - where TColor : struct, IPixel + internal class Laplacian5X5Processor : EdgeDetectorProcessor + where TPixel : struct, IPixel { /// /// The 2d gradient operator. @@ -31,7 +34,7 @@ namespace ImageSharp.Processing.Processors }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public Laplacian5X5Processor() : base(Laplacian5X5XY) diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs index f0944e6818..de2594653d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/LaplacianOfGaussianProcessor.cs @@ -8,14 +8,17 @@ namespace ImageSharp.Processing.Processors using System; using System.Diagnostics.CodeAnalysis; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// The Laplacian of Gaussian operator filter. /// /// - /// The pixel format. + /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] - internal class LaplacianOfGaussianProcessor : EdgeDetectorProcessor - where TColor : struct, IPixel + internal class LaplacianOfGaussianProcessor : EdgeDetectorProcessor + where TPixel : struct, IPixel { /// /// The 2d gradient operator. @@ -31,7 +34,7 @@ namespace ImageSharp.Processing.Processors }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public LaplacianOfGaussianProcessor() : base(LaplacianOfGaussianXY) diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs index fdb63d837e..1b2d5f50ed 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/PrewittProcessor.cs @@ -8,14 +8,17 @@ namespace ImageSharp.Processing.Processors using System; using System.Diagnostics.CodeAnalysis; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// The Prewitt operator filter. /// /// - /// The pixel format. + /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] - internal class PrewittProcessor : EdgeDetector2DProcessor - where TColor : struct, IPixel + internal class PrewittProcessor : EdgeDetector2DProcessor + where TPixel : struct, IPixel { /// /// The horizontal gradient operator. @@ -40,7 +43,7 @@ namespace ImageSharp.Processing.Processors }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public PrewittProcessor() : base(PrewittX, PrewittY) diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs index d9c5f5d213..d1b9614b62 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobertsCrossProcessor.cs @@ -8,14 +8,17 @@ namespace ImageSharp.Processing.Processors using System; using System.Diagnostics.CodeAnalysis; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// The Roberts Cross operator filter. /// /// - /// The pixel format. + /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] - internal class RobertsCrossProcessor : EdgeDetector2DProcessor - where TColor : struct, IPixel + internal class RobertsCrossProcessor : EdgeDetector2DProcessor + where TPixel : struct, IPixel { /// /// The horizontal gradient operator. @@ -38,7 +41,7 @@ namespace ImageSharp.Processing.Processors }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public RobertsCrossProcessor() : base(RobertsCrossX, RobertsCrossY) diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs index 681d983c45..bc687f6743 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/RobinsonProcessor.cs @@ -8,14 +8,17 @@ namespace ImageSharp.Processing.Processors using System; using System.Diagnostics.CodeAnalysis; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// The Kirsch operator filter. /// /// - /// The pixel format. + /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] - internal class RobinsonProcessor : EdgeDetectorCompassProcessor - where TColor : struct, IPixel + internal class RobinsonProcessor : EdgeDetectorCompassProcessor + where TPixel : struct, IPixel { /// /// The North gradient operator diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs index c1e83b7f97..339b9741fc 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/ScharrProcessor.cs @@ -8,14 +8,17 @@ namespace ImageSharp.Processing.Processors using System; using System.Diagnostics.CodeAnalysis; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// The Scharr operator filter. /// /// - /// The pixel format. + /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] - internal class ScharrProcessor : EdgeDetector2DProcessor - where TColor : struct, IPixel + internal class ScharrProcessor : EdgeDetector2DProcessor + where TPixel : struct, IPixel { /// /// The horizontal gradient operator. @@ -40,7 +43,7 @@ namespace ImageSharp.Processing.Processors }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public ScharrProcessor() : base(ScharrX, ScharrY) diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs index 0c13fa3d24..b9060ecbca 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetection/SobelProcessor.cs @@ -8,14 +8,17 @@ namespace ImageSharp.Processing.Processors using System; using System.Diagnostics.CodeAnalysis; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// The Sobel operator filter. /// /// - /// The pixel format. + /// The pixel format. [SuppressMessage("ReSharper", "StaticMemberInGenericType", Justification = "We want to use only one instance of each array field for each generic type.")] - internal class SobelProcessor : EdgeDetector2DProcessor - where TColor : struct, IPixel + internal class SobelProcessor : EdgeDetector2DProcessor + where TPixel : struct, IPixel { /// /// The horizontal gradient operator. @@ -40,7 +43,7 @@ namespace ImageSharp.Processing.Processors }; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public SobelProcessor() : base(SobelX, SobelY) diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs index 65a137e359..4cd49e149e 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -7,12 +7,15 @@ namespace ImageSharp.Processing.Processors { using System; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// Applies a Gaussian blur sampler to the image. /// - /// The pixel format. - internal class GaussianBlurProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class GaussianBlurProcessor : ImageProcessor + where TPixel : struct, IPixel { /// /// The maximum size of the kernel in either direction. @@ -25,7 +28,7 @@ namespace ImageSharp.Processing.Processors private readonly float sigma; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The 'sigma' value representing the weight of the blur. public GaussianBlurProcessor(float sigma = 3f) @@ -37,7 +40,7 @@ namespace ImageSharp.Processing.Processors } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. @@ -51,7 +54,7 @@ namespace ImageSharp.Processing.Processors } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'sigma' value representing the weight of the blur. @@ -79,9 +82,9 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); + new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); } /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs index bb3dc6f999..5bb29a67e2 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -7,12 +7,15 @@ namespace ImageSharp.Processing.Processors { using System; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// Applies a Gaussian sharpening sampler to the image. /// - /// The pixel format. - internal class GaussianSharpenProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class GaussianSharpenProcessor : ImageProcessor + where TPixel : struct, IPixel { /// /// The maximum size of the kernel in either direction. @@ -25,7 +28,7 @@ namespace ImageSharp.Processing.Processors private readonly float sigma; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'sigma' value representing the weight of the sharpening. @@ -39,7 +42,7 @@ namespace ImageSharp.Processing.Processors } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. @@ -53,7 +56,7 @@ namespace ImageSharp.Processing.Processors } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'sigma' value representing the weight of the sharpen. @@ -81,9 +84,9 @@ namespace ImageSharp.Processing.Processors public Fast2DArray KernelY { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); + new Convolution2PassProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle); } /// diff --git a/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs index ce48aea1ad..5e7310e32b 100644 --- a/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/AlphaProcessor.cs @@ -9,36 +9,36 @@ namespace ImageSharp.Processing.Processors using System.Numerics; using System.Threading.Tasks; + using ImageSharp.PixelFormats; + /// - /// An to change the alpha component of an . + /// An to change the alpha component of an . /// - /// The pixel format. - internal class AlphaProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class AlphaProcessor : ImageProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The percentage to adjust the opacity of the image. Must be between 0 and 100. + /// The percentage to adjust the opacity of the image. Must be between 0 and 1. /// - /// is less than 0 or is greater than 100. + /// is less than 0 or is greater than 1. /// - public AlphaProcessor(int percent) + public AlphaProcessor(float percent) { - Guard.MustBeBetweenOrEqualTo(percent, 0, 100, nameof(percent)); + Guard.MustBeBetweenOrEqualTo(percent, 0, 1, nameof(percent)); this.Value = percent; } /// /// Gets the alpha value. /// - public int Value { get; } + public float Value { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - float alpha = this.Value / 100F; - int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; @@ -61,9 +61,9 @@ namespace ImageSharp.Processing.Processors startY = 0; } - Vector4 alphaVector = new Vector4(1, 1, 1, alpha); + Vector4 alphaVector = new Vector4(1, 1, 1, this.Value); - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { Parallel.For( minY, @@ -75,7 +75,7 @@ namespace ImageSharp.Processing.Processors for (int x = minX; x < maxX; x++) { int offsetX = x - startX; - TColor packed = default(TColor); + TPixel packed = default(TPixel); packed.PackFromVector4(sourcePixels[offsetX, offsetY].ToVector4() * alphaVector); sourcePixels[offsetX, offsetY] = packed; } diff --git a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs index d928eb1a47..cc95f15fc6 100644 --- a/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/BackgroundColorProcessor.cs @@ -9,29 +9,36 @@ namespace ImageSharp.Processing.Processors using System.Numerics; using System.Threading.Tasks; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// Sets the background color of the image. /// - /// The pixel format. - internal class BackgroundColorProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class BackgroundColorProcessor : ImageProcessor + where TPixel : struct, IPixel { + private readonly GraphicsOptions options; + /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The to set the background color to. - public BackgroundColorProcessor(TColor color) + /// The to set the background color to. + /// The options defining blending algorithum and amount. + public BackgroundColorProcessor(TPixel color, GraphicsOptions options) { this.Value = color; + this.options = options; } /// /// Gets the background color value. /// - public TColor Value { get; } + public TPixel Value { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -55,10 +62,19 @@ namespace ImageSharp.Processing.Processors startY = 0; } - Vector4 backgroundColor = this.Value.ToVector4(); + int width = maxX - minX; - using (PixelAccessor sourcePixels = source.Lock()) + using (Buffer colors = new Buffer(width)) + using (Buffer amount = new Buffer(width)) + using (PixelAccessor sourcePixels = source.Lock()) { + for (int i = 0; i < width; i++) + { + colors[i] = this.Value; + amount[i] = this.options.BlendPercentage; + } + + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(this.options.BlenderMode); Parallel.For( minY, maxY, @@ -66,26 +82,11 @@ namespace ImageSharp.Processing.Processors y => { int offsetY = y - startY; - for (int x = minX; x < maxX; x++) - { - int offsetX = x - startX; - Vector4 color = sourcePixels[offsetX, offsetY].ToVector4(); - float a = color.W; - - if (a < 1 && a > 0) - { - color = Vector4BlendTransforms.PremultipliedLerp(backgroundColor, color, .5F); - } - if (MathF.Abs(a) < Constants.Epsilon) - { - color = backgroundColor; - } + Span destination = sourcePixels.GetRowSpan(offsetY).Slice(minX - startX, width); - TColor packed = default(TColor); - packed.PackFromVector4(color); - sourcePixels[offsetX, offsetY] = packed; - } + // this switched color & destination in the 2nd and 3rd places because we are applying the target colour under the current one + blender.Blend(destination, colors, destination, amount); }); } } diff --git a/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs index 84df5e89e8..f9f1585ea9 100644 --- a/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/BrightnessProcessor.cs @@ -9,15 +9,17 @@ namespace ImageSharp.Processing.Processors using System.Numerics; using System.Threading.Tasks; + using ImageSharp.PixelFormats; + /// - /// An to change the brightness of an . + /// An to change the brightness of an . /// - /// The pixel format. - internal class BrightnessProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class BrightnessProcessor : ImageProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The new brightness of the image. Must be between -100 and 100. /// @@ -35,7 +37,7 @@ namespace ImageSharp.Processing.Processors public int Value { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { float brightness = this.Value / 100F; @@ -61,7 +63,7 @@ namespace ImageSharp.Processing.Processors startY = 0; } - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { Parallel.For( minY, @@ -79,7 +81,7 @@ namespace ImageSharp.Processing.Processors Vector3 transformed = new Vector3(vector.X, vector.Y, vector.Z) + new Vector3(brightness); vector = new Vector4(transformed, vector.W); - TColor packed = default(TColor); + TPixel packed = default(TPixel); packed.PackFromVector4(vector.Compress()); sourcePixels[offsetX, offsetY] = packed; diff --git a/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs index 042e396996..8308c57e2b 100644 --- a/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/ContrastProcessor.cs @@ -9,15 +9,17 @@ namespace ImageSharp.Processing.Processors using System.Numerics; using System.Threading.Tasks; + using ImageSharp.PixelFormats; + /// - /// An to change the contrast of an . + /// An to change the contrast of an . /// - /// The pixel format. - internal class ContrastProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class ContrastProcessor : ImageProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The new contrast of the image. Must be between -100 and 100. /// @@ -35,7 +37,7 @@ namespace ImageSharp.Processing.Processors public int Value { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { float contrast = (100F + this.Value) / 100F; @@ -63,7 +65,7 @@ namespace ImageSharp.Processing.Processors startY = 0; } - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { Parallel.For( minY, @@ -80,7 +82,7 @@ namespace ImageSharp.Processing.Processors vector -= shiftVector; vector *= contrastVector; vector += shiftVector; - TColor packed = default(TColor); + TPixel packed = default(TPixel); packed.PackFromVector4(vector.Compress()); sourcePixels[offsetX, offsetY] = packed; } diff --git a/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs index 4358e89460..a0348970e7 100644 --- a/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/InvertProcessor.cs @@ -9,15 +9,17 @@ namespace ImageSharp.Processing.Processors using System.Numerics; using System.Threading.Tasks; + using ImageSharp.PixelFormats; + /// - /// An to invert the colors of an . + /// An to invert the colors of an . /// - /// The pixel format. - internal class InvertProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class InvertProcessor : ImageProcessor + where TPixel : struct, IPixel { /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -42,7 +44,7 @@ namespace ImageSharp.Processing.Processors startY = 0; } - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { Parallel.For( minY, @@ -57,7 +59,7 @@ namespace ImageSharp.Processing.Processors Vector4 color = sourcePixels[offsetX, offsetY].ToVector4(); Vector3 vector = inverseVector - new Vector3(color.X, color.Y, color.Z); - TColor packed = default(TColor); + TPixel packed = default(TPixel); packed.PackFromVector4(new Vector4(vector, color.W)); sourcePixels[offsetX, offsetY] = packed; } diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs index 957955c6c4..1f06924af0 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs @@ -9,16 +9,18 @@ namespace ImageSharp.Processing.Processors using System.Numerics; using System.Threading.Tasks; + using ImageSharp.PixelFormats; + /// - /// An to apply an oil painting effect to an . + /// An to apply an oil painting effect to an . /// /// Adapted from by Dewald Esterhuizen. - /// The pixel format. - internal class OilPaintingProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class OilPaintingProcessor : ImageProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image. @@ -46,7 +48,7 @@ namespace ImageSharp.Processing.Processors public int BrushSize { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -67,89 +69,89 @@ namespace ImageSharp.Processing.Processors startX = 0; } - using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) + using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) + using (PixelAccessor sourcePixels = source.Lock()) { - using (PixelAccessor sourcePixels = source.Lock()) - { - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => + sourcePixels.CopyTo(targetPixels); + + Parallel.For( + minY, + maxY, + this.ParallelOptions, + y => + { + for (int x = startX; x < endX; x++) { - for (int x = startX; x < endX; x++) + int maxIntensity = 0; + int maxIndex = 0; + + int[] intensityBin = new int[levels]; + float[] redBin = new float[levels]; + float[] blueBin = new float[levels]; + float[] greenBin = new float[levels]; + + for (int fy = 0; fy <= radius; fy++) { - int maxIntensity = 0; - int maxIndex = 0; + int fyr = fy - radius; + int offsetY = y + fyr; - int[] intensityBin = new int[levels]; - float[] redBin = new float[levels]; - float[] blueBin = new float[levels]; - float[] greenBin = new float[levels]; + // Skip the current row + if (offsetY < minY) + { + continue; + } - for (int fy = 0; fy <= radius; fy++) + // Outwith the current bounds so break. + if (offsetY >= maxY) { - int fyr = fy - radius; - int offsetY = y + fyr; + break; + } - // Skip the current row - if (offsetY < minY) - { - continue; - } + for (int fx = 0; fx <= radius; fx++) + { + int fxr = fx - radius; + int offsetX = x + fxr; - // Outwith the current bounds so break. - if (offsetY >= maxY) + // Skip the column + if (offsetX < 0) { - break; + continue; } - for (int fx = 0; fx <= radius; fx++) + if (offsetX < maxX) { - int fxr = fx - radius; - int offsetX = x + fxr; + // ReSharper disable once AccessToDisposedClosure + Vector4 color = sourcePixels[offsetX, offsetY].ToVector4(); - // Skip the column - if (offsetX < 0) - { - continue; - } - - if (offsetX < maxX) - { - // ReSharper disable once AccessToDisposedClosure - Vector4 color = sourcePixels[offsetX, offsetY].ToVector4(); + float sourceRed = color.X; + float sourceBlue = color.Z; + float sourceGreen = color.Y; - float sourceRed = color.X; - float sourceBlue = color.Z; - float sourceGreen = color.Y; + int currentIntensity = (int)Math.Round((sourceBlue + sourceGreen + sourceRed) / 3.0 * (levels - 1)); - int currentIntensity = (int)Math.Round((sourceBlue + sourceGreen + sourceRed) / 3.0 * (levels - 1)); + intensityBin[currentIntensity] += 1; + blueBin[currentIntensity] += sourceBlue; + greenBin[currentIntensity] += sourceGreen; + redBin[currentIntensity] += sourceRed; - intensityBin[currentIntensity] += 1; - blueBin[currentIntensity] += sourceBlue; - greenBin[currentIntensity] += sourceGreen; - redBin[currentIntensity] += sourceRed; - - if (intensityBin[currentIntensity] > maxIntensity) - { - maxIntensity = intensityBin[currentIntensity]; - maxIndex = currentIntensity; - } + if (intensityBin[currentIntensity] > maxIntensity) + { + maxIntensity = intensityBin[currentIntensity]; + maxIndex = currentIntensity; } } + } - float red = MathF.Abs(redBin[maxIndex] / maxIntensity); - float green = MathF.Abs(greenBin[maxIndex] / maxIntensity); - float blue = MathF.Abs(blueBin[maxIndex] / maxIntensity); + float red = MathF.Abs(redBin[maxIndex] / maxIntensity); + float green = MathF.Abs(greenBin[maxIndex] / maxIntensity); + float blue = MathF.Abs(blueBin[maxIndex] / maxIntensity); - TColor packed = default(TColor); - packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); - targetPixels[x, y] = packed; - } + TPixel packed = default(TPixel); + packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); + targetPixels[x, y] = packed; } - }); - } + } + }); source.SwapPixelsBuffers(targetPixels); } diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs index 818b1f5137..0287eaab8e 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs @@ -9,15 +9,17 @@ namespace ImageSharp.Processing.Processors using System.Collections.Generic; using System.Threading.Tasks; + using ImageSharp.PixelFormats; + /// - /// An to pixelate the colors of an . + /// An to pixelate the colors of an . /// - /// The pixel format. - internal class PixelateProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class PixelateProcessor : ImageProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The size of the pixels. Must be greater than 0. /// @@ -35,7 +37,7 @@ namespace ImageSharp.Processing.Processors public int Value { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; @@ -64,51 +66,46 @@ namespace ImageSharp.Processing.Processors // Get the range on the y-plane to choose from. IEnumerable range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size); - using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) + using (PixelAccessor sourcePixels = source.Lock()) { - using (PixelAccessor sourcePixels = source.Lock()) - { - Parallel.ForEach( - range, - this.ParallelOptions, - y => + Parallel.ForEach( + range, + this.ParallelOptions, + y => + { + int offsetY = y - startY; + int offsetPy = offset; + + for (int x = minX; x < maxX; x += size) { - int offsetY = y - startY; - int offsetPy = offset; + int offsetX = x - startX; + int offsetPx = offset; - for (int x = minX; x < maxX; x += size) + // Make sure that the offset is within the boundary of the image. + while (offsetY + offsetPy >= maxY) { - int offsetX = x - startX; - int offsetPx = offset; - - // Make sure that the offset is within the boundary of the image. - while (offsetY + offsetPy >= maxY) - { - offsetPy--; - } + offsetPy--; + } - while (x + offsetPx >= maxX) - { - offsetPx--; - } + while (x + offsetPx >= maxX) + { + offsetPx--; + } - // Get the pixel color in the centre of the soon to be pixelated area. - // ReSharper disable AccessToDisposedClosure - TColor pixel = sourcePixels[offsetX + offsetPx, offsetY + offsetPy]; + // Get the pixel color in the centre of the soon to be pixelated area. + // ReSharper disable AccessToDisposedClosure + TPixel pixel = sourcePixels[offsetX + offsetPx, offsetY + offsetPy]; - // For each pixel in the pixelate size, set it to the centre color. - for (int l = offsetY; l < offsetY + size && l < maxY; l++) + // For each pixel in the pixelate size, set it to the centre color. + for (int l = offsetY; l < offsetY + size && l < maxY; l++) + { + for (int k = offsetX; k < offsetX + size && k < maxX; k++) { - for (int k = offsetX; k < offsetX + size && k < maxX; k++) - { - targetPixels[k, l] = pixel; - } + sourcePixels[k, l] = pixel; } } - }); - - source.SwapPixelsBuffers(targetPixels); - } + } + }); } } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 6eeb7398aa..9de91c47b4 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -9,26 +9,35 @@ namespace ImageSharp.Processing.Processors using System.Numerics; using System.Threading.Tasks; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// - /// An that applies a radial glow effect an . + /// An that applies a radial glow effect an . /// - /// The pixel format. - internal class GlowProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class GlowProcessor : ImageProcessor + where TPixel : struct, IPixel { + private readonly GraphicsOptions options; + private readonly PixelBlender blender; + /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The color or the glow. - public GlowProcessor(TColor color) + /// The options effecting blending and composition. + public GlowProcessor(TPixel color, GraphicsOptions options) { + this.options = options; this.GlowColor = color; + this.blender = PixelOperations.Instance.GetPixelBlender(this.options.BlenderMode); } /// /// Gets or sets the glow color to apply. /// - public TColor GlowColor { get; set; } + public TPixel GlowColor { get; set; } /// /// Gets or sets the the radius. @@ -36,13 +45,13 @@ namespace ImageSharp.Processing.Processors public float Radius { get; set; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; - TColor glowColor = this.GlowColor; + TPixel glowColor = this.GlowColor; Vector2 centre = Rectangle.Center(sourceRectangle).ToVector2(); float maxDistance = this.Radius > 0 ? MathF.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; @@ -63,25 +72,36 @@ namespace ImageSharp.Processing.Processors startY = 0; } - using (PixelAccessor sourcePixels = source.Lock()) + int width = maxX - minX; + using (Buffer rowColors = new Buffer(width)) + using (PixelAccessor sourcePixels = source.Lock()) { + for (int i = 0; i < width; i++) + { + rowColors[i] = glowColor; + } + Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - int offsetY = y - startY; - for (int x = minX; x < maxX; x++) - { - int offsetX = x - startX; - float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY)); - Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4(); - TColor packed = default(TColor); - packed.PackFromVector4(Vector4BlendTransforms.PremultipliedLerp(sourceColor, glowColor.ToVector4(), 1 - (.95F * (distance / maxDistance)))); - sourcePixels[offsetX, offsetY] = packed; - } - }); + minY, + maxY, + this.ParallelOptions, + y => + { + using (Buffer amounts = new Buffer(width)) + { + int offsetY = y - startY; + int offsetX = minX - startX; + for (int i = 0; i < width; i++) + { + float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY)); + amounts[i] = (this.options.BlendPercentage * (1 - (.95F * (distance / maxDistance)))).Clamp(0, 1); + } + + Span destination = sourcePixels.GetRowSpan(offsetY).Slice(offsetX, width); + + this.blender.Blend(destination, destination, rowColors, amounts); + } + }); } } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index 40d6d94ac9..be431b07d2 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -9,26 +9,36 @@ namespace ImageSharp.Processing.Processors using System.Numerics; using System.Threading.Tasks; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// - /// An that applies a radial vignette effect to an . + /// An that applies a radial vignette effect to an . /// - /// The pixel format. - internal class VignetteProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class VignetteProcessor : ImageProcessor + where TPixel : struct, IPixel { + private readonly GraphicsOptions options; + private readonly PixelBlender blender; + /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The color of the vignette. - public VignetteProcessor(TColor color) + /// The options effecting blending and composition. + public VignetteProcessor(TPixel color, GraphicsOptions options) { this.VignetteColor = color; + + this.options = options; + this.blender = PixelOperations.Instance.GetPixelBlender(this.options.BlenderMode); } /// /// Gets or sets the vignette color to apply. /// - public TColor VignetteColor { get; set; } + public TPixel VignetteColor { get; set; } /// /// Gets or sets the the x-radius. @@ -41,13 +51,13 @@ namespace ImageSharp.Processing.Processors public float RadiusY { get; set; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; - TColor vignetteColor = this.VignetteColor; + TPixel vignetteColor = this.VignetteColor; Vector2 centre = Rectangle.Center(sourceRectangle).ToVector2(); float rX = this.RadiusX > 0 ? MathF.Min(this.RadiusX, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; float rY = this.RadiusY > 0 ? MathF.Min(this.RadiusY, sourceRectangle.Height * .5F) : sourceRectangle.Height * .5F; @@ -70,23 +80,34 @@ namespace ImageSharp.Processing.Processors startY = 0; } - using (PixelAccessor sourcePixels = source.Lock()) + int width = maxX - minX; + using (Buffer rowColors = new Buffer(width)) + using (PixelAccessor sourcePixels = source.Lock()) { + for (int i = 0; i < width; i++) + { + rowColors[i] = vignetteColor; + } + Parallel.For( minY, maxY, this.ParallelOptions, y => { - int offsetY = y - startY; - for (int x = minX; x < maxX; x++) + using (Buffer amounts = new Buffer(width)) { - int offsetX = x - startX; - float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY)); - Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4(); - TColor packed = default(TColor); - packed.PackFromVector4(Vector4BlendTransforms.PremultipliedLerp(sourceColor, vignetteColor.ToVector4(), .9F * (distance / maxDistance))); - sourcePixels[offsetX, offsetY] = packed; + int offsetY = y - startY; + int offsetX = minX - startX; + for (int i = 0; i < width; i++) + { + float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY)); + amounts[i] = (this.options.BlendPercentage * (.9F * (distance / maxDistance))).Clamp(0, 1); + } + + Span destination = sourcePixels.GetRowSpan(offsetY).Slice(offsetX, width); + + this.blender.Blend(destination, destination, rowColors, amounts); } }); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index 7d473c55ed..b67ef5bf1e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs @@ -8,15 +8,17 @@ namespace ImageSharp.Processing.Processors using System; using System.Threading.Tasks; + using ImageSharp.PixelFormats; + /// /// Provides methods to allow the cropping of an image. /// - /// The pixel format. - internal class CropProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class CropProcessor : ImageProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The target cropped rectangle. public CropProcessor(Rectangle cropRectangle) @@ -30,7 +32,7 @@ namespace ImageSharp.Processing.Processors public Rectangle CropRectangle { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { if (this.CropRectangle == sourceRectangle) { @@ -42,9 +44,9 @@ namespace ImageSharp.Processing.Processors int minX = Math.Max(this.CropRectangle.X, sourceRectangle.X); int maxX = Math.Min(this.CropRectangle.Right, sourceRectangle.Right); - using (PixelAccessor targetPixels = new PixelAccessor(this.CropRectangle.Width, this.CropRectangle.Height)) + using (PixelAccessor targetPixels = new PixelAccessor(this.CropRectangle.Width, this.CropRectangle.Height)) { - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { Parallel.For( minY, diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs index 049fbf2de0..571c40939e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs @@ -7,16 +7,18 @@ namespace ImageSharp.Processing.Processors { using System; + using ImageSharp.PixelFormats; + /// /// Provides methods to allow the cropping of an image to preserve areas of highest /// entropy. /// - /// The pixel format. - internal class EntropyCropProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class EntropyCropProcessor : ImageProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The threshold to split the image. Must be between 0 and 1. /// @@ -34,15 +36,15 @@ namespace ImageSharp.Processing.Processors public float Value { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { - using (ImageBase temp = new Image(source)) + using (ImageBase temp = new Image(source)) { // Detect the edges. - new SobelProcessor().Apply(temp, sourceRectangle); + new SobelProcessor().Apply(temp, sourceRectangle); // Apply threshold binarization filter. - new BinaryThresholdProcessor(this.Value).Apply(temp, sourceRectangle); + new BinaryThresholdProcessor(this.Value).Apply(temp, sourceRectangle); // Search for the first white pixels Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); @@ -52,7 +54,7 @@ namespace ImageSharp.Processing.Processors return; } - new CropProcessor(rectangle).Apply(source, sourceRectangle); + new CropProcessor(rectangle).Apply(source, sourceRectangle); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs index 290d81799a..2faf779053 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs @@ -8,15 +8,17 @@ namespace ImageSharp.Processing.Processors using System; using System.Threading.Tasks; + using ImageSharp.PixelFormats; + /// /// Provides methods that allow the flipping of an image around its center point. /// - /// The pixel format. - internal class FlipProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class FlipProcessor : ImageProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The used to perform flipping. public FlipProcessor(FlipType flipType) @@ -30,7 +32,7 @@ namespace ImageSharp.Processing.Processors public FlipType FlipType { get; } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { switch (this.FlipType) { @@ -49,15 +51,15 @@ namespace ImageSharp.Processing.Processors /// at half the height of the image. /// /// The source image to apply the process to. - private void FlipX(ImageBase source) + private void FlipX(ImageBase source) { int width = source.Width; int height = source.Height; int halfHeight = (int)Math.Ceiling(source.Height * .5F); - using (PixelAccessor targetPixels = new PixelAccessor(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { Parallel.For( 0, @@ -83,15 +85,15 @@ namespace ImageSharp.Processing.Processors /// at half of the width of the image. /// /// The source image to apply the process to. - private void FlipY(ImageBase source) + private void FlipY(ImageBase source) { int width = source.Width; int height = source.Height; int halfWidth = (int)Math.Ceiling(width * .5F); - using (PixelAccessor targetPixels = new PixelAccessor(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { Parallel.For( 0, diff --git a/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs b/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs index 0c290a9b62..3135551f8a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs @@ -8,12 +8,14 @@ namespace ImageSharp.Processing.Processors using System; using System.Numerics; + using ImageSharp.PixelFormats; + /// /// Provides methods to transform an image using a . /// - /// The pixel format. - internal abstract class Matrix3x2Processor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal abstract class Matrix3x2Processor : ImageProcessor + where TPixel : struct, IPixel { /// /// Gets the rectangle designating the target canvas. @@ -41,7 +43,7 @@ namespace ImageSharp.Processing.Processors /// /// The . /// - protected Matrix3x2 GetCenteredMatrix(ImageBase source, Matrix3x2 matrix) + protected Matrix3x2 GetCenteredMatrix(ImageBase source, Matrix3x2 matrix) { Matrix3x2 translationToTargetCenter = Matrix3x2.CreateTranslation(-this.CanvasRectangle.Width * .5F, -this.CanvasRectangle.Height * .5F); Matrix3x2 translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width * .5F, source.Height * .5F); diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs index 99b143de67..d49f37803f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs @@ -4,15 +4,17 @@ namespace ImageSharp.Processing.Processors using System.Numerics; using System.Runtime.CompilerServices; + using ImageSharp.Memory; + /// /// Conains the definition of and . /// - internal abstract partial class ResamplingWeightedProcessor + internal abstract partial class ResamplingWeightedProcessor { /// /// Points to a collection of of weights allocated in . /// - internal unsafe struct WeightsWindow + internal struct WeightsWindow { /// /// The local left index position @@ -22,9 +24,7 @@ namespace ImageSharp.Processing.Processors /// /// The span of weights pointing to . /// - // TODO: In the case of switching to official System.Memory and System.Buffers.Primitives this should be System.Buffers.Buffer (formerly Memory), because Span is stack-only! - // see: https://github.com/dotnet/corefxlab/blob/873d35ebed7264e2f9adb556f3b61bebc12395d6/docs/specs/memory.md - public BufferSpan Span; + public Span Span; /// /// Initializes a new instance of the struct. @@ -32,7 +32,7 @@ namespace ImageSharp.Processing.Processors /// The local left index /// The span [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal WeightsWindow(int left, BufferSpan span) + internal WeightsWindow(int left, Span span) { this.Left = left; this.Span = span; @@ -41,7 +41,7 @@ namespace ImageSharp.Processing.Processors /// /// Gets an unsafe float* pointer to the beginning of . /// - public float* Ptr => (float*)this.Span.PointerAtOffset; + public ref float Ptr => ref this.Span.DangerousGetPinnableReference(); /// /// Gets the lenghth of the weights window @@ -52,23 +52,23 @@ namespace ImageSharp.Processing.Processors /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance. /// /// The input span of vectors + /// The source row position. /// The weighted sum [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ComputeWeightedRowSum(BufferSpan rowSpan) + public Vector4 ComputeWeightedRowSum(Span rowSpan, int sourceX) { - float* horizontalValues = this.Ptr; + ref float horizontalValues = ref this.Ptr; int left = this.Left; - Vector4* vecPtr = (Vector4*)rowSpan.PointerAtOffset; - vecPtr += left; + ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX); // Destination color components Vector4 result = Vector4.Zero; for (int i = 0; i < this.Length; i++) { - float weight = horizontalValues[i]; - result += (*vecPtr) * weight; - vecPtr++; + float weight = Unsafe.Add(ref horizontalValues, i); + Vector4 v = Unsafe.Add(ref vecPtr, i); + result += v * weight; } return result; @@ -79,39 +79,40 @@ namespace ImageSharp.Processing.Processors /// Applies to all input vectors. /// /// The input span of vectors + /// The source row position. /// The weighted sum [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ComputeExpandedWeightedRowSum(BufferSpan rowSpan) + public Vector4 ComputeExpandedWeightedRowSum(Span rowSpan, int sourceX) { - float* horizontalValues = this.Ptr; + ref float horizontalValues = ref this.Ptr; int left = this.Left; - Vector4* vecPtr = (Vector4*)rowSpan.PointerAtOffset; - vecPtr += left; + ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX); // Destination color components Vector4 result = Vector4.Zero; for (int i = 0; i < this.Length; i++) { - float weight = horizontalValues[i]; - result += (*vecPtr).Expand() * weight; - vecPtr++; + float weight = Unsafe.Add(ref horizontalValues, i); + Vector4 v = Unsafe.Add(ref vecPtr, i); + result += v.Expand() * weight; } return result; } /// - /// Computes the sum of vectors in 'firstPassPixels' at a column pointed by 'x', + /// Computes the sum of vectors in 'firstPassPixels' at a row pointed by 'x', /// weighted by weight values, pointed by this instance. /// /// The buffer of input vectors in row first order - /// The column position + /// The row position + /// The source column position. /// The weighted sum [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ComputeWeightedColumnSum(PinnedImageBuffer firstPassPixels, int x) + public Vector4 ComputeWeightedColumnSum(Buffer2D firstPassPixels, int x, int sourceY) { - float* verticalValues = this.Ptr; + ref float verticalValues = ref this.Ptr; int left = this.Left; // Destination color components @@ -119,8 +120,8 @@ namespace ImageSharp.Processing.Processors for (int i = 0; i < this.Length; i++) { - float yw = verticalValues[i]; - int index = left + i; + float yw = Unsafe.Add(ref verticalValues, i); + int index = left + i + sourceY; result += firstPassPixels[x, index] * yw; } @@ -133,7 +134,7 @@ namespace ImageSharp.Processing.Processors /// internal class WeightsBuffer : IDisposable { - private PinnedImageBuffer dataBuffer; + private Buffer2D dataBuffer; /// /// Initializes a new instance of the class. @@ -142,7 +143,7 @@ namespace ImageSharp.Processing.Processors /// The size of the destination window public WeightsBuffer(int sourceSize, int destinationSize) { - this.dataBuffer = PinnedImageBuffer.CreateClean(sourceSize, destinationSize); + this.dataBuffer = Buffer2D.CreateClean(sourceSize, destinationSize); this.Weights = new WeightsWindow[destinationSize]; } @@ -168,7 +169,7 @@ namespace ImageSharp.Processing.Processors /// The weights public WeightsWindow GetWeightsWindow(int destIdx, int leftIdx, int rightIdx) { - BufferSpan span = this.dataBuffer.GetRowSpan(destIdx).Slice(leftIdx, rightIdx - leftIdx + 1); + Span span = this.dataBuffer.GetRowSpan(destIdx).Slice(leftIdx, rightIdx - leftIdx + 1); return new WeightsWindow(leftIdx, span); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs index 1374e58156..e2f77d812e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs @@ -7,18 +7,21 @@ namespace ImageSharp.Processing.Processors { using System; using System.Buffers; + using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + using ImageSharp.PixelFormats; + /// /// Provides methods that allow the resizing of images using various algorithms. /// Adapted from /// - /// The pixel format. - internal abstract partial class ResamplingWeightedProcessor : ImageProcessor - where TColor : struct, IPixel + /// The pixel format. + internal abstract partial class ResamplingWeightedProcessor : ImageProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The sampler to perform the resize operation. /// The target width. @@ -111,13 +114,15 @@ namespace ImageSharp.Processing.Processors WeightsWindow ws = result.GetWeightsWindow(i, left, right); result.Weights[i] = ws; - float* weights = ws.Ptr; + ref float weights = ref ws.Ptr; for (int j = left; j <= right; j++) { float weight = sampler.GetValue((j - center) / scale); sum += weight; - weights[j - left] = weight; + + // weights[j - left] = weight: + Unsafe.Add(ref weights, j - left) = weight; } // Normalise, best to do it here rather than in the pixel loop later on. @@ -125,7 +130,9 @@ namespace ImageSharp.Processing.Processors { for (int w = 0; w < ws.Length; w++) { - weights[w] = weights[w] / sum; + // weights[w] = weights[w] / sum: + ref float wRef = ref Unsafe.Add(ref weights, w); + wRef = wRef / sum; } } } @@ -134,7 +141,7 @@ namespace ImageSharp.Processing.Processors } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) { if (!(this.Sampler is NearestNeighborResampler)) { @@ -149,7 +156,7 @@ namespace ImageSharp.Processing.Processors } /// - protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) + protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) { base.AfterApply(source, sourceRectangle); this.HorizontalWeights?.Dispose(); diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 944e245ac8..61a64f60ff 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -9,15 +9,18 @@ namespace ImageSharp.Processing.Processors using System.Numerics; using System.Threading.Tasks; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + /// /// Provides methods that allow the resizing of images using various algorithms. /// - /// The pixel format. - internal class ResizeProcessor : ResamplingWeightedProcessor - where TColor : struct, IPixel + /// The pixel format. + internal class ResizeProcessor : ResamplingWeightedProcessor + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The sampler to perform the resize operation. /// The target width. @@ -28,7 +31,7 @@ namespace ImageSharp.Processing.Processors } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The sampler to perform the resize operation. /// The target width. @@ -42,7 +45,7 @@ namespace ImageSharp.Processing.Processors } /// - protected override unsafe void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override unsafe void OnApply(ImageBase source, Rectangle sourceRectangle) { // Jump out, we'll deal with that later. if (source.Width == this.Width && source.Height == this.Height && sourceRectangle == this.ResizeRectangle) @@ -70,9 +73,9 @@ namespace ImageSharp.Processing.Processors float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; - using (PixelAccessor targetPixels = new PixelAccessor(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { Parallel.For( minY, @@ -103,10 +106,10 @@ namespace ImageSharp.Processing.Processors // are the upper and lower bounds of the source rectangle. // TODO: Using a transposed variant of 'firstPassPixels' could eliminate the need for the WeightsWindow.ComputeWeightedColumnSum() method, and improve speed! - using (PixelAccessor targetPixels = new PixelAccessor(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - using (PixelAccessor sourcePixels = source.Lock()) - using (PinnedImageBuffer firstPassPixels = new PinnedImageBuffer(width, source.Height)) + using (PixelAccessor sourcePixels = source.Lock()) + using (Buffer2D firstPassPixels = new Buffer2D(width, source.Height)) { firstPassPixels.Clear(); @@ -117,11 +120,11 @@ namespace ImageSharp.Processing.Processors y => { // TODO: Without Parallel.For() this buffer object could be reused: - using (PinnedBuffer tempRowBuffer = new PinnedBuffer(sourcePixels.Width)) + using (Buffer tempRowBuffer = new Buffer(sourcePixels.Width)) { - BufferSpan sourceRow = sourcePixels.GetRowSpan(y); + Span sourceRow = sourcePixels.GetRowSpan(y); - BulkPixelOperations.Instance.ToVector4( + PixelOperations.Instance.ToVector4( sourceRow, tempRowBuffer, sourceRow.Length); @@ -131,7 +134,7 @@ namespace ImageSharp.Processing.Processors for (int x = minX; x < maxX; x++) { WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; - firstPassPixels[x, y] = window.ComputeExpandedWeightedRowSum(tempRowBuffer); + firstPassPixels[x, y] = window.ComputeExpandedWeightedRowSum(tempRowBuffer, sourceX); } } else @@ -139,7 +142,7 @@ namespace ImageSharp.Processing.Processors for (int x = minX; x < maxX; x++) { WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; - firstPassPixels[x, y] = window.ComputeWeightedRowSum(tempRowBuffer); + firstPassPixels[x, y] = window.ComputeWeightedRowSum(tempRowBuffer, sourceX); } } } @@ -160,9 +163,9 @@ namespace ImageSharp.Processing.Processors for (int x = 0; x < width; x++) { // Destination color components - Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x); + Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY); destination = destination.Compress(); - TColor d = default(TColor); + TPixel d = default(TPixel); d.PackFromVector4(destination); targetPixels[x, y] = d; } @@ -172,9 +175,9 @@ namespace ImageSharp.Processing.Processors for (int x = 0; x < width; x++) { // Destination color components - Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x); + Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY); - TColor d = default(TColor); + TPixel d = default(TPixel); d.PackFromVector4(destination); targetPixels[x, y] = d; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index 16e0b6635f..fc5d29b06a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -9,12 +9,14 @@ namespace ImageSharp.Processing.Processors using System.Numerics; using System.Threading.Tasks; + using ImageSharp.PixelFormats; + /// /// Provides methods that allow the rotating of images. /// - /// The pixel format. - internal class RotateProcessor : Matrix3x2Processor - where TColor : struct, IPixel + /// The pixel format. + internal class RotateProcessor : Matrix3x2Processor + where TPixel : struct, IPixel { /// /// The transform matrix to apply. @@ -32,7 +34,7 @@ namespace ImageSharp.Processing.Processors public bool Expand { get; set; } = true; /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { if (this.OptimizedApply(source)) { @@ -43,9 +45,9 @@ namespace ImageSharp.Processing.Processors int width = this.CanvasRectangle.Width; Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); - using (PixelAccessor targetPixels = new PixelAccessor(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { Parallel.For( 0, @@ -69,7 +71,7 @@ namespace ImageSharp.Processing.Processors } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) { if (MathF.Abs(this.Angle) < Constants.Epsilon || MathF.Abs(this.Angle - 90) < Constants.Epsilon || MathF.Abs(this.Angle - 180) < Constants.Epsilon || MathF.Abs(this.Angle - 270) < Constants.Epsilon) { @@ -88,7 +90,7 @@ namespace ImageSharp.Processing.Processors /// /// The source image. /// The - private bool OptimizedApply(ImageBase source) + private bool OptimizedApply(ImageBase source) { if (MathF.Abs(this.Angle) < Constants.Epsilon) { @@ -121,14 +123,14 @@ namespace ImageSharp.Processing.Processors /// Rotates the image 270 degrees clockwise at the centre point. /// /// The source image. - private void Rotate270(ImageBase source) + private void Rotate270(ImageBase source) { int width = source.Width; int height = source.Height; - using (PixelAccessor targetPixels = new PixelAccessor(height, width)) + using (PixelAccessor targetPixels = new PixelAccessor(height, width)) { - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { Parallel.For( 0, @@ -154,14 +156,14 @@ namespace ImageSharp.Processing.Processors /// Rotates the image 180 degrees clockwise at the centre point. /// /// The source image. - private void Rotate180(ImageBase source) + private void Rotate180(ImageBase source) { int width = source.Width; int height = source.Height; - using (PixelAccessor targetPixels = new PixelAccessor(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { Parallel.For( 0, @@ -186,14 +188,14 @@ namespace ImageSharp.Processing.Processors /// Rotates the image 90 degrees clockwise at the centre point. /// /// The source image. - private void Rotate90(ImageBase source) + private void Rotate90(ImageBase source) { int width = source.Width; int height = source.Height; - using (PixelAccessor targetPixels = new PixelAccessor(height, width)) + using (PixelAccessor targetPixels = new PixelAccessor(height, width)) { - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { Parallel.For( 0, diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index 5fe3f7d958..40ea6a94e5 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -9,12 +9,14 @@ namespace ImageSharp.Processing.Processors using System.Numerics; using System.Threading.Tasks; + using ImageSharp.PixelFormats; + /// /// Provides methods that allow the skewing of images. /// - /// The pixel format. - internal class SkewProcessor : Matrix3x2Processor - where TColor : struct, IPixel + /// The pixel format. + internal class SkewProcessor : Matrix3x2Processor + where TPixel : struct, IPixel { /// /// The transform matrix to apply. @@ -37,15 +39,15 @@ namespace ImageSharp.Processing.Processors public bool Expand { get; set; } = true; /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { int height = this.CanvasRectangle.Height; int width = this.CanvasRectangle.Width; Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); - using (PixelAccessor targetPixels = new PixelAccessor(width, height)) + using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { - using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { Parallel.For( 0, @@ -69,7 +71,7 @@ namespace ImageSharp.Processing.Processors } /// - protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) + protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) { this.processMatrix = Point.CreateSkew(new Point(0, 0), -this.AngleX, -this.AngleY); if (this.Expand) diff --git a/src/ImageSharp/Processing/Transforms/AutoOrient.cs b/src/ImageSharp/Processing/Transforms/AutoOrient.cs index 8c5e22b995..f9d3a60aa1 100644 --- a/src/ImageSharp/Processing/Transforms/AutoOrient.cs +++ b/src/ImageSharp/Processing/Transforms/AutoOrient.cs @@ -6,22 +6,25 @@ namespace ImageSharp { using System; + + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Adjusts an image so that its orientation is suitable for viewing. Adjustments are based on EXIF metadata embedded in the image. /// - /// The pixel format. + /// The pixel format. /// The image to auto rotate. - /// The - public static Image AutoOrient(this Image source) - where TColor : struct, IPixel + /// The + public static Image AutoOrient(this Image source) + where TPixel : struct, IPixel { Orientation orientation = GetExifOrientation(source); @@ -60,11 +63,11 @@ namespace ImageSharp /// /// Returns the current EXIF orientation /// - /// The pixel format. + /// The pixel format. /// The image to auto rotate. /// The - private static Orientation GetExifOrientation(Image source) - where TColor : struct, IPixel + private static Orientation GetExifOrientation(Image source) + where TPixel : struct, IPixel { if (source.MetaData.ExifProfile == null) { diff --git a/src/ImageSharp/Processing/Transforms/Crop.cs b/src/ImageSharp/Processing/Transforms/Crop.cs index 92773aaeac..1cdef56c48 100644 --- a/src/ImageSharp/Processing/Transforms/Crop.cs +++ b/src/ImageSharp/Processing/Transforms/Crop.cs @@ -7,23 +7,25 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Crops an image to the given width and height. /// - /// The pixel format. + /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. - /// The - public static Image Crop(this Image source, int width, int height) - where TColor : struct, IPixel + /// The + public static Image Crop(this Image source, int width, int height) + where TPixel : struct, IPixel { return Crop(source, new Rectangle(0, 0, width, height)); } @@ -31,16 +33,16 @@ namespace ImageSharp /// /// Crops an image to the given rectangle. /// - /// The pixel format. + /// The pixel format. /// The image to crop. /// /// The structure that specifies the portion of the image object to retain. /// - /// The - public static Image Crop(this Image source, Rectangle cropRectangle) - where TColor : struct, IPixel + /// The + public static Image Crop(this Image source, Rectangle cropRectangle) + where TPixel : struct, IPixel { - CropProcessor processor = new CropProcessor(cropRectangle); + CropProcessor processor = new CropProcessor(cropRectangle); source.ApplyProcessor(processor, source.Bounds); return source; diff --git a/src/ImageSharp/Processing/Transforms/EntropyCrop.cs b/src/ImageSharp/Processing/Transforms/EntropyCrop.cs index ad2ce89e3d..2f4a8e3833 100644 --- a/src/ImageSharp/Processing/Transforms/EntropyCrop.cs +++ b/src/ImageSharp/Processing/Transforms/EntropyCrop.cs @@ -7,24 +7,26 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Crops an image to the area of greatest entropy. /// - /// The pixel format. + /// The pixel format. /// The image to crop. /// The threshold for entropic density. - /// The - public static Image EntropyCrop(this Image source, float threshold = .5f) - where TColor : struct, IPixel + /// The + public static Image EntropyCrop(this Image source, float threshold = .5f) + where TPixel : struct, IPixel { - EntropyCropProcessor processor = new EntropyCropProcessor(threshold); + EntropyCropProcessor processor = new EntropyCropProcessor(threshold); source.ApplyProcessor(processor, source.Bounds); return source; diff --git a/src/ImageSharp/Processing/Transforms/Flip.cs b/src/ImageSharp/Processing/Transforms/Flip.cs index ed096eb750..342b4dd46e 100644 --- a/src/ImageSharp/Processing/Transforms/Flip.cs +++ b/src/ImageSharp/Processing/Transforms/Flip.cs @@ -7,25 +7,27 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Flips an image by the given instructions. /// - /// The pixel format. + /// The pixel format. /// The image to rotate, flip, or both. /// The to perform the flip. - /// The - public static Image Flip(this Image source, FlipType flipType) - where TColor : struct, IPixel + /// The + public static Image Flip(this Image source, FlipType flipType) + where TPixel : struct, IPixel { - FlipProcessor processor = new FlipProcessor(flipType); + FlipProcessor processor = new FlipProcessor(flipType); source.ApplyProcessor(processor, source.Bounds); return source; diff --git a/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs b/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs index 4be938c399..c876882662 100644 --- a/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs +++ b/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs @@ -8,6 +8,8 @@ namespace ImageSharp.Processing using System; using System.Linq; + using ImageSharp.PixelFormats; + /// /// Provides methods to help calculate the target rectangle when resizing using the /// enumeration. @@ -17,14 +19,14 @@ namespace ImageSharp.Processing /// /// Calculates the target location and bounds to perform the resize operation against. /// - /// The pixel format. + /// The pixel format. /// The source image. /// The resize options. /// /// The . /// - public static Rectangle CalculateTargetLocationAndBounds(ImageBase source, ResizeOptions options) - where TColor : struct, IPixel + public static Rectangle CalculateTargetLocationAndBounds(ImageBase source, ResizeOptions options) + where TPixel : struct, IPixel { switch (options.Mode) { @@ -48,14 +50,14 @@ namespace ImageSharp.Processing /// /// Calculates the target rectangle for crop mode. /// - /// The pixel format. + /// The pixel format. /// The source image. /// The resize options. /// /// The . /// - private static Rectangle CalculateCropRectangle(ImageBase source, ResizeOptions options) - where TColor : struct, IPixel + private static Rectangle CalculateCropRectangle(ImageBase source, ResizeOptions options) + where TPixel : struct, IPixel { int width = options.Size.Width; int height = options.Size.Height; @@ -167,14 +169,14 @@ namespace ImageSharp.Processing /// /// Calculates the target rectangle for pad mode. /// - /// The pixel format. + /// The pixel format. /// The source image. /// The resize options. /// /// The . /// - private static Rectangle CalculatePadRectangle(ImageBase source, ResizeOptions options) - where TColor : struct, IPixel + private static Rectangle CalculatePadRectangle(ImageBase source, ResizeOptions options) + where TPixel : struct, IPixel { int width = options.Size.Width; int height = options.Size.Height; @@ -248,14 +250,14 @@ namespace ImageSharp.Processing /// /// Calculates the target rectangle for box pad mode. /// - /// The pixel format. + /// The pixel format. /// The source image. /// The resize options. /// /// The . /// - private static Rectangle CalculateBoxPadRectangle(ImageBase source, ResizeOptions options) - where TColor : struct, IPixel + private static Rectangle CalculateBoxPadRectangle(ImageBase source, ResizeOptions options) + where TPixel : struct, IPixel { int width = options.Size.Width; int height = options.Size.Height; @@ -335,14 +337,14 @@ namespace ImageSharp.Processing /// /// Calculates the target rectangle for max mode. /// - /// The pixel format. + /// The pixel format. /// The source image. /// The resize options. /// /// The . /// - private static Rectangle CalculateMaxRectangle(ImageBase source, ResizeOptions options) - where TColor : struct, IPixel + private static Rectangle CalculateMaxRectangle(ImageBase source, ResizeOptions options) + where TPixel : struct, IPixel { int width = options.Size.Width; int height = options.Size.Height; @@ -376,14 +378,14 @@ namespace ImageSharp.Processing /// /// Calculates the target rectangle for min mode. /// - /// The pixel format. + /// The pixel format. /// The source image. /// The resize options. /// /// The . /// - private static Rectangle CalculateMinRectangle(ImageBase source, ResizeOptions options) - where TColor : struct, IPixel + private static Rectangle CalculateMinRectangle(ImageBase source, ResizeOptions options) + where TPixel : struct, IPixel { int width = options.Size.Width; int height = options.Size.Height; diff --git a/src/ImageSharp/Processing/Transforms/Pad.cs b/src/ImageSharp/Processing/Transforms/Pad.cs index bd530ecd82..42851e205d 100644 --- a/src/ImageSharp/Processing/Transforms/Pad.cs +++ b/src/ImageSharp/Processing/Transforms/Pad.cs @@ -7,24 +7,26 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Evenly pads an image to fit the new dimensions. /// - /// The pixel format. + /// The pixel format. /// The source image to pad. /// The new width. /// The new height. - /// The . - public static Image Pad(this Image source, int width, int height) - where TColor : struct, IPixel + /// The . + public static Image Pad(this Image source, int width, int height) + where TPixel : struct, IPixel { ResizeOptions options = new ResizeOptions { diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos2Resampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos2Resampler.cs index 05d6c630f9..a46f38df42 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos2Resampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos2Resampler.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Processing if (x < 2F) { - return ImageMaths.SinC(x) * ImageMaths.SinC(x / 2F); + return MathF.SinC(x) * MathF.SinC(x / 2F); } return 0F; diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos3Resampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos3Resampler.cs index 094e5bd67f..3ad01b2b7b 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos3Resampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos3Resampler.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Processing if (x < 3F) { - return ImageMaths.SinC(x) * ImageMaths.SinC(x / 3F); + return MathF.SinC(x) * MathF.SinC(x / 3F); } return 0F; diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos5Resampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos5Resampler.cs index 9022aaaaf0..9bb8bdd16f 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos5Resampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos5Resampler.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Processing if (x < 5F) { - return ImageMaths.SinC(x) * ImageMaths.SinC(x / 5F); + return MathF.SinC(x) * MathF.SinC(x / 5F); } return 0F; diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos8Resampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos8Resampler.cs index 4769d38476..af8c2c111c 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos8Resampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/Lanczos8Resampler.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Processing if (x < 8F) { - return ImageMaths.SinC(x) * ImageMaths.SinC(x / 8F); + return MathF.SinC(x) * MathF.SinC(x / 8F); } return 0F; diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs b/src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs index 6fafd8ebb2..33a2cc825e 100644 --- a/src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs +++ b/src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs @@ -24,7 +24,7 @@ namespace ImageSharp.Processing if (x < 3F) { - return ImageMaths.SinC(x) * (1F - (x * x / 9.0F)); + return MathF.SinC(x) * (1F - (x * x / 9F)); } return 0F; diff --git a/src/ImageSharp/Processing/Transforms/Resize.cs b/src/ImageSharp/Processing/Transforms/Resize.cs index 1952aa1a7a..543b982976 100644 --- a/src/ImageSharp/Processing/Transforms/Resize.cs +++ b/src/ImageSharp/Processing/Transforms/Resize.cs @@ -7,24 +7,26 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Resizes an image in accordance with the given . /// - /// The pixel format. + /// The pixel format. /// The image to resize. /// The resize options. - /// The + /// The /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, ResizeOptions options) - where TColor : struct, IPixel + public static Image Resize(this Image source, ResizeOptions options) + where TPixel : struct, IPixel { // Ensure size is populated across both dimensions. if (options.Size.Width == 0 && options.Size.Height > 0) @@ -45,13 +47,13 @@ namespace ImageSharp /// /// Resizes an image to the given . /// - /// The pixel format. + /// The pixel format. /// The image to resize. /// The target image size. - /// The + /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, Size size) - where TColor : struct, IPixel + public static Image Resize(this Image source, Size size) + where TPixel : struct, IPixel { return Resize(source, size.Width, size.Height, new BicubicResampler(), false); } @@ -59,14 +61,14 @@ namespace ImageSharp /// /// Resizes an image to the given width and height. /// - /// The pixel format. + /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. - /// The + /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height) - where TColor : struct, IPixel + public static Image Resize(this Image source, int width, int height) + where TPixel : struct, IPixel { return Resize(source, width, height, new BicubicResampler(), false); } @@ -74,15 +76,15 @@ namespace ImageSharp /// /// Resizes an image to the given width and height. /// - /// The pixel format. + /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The + /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, bool compand) - where TColor : struct, IPixel + public static Image Resize(this Image source, int width, int height, bool compand) + where TPixel : struct, IPixel { return Resize(source, width, height, new BicubicResampler(), compand); } @@ -90,15 +92,15 @@ namespace ImageSharp /// /// Resizes an image to the given width and height with the given sampler. /// - /// The pixel format. + /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. /// The to perform the resampling. - /// The + /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, IResampler sampler) - where TColor : struct, IPixel + public static Image Resize(this Image source, int width, int height, IResampler sampler) + where TPixel : struct, IPixel { return Resize(source, width, height, sampler, false); } @@ -106,16 +108,16 @@ namespace ImageSharp /// /// Resizes an image to the given width and height with the given sampler. /// - /// The pixel format. + /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. /// The to perform the resampling. /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The + /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, IResampler sampler, bool compand) - where TColor : struct, IPixel + public static Image Resize(this Image source, int width, int height, IResampler sampler, bool compand) + where TPixel : struct, IPixel { return Resize(source, width, height, sampler, source.Bounds, new Rectangle(0, 0, width, height), compand); } @@ -124,7 +126,7 @@ namespace ImageSharp /// Resizes an image to the given width and height with the given sampler and /// source rectangle. /// - /// The pixel format. + /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. @@ -136,10 +138,10 @@ namespace ImageSharp /// The structure that specifies the portion of the target image object to draw to. /// /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The + /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, IResampler sampler, Rectangle sourceRectangle, Rectangle targetRectangle, bool compand = false) - where TColor : struct, IPixel + public static Image Resize(this Image source, int width, int height, IResampler sampler, Rectangle sourceRectangle, Rectangle targetRectangle, bool compand = false) + where TPixel : struct, IPixel { if (width == 0 && height > 0) { @@ -156,8 +158,8 @@ namespace ImageSharp Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - ResizeProcessor processor = - new ResizeProcessor(sampler, width, height, targetRectangle) { Compand = compand }; + ResizeProcessor processor = + new ResizeProcessor(sampler, width, height, targetRectangle) { Compand = compand }; source.ApplyProcessor(processor, sourceRectangle); return source; diff --git a/src/ImageSharp/Processing/Transforms/Rotate.cs b/src/ImageSharp/Processing/Transforms/Rotate.cs index 76311ef252..de9dbdc3bc 100644 --- a/src/ImageSharp/Processing/Transforms/Rotate.cs +++ b/src/ImageSharp/Processing/Transforms/Rotate.cs @@ -7,23 +7,25 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing; using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Rotates an image by the given angle in degrees, expanding the image to fit the rotated result. /// - /// The pixel format. + /// The pixel format. /// The image to rotate. /// The angle in degrees to perform the rotation. - /// The - public static Image Rotate(this Image source, float degrees) - where TColor : struct, IPixel + /// The + public static Image Rotate(this Image source, float degrees) + where TPixel : struct, IPixel { return Rotate(source, degrees, true); } @@ -31,12 +33,12 @@ namespace ImageSharp /// /// Rotates and flips an image by the given instructions. /// - /// The pixel format. + /// The pixel format. /// The image to rotate. /// The to perform the rotation. - /// The - public static Image Rotate(this Image source, RotateType rotateType) - where TColor : struct, IPixel + /// The + public static Image Rotate(this Image source, RotateType rotateType) + where TPixel : struct, IPixel { return Rotate(source, (float)rotateType, false); } @@ -44,15 +46,15 @@ namespace ImageSharp /// /// Rotates an image by the given angle in degrees. /// - /// The pixel format. + /// The pixel format. /// The image to rotate. /// The angle in degrees to perform the rotation. /// Whether to expand the image to fit the rotated result. - /// The - public static Image Rotate(this Image source, float degrees, bool expand) - where TColor : struct, IPixel + /// The + public static Image Rotate(this Image source, float degrees, bool expand) + where TPixel : struct, IPixel { - RotateProcessor processor = new RotateProcessor { Angle = degrees, Expand = expand }; + RotateProcessor processor = new RotateProcessor { Angle = degrees, Expand = expand }; source.ApplyProcessor(processor, source.Bounds); return source; diff --git a/src/ImageSharp/Processing/Transforms/RotateFlip.cs b/src/ImageSharp/Processing/Transforms/RotateFlip.cs index d6050db3f3..460d004cbc 100644 --- a/src/ImageSharp/Processing/Transforms/RotateFlip.cs +++ b/src/ImageSharp/Processing/Transforms/RotateFlip.cs @@ -6,23 +6,26 @@ namespace ImageSharp { using System; + + using ImageSharp.PixelFormats; + using Processing; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Rotates and flips an image by the given instructions. /// - /// The pixel format. + /// The pixel format. /// The image to rotate, flip, or both. /// The to perform the rotation. /// The to perform the flip. - /// The - public static Image RotateFlip(this Image source, RotateType rotateType, FlipType flipType) - where TColor : struct, IPixel + /// The + public static Image RotateFlip(this Image source, RotateType rotateType, FlipType flipType) + where TPixel : struct, IPixel { return source.Rotate(rotateType).Flip(flipType); } diff --git a/src/ImageSharp/Processing/Transforms/Skew.cs b/src/ImageSharp/Processing/Transforms/Skew.cs index 03fdbcceb4..d42d79225b 100644 --- a/src/ImageSharp/Processing/Transforms/Skew.cs +++ b/src/ImageSharp/Processing/Transforms/Skew.cs @@ -7,23 +7,25 @@ namespace ImageSharp { using System; + using ImageSharp.PixelFormats; + using Processing.Processors; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Skews an image by the given angles in degrees, expanding the image to fit the skewed result. /// - /// The pixel format. + /// The pixel format. /// The image to skew. /// The angle in degrees to perform the rotation along the x-axis. /// The angle in degrees to perform the rotation along the y-axis. - /// The - public static Image Skew(this Image source, float degreesX, float degreesY) - where TColor : struct, IPixel + /// The + public static Image Skew(this Image source, float degreesX, float degreesY) + where TPixel : struct, IPixel { return Skew(source, degreesX, degreesY, true); } @@ -31,16 +33,16 @@ namespace ImageSharp /// /// Skews an image by the given angles in degrees. /// - /// The pixel format. + /// The pixel format. /// The image to skew. /// The angle in degrees to perform the rotation along the x-axis. /// The angle in degrees to perform the rotation along the y-axis. /// Whether to expand the image to fit the skewed result. - /// The - public static Image Skew(this Image source, float degreesX, float degreesY, bool expand) - where TColor : struct, IPixel + /// The + public static Image Skew(this Image source, float degreesX, float degreesY, bool expand) + where TPixel : struct, IPixel { - SkewProcessor processor = new SkewProcessor { AngleX = degreesX, AngleY = degreesY, Expand = expand }; + SkewProcessor processor = new SkewProcessor { AngleX = degreesX, AngleY = degreesY, Expand = expand }; source.ApplyProcessor(processor, source.Bounds); return source; diff --git a/src/ImageSharp/Quantizers/Wu/Box.cs b/src/ImageSharp/Quantizers/Box.cs similarity index 96% rename from src/ImageSharp/Quantizers/Wu/Box.cs rename to src/ImageSharp/Quantizers/Box.cs index e715c7839a..7f2a320873 100644 --- a/src/ImageSharp/Quantizers/Wu/Box.cs +++ b/src/ImageSharp/Quantizers/Box.cs @@ -7,6 +7,7 @@ namespace ImageSharp.Quantizers { /// /// Represents a box color cube. + /// TODO: This should be a struct for performance /// internal sealed class Box { diff --git a/src/ImageSharp/Quantizers/IQuantizer.cs b/src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs similarity index 63% rename from src/ImageSharp/Quantizers/IQuantizer.cs rename to src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs index 88f273e9b6..b7cf2d469a 100644 --- a/src/ImageSharp/Quantizers/IQuantizer.cs +++ b/src/ImageSharp/Quantizers/IQuantizer{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -6,13 +6,14 @@ namespace ImageSharp.Quantizers { using ImageSharp.Dithering; + using ImageSharp.PixelFormats; /// - /// Provides methods for allowing quantization of images pixels. + /// Provides methods for for allowing quantization of images pixels with configurable dithering. /// - /// The pixel format. - public interface IQuantizer : IQuantizer - where TColor : struct, IPixel + /// The pixel format. + public interface IQuantizer : IQuantizer + where TPixel : struct, IPixel { /// /// Quantize an image and return the resulting output pixels. @@ -22,15 +23,13 @@ namespace ImageSharp.Quantizers /// /// A representing a quantized version of the image pixels. /// - QuantizedImage Quantize(ImageBase image, int maxColors); + QuantizedImage Quantize(ImageBase image, int maxColors); } /// - /// Provides methods for allowing dithering of quantized image pixels. + /// Provides methods for allowing quantization of images pixels with configurable dithering. /// - /// The pixel format. - public interface IDitheredQuantizer : IQuantizer - where TColor : struct, IPixel + public interface IQuantizer { /// /// Gets or sets a value indicating whether to apply dithering to the output image. @@ -42,11 +41,4 @@ namespace ImageSharp.Quantizers /// IErrorDiffuser DitherType { get; set; } } - - /// - /// Provides methods for allowing quantization of images pixels. - /// - public interface IQuantizer - { - } } diff --git a/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs b/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs similarity index 82% rename from src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs rename to src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs index 2590f297eb..40bce74c3f 100644 --- a/src/ImageSharp/Quantizers/Octree/OctreeQuantizer.cs +++ b/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -8,19 +8,20 @@ namespace ImageSharp.Quantizers using System; using System.Collections.Generic; using System.Runtime.CompilerServices; + using ImageSharp.PixelFormats; /// /// Encapsulates methods to calculate the color palette if an image using an Octree pattern. /// /// - /// The pixel format. - public sealed class OctreeQuantizer : Quantizer - where TColor : struct, IPixel + /// The pixel format. + public sealed class OctreeQuantizer : Quantizer + where TPixel : struct, IPixel { /// /// A lookup table for colors /// - private readonly Dictionary colorMap = new Dictionary(); + private readonly Dictionary colorMap = new Dictionary(); /// /// The pixel buffer, used to reduce allocations. @@ -40,10 +41,10 @@ namespace ImageSharp.Quantizers /// /// The reduced image palette /// - private TColor[] palette; + private TPixel[] palette; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The Octree quantizer is a two pass algorithm. The initial pass sets up the Octree, @@ -55,30 +56,25 @@ namespace ImageSharp.Quantizers } /// - public override QuantizedImage Quantize(ImageBase image, int maxColors) + public override QuantizedImage Quantize(ImageBase image, int maxColors) { this.colors = maxColors.Clamp(1, 255); - this.octree = new Octree(this.GetBitsNeededForColorDepth(maxColors)); + this.octree = new Octree(this.GetBitsNeededForColorDepth(this.colors)); + this.palette = null; - return base.Quantize(image, maxColors); + return base.Quantize(image, this.colors); } - /// - /// Execute a second pass through the bitmap - /// - /// The source image. - /// The output pixel array - /// The width in pixels of the image - /// The height in pixels of the image - protected override void SecondPass(PixelAccessor source, byte[] output, int width, int height) + /// + protected override void SecondPass(PixelAccessor source, byte[] output, int width, int height) { // Load up the values for the first pixel. We can use these to speed up the second // pass of the algorithm by avoiding transforming rows of identical color. - TColor sourcePixel = source[0, 0]; - TColor previousPixel = sourcePixel; + TPixel sourcePixel = source[0, 0]; + TPixel previousPixel = sourcePixel; byte pixelValue = this.QuantizePixel(sourcePixel); - TColor[] colorPalette = this.GetPalette(); - TColor transformedPixel = colorPalette[pixelValue]; + TPixel[] colorPalette = this.GetPalette(); + TPixel transformedPixel = colorPalette[pixelValue]; for (int y = 0; y < height; y++) { @@ -107,7 +103,7 @@ namespace ImageSharp.Quantizers if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, width, height); + this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, width, height, false); } output[(y * source.Width) + x] = pixelValue; @@ -115,36 +111,17 @@ namespace ImageSharp.Quantizers } } - /// - /// Process the pixel in the first pass of the algorithm - /// - /// - /// The pixel to quantize - /// - /// - /// This function need only be overridden if your quantize algorithm needs two passes, - /// such as an Octree quantizer. - /// - protected override void InitialQuantizePixel(TColor pixel) + /// + protected override void InitialQuantizePixel(TPixel pixel) { // Add the color to the Octree this.octree.AddColor(pixel, this.pixelBuffer); } - /// - /// Retrieve the palette for the quantized image. - /// - /// - /// The new color palette - /// - protected override TColor[] GetPalette() + /// + protected override TPixel[] GetPalette() { - if (this.palette == null) - { - this.palette = this.octree.Palletize(Math.Max(this.colors, 1)); - } - - return this.palette; + return this.palette ?? (this.palette = this.octree.Palletize(Math.Max(this.colors, 1))); } /// @@ -155,13 +132,13 @@ namespace ImageSharp.Quantizers /// The quantized value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private byte QuantizePixel(TColor pixel) + private byte QuantizePixel(TPixel pixel) { if (this.Dither) { // The colors have changed so we need to use Euclidean distance caclulation to find the closest value. // This palette can never be null here. - return this.GetClosestColor(pixel, this.palette, this.colorMap); + return this.GetClosestPixel(pixel, this.palette, this.colorMap); } return (byte)this.octree.GetPaletteIndex(pixel, this.pixelBuffer); @@ -175,6 +152,7 @@ namespace ImageSharp.Quantizers /// /// The /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] private int GetBitsNeededForColorDepth(int colorCount) { return (int)Math.Ceiling(Math.Log(colorCount, 2)); @@ -189,7 +167,7 @@ namespace ImageSharp.Quantizers /// Mask used when getting the appropriate pixels for a given node /// // ReSharper disable once StaticMemberInGenericType - private static readonly int[] Mask = { 0x100, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; + private static readonly int[] Mask = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; /// /// The root of the Octree @@ -214,7 +192,7 @@ namespace ImageSharp.Quantizers /// /// Cache the previous color quantized /// - private TColor previousColor; + private TPixel previousColor; /// /// Initializes a new instance of the class. @@ -228,7 +206,7 @@ namespace ImageSharp.Quantizers this.Leaves = 0; this.reducibleNodes = new OctreeNode[9]; this.root = new OctreeNode(0, this.maxColorBits, this); - this.previousColor = default(TColor); + this.previousColor = default(TPixel); this.previousNode = null; } @@ -247,7 +225,7 @@ namespace ImageSharp.Quantizers /// /// The pixel data. /// The buffer array. - public void AddColor(TColor pixel, byte[] buffer) + public void AddColor(TPixel pixel, byte[] buffer) { // Check if this request is for the same color as the last if (this.previousColor.Equals(pixel)) @@ -277,9 +255,9 @@ namespace ImageSharp.Quantizers /// /// The maximum number of colors /// - /// An with the palletized colors + /// An with the palletized colors /// - public TColor[] Palletize(int colorCount) + public TPixel[] Palletize(int colorCount) { while (this.Leaves > colorCount) { @@ -287,7 +265,7 @@ namespace ImageSharp.Quantizers } // Now palletize the nodes - TColor[] palette = new TColor[colorCount + 1]; + TPixel[] palette = new TPixel[colorCount + 1]; int paletteIndex = 0; this.root.ConstructPalette(palette, ref paletteIndex); @@ -304,7 +282,7 @@ namespace ImageSharp.Quantizers /// /// The . /// - public int GetPaletteIndex(TColor pixel, byte[] buffer) + public int GetPaletteIndex(TPixel pixel, byte[] buffer) { return this.root.GetPaletteIndex(pixel, 0, buffer); } @@ -379,11 +357,6 @@ namespace ImageSharp.Quantizers /// private int blue; - /// - /// Alpha component - /// - private int alpha; - /// /// The index of this node in the palette /// @@ -406,7 +379,7 @@ namespace ImageSharp.Quantizers // Construct the new node this.leaf = level == colorBits; - this.red = this.green = this.blue = this.alpha = 0; + this.red = this.green = this.blue = 0; this.pixelCount = 0; // If a leaf, increment the leaf count @@ -438,7 +411,7 @@ namespace ImageSharp.Quantizers /// The level in the tree /// The tree to which this node belongs /// The buffer array. - public void AddColor(TColor pixel, int colorBits, int level, Octree octree, byte[] buffer) + public void AddColor(TPixel pixel, int colorBits, int level, Octree octree, byte[] buffer) { // Update the color information if this is a leaf if (this.leaf) @@ -454,10 +427,9 @@ namespace ImageSharp.Quantizers int shift = 7 - level; pixel.ToXyzwBytes(buffer, 0); - int index = ((buffer[3] & Mask[0]) >> (shift - 3)) | - ((buffer[2] & Mask[level + 1]) >> (shift - 2)) | - ((buffer[1] & Mask[level + 1]) >> (shift - 1)) | - ((buffer[0] & Mask[level + 1]) >> shift); + int index = ((buffer[2] & Mask[level]) >> (shift - 2)) | + ((buffer[1] & Mask[level]) >> (shift - 1)) | + ((buffer[0] & Mask[level]) >> shift); OctreeNode child = this.children[index]; @@ -479,7 +451,7 @@ namespace ImageSharp.Quantizers /// The number of leaves removed public int Reduce() { - this.red = this.green = this.blue = this.alpha = 0; + this.red = this.green = this.blue = 0; int childNodes = 0; // Loop through all children and add their information to this node @@ -490,7 +462,6 @@ namespace ImageSharp.Quantizers this.red += this.children[index].red; this.green += this.children[index].green; this.blue += this.children[index].blue; - this.alpha += this.children[index].alpha; this.pixelCount += this.children[index].pixelCount; ++childNodes; this.children[index] = null; @@ -509,7 +480,7 @@ namespace ImageSharp.Quantizers /// /// The palette /// The current palette index - public void ConstructPalette(TColor[] palette, ref int index) + public void ConstructPalette(TPixel[] palette, ref int index) { if (this.leaf) { @@ -517,11 +488,10 @@ namespace ImageSharp.Quantizers byte r = (this.red / this.pixelCount).ToByte(); byte g = (this.green / this.pixelCount).ToByte(); byte b = (this.blue / this.pixelCount).ToByte(); - byte a = (this.alpha / this.pixelCount).ToByte(); // And set the color of the palette entry - TColor pixel = default(TColor); - pixel.PackFromBytes(r, g, b, a); + TPixel pixel = default(TPixel); + pixel.PackFromBytes(r, g, b, 255); palette[index] = pixel; // Consume the next palette index @@ -549,7 +519,7 @@ namespace ImageSharp.Quantizers /// /// The representing the index of the pixel in the palette. /// - public int GetPaletteIndex(TColor pixel, int level, byte[] buffer) + public int GetPaletteIndex(TPixel pixel, int level, byte[] buffer) { int index = this.paletteIndex; @@ -558,10 +528,9 @@ namespace ImageSharp.Quantizers int shift = 7 - level; pixel.ToXyzwBytes(buffer, 0); - int pixelIndex = ((buffer[3] & Mask[0]) >> (shift - 3)) | - ((buffer[2] & Mask[level + 1]) >> (shift - 2)) | - ((buffer[1] & Mask[level + 1]) >> (shift - 1)) | - ((buffer[0] & Mask[level + 1]) >> shift); + int pixelIndex = ((buffer[2] & Mask[level]) >> (shift - 2)) | + ((buffer[1] & Mask[level]) >> (shift - 1)) | + ((buffer[0] & Mask[level]) >> shift); if (this.children[pixelIndex] != null) { @@ -581,16 +550,15 @@ namespace ImageSharp.Quantizers /// /// The pixel to add. /// The buffer array. - public void Increment(TColor pixel, byte[] buffer) + public void Increment(TPixel pixel, byte[] buffer) { pixel.ToXyzwBytes(buffer, 0); this.pixelCount++; this.red += buffer[0]; this.green += buffer[1]; this.blue += buffer[2]; - this.alpha += buffer[3]; } } } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs b/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs similarity index 70% rename from src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs rename to src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs index dedb5ca238..7e07da6c3d 100644 --- a/src/ImageSharp/Quantizers/Palette/PaletteQuantizer.cs +++ b/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -7,16 +7,16 @@ namespace ImageSharp.Quantizers { using System; using System.Collections.Generic; - using System.Numerics; using System.Runtime.CompilerServices; + using ImageSharp.PixelFormats; /// /// Encapsulates methods to create a quantized image based upon the given palette. /// /// - /// The pixel format. - public sealed class PaletteQuantizer : Quantizer - where TColor : struct, IPixel + /// The pixel format. + public sealed class PaletteQuantizer : Quantizer + where TPixel : struct, IPixel { /// /// The pixel buffer, used to reduce allocations. @@ -26,32 +26,32 @@ namespace ImageSharp.Quantizers /// /// A lookup table for colors /// - private readonly Dictionary colorMap = new Dictionary(); + private readonly Dictionary colorMap = new Dictionary(); /// /// List of all colors in the palette /// - private TColor[] colors; + private TPixel[] colors; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The color palette. If none is given this will default to the web safe colors defined /// in the CSS Color Module Level 4. /// - public PaletteQuantizer(TColor[] palette = null) + public PaletteQuantizer(TPixel[] palette = null) : base(true) { if (palette == null) { - Color[] constants = ColorConstants.WebSafeColors; - TColor[] safe = new TColor[constants.Length + 1]; + Rgba32[] constants = ColorConstants.WebSafeColors; + TPixel[] safe = new TPixel[constants.Length + 1]; for (int i = 0; i < constants.Length; i++) { constants[i].ToXyzwBytes(this.pixelBuffer, 0); - TColor packed = default(TColor); + TPixel packed = default(TPixel); packed.PackFromBytes(this.pixelBuffer[0], this.pixelBuffer[1], this.pixelBuffer[2], this.pixelBuffer[3]); safe[i] = packed; } @@ -65,28 +65,22 @@ namespace ImageSharp.Quantizers } /// - public override QuantizedImage Quantize(ImageBase image, int maxColors) + public override QuantizedImage Quantize(ImageBase image, int maxColors) { Array.Resize(ref this.colors, maxColors.Clamp(1, 255)); return base.Quantize(image, maxColors); } - /// - /// Execute a second pass through the bitmap - /// - /// The source image. - /// The output pixel array - /// The width in pixels of the image - /// The height in pixels of the image - protected override void SecondPass(PixelAccessor source, byte[] output, int width, int height) + /// + protected override void SecondPass(PixelAccessor source, byte[] output, int width, int height) { // Load up the values for the first pixel. We can use these to speed up the second // pass of the algorithm by avoiding transforming rows of identical color. - TColor sourcePixel = source[0, 0]; - TColor previousPixel = sourcePixel; + TPixel sourcePixel = source[0, 0]; + TPixel previousPixel = sourcePixel; byte pixelValue = this.QuantizePixel(sourcePixel); - TColor[] colorPalette = this.GetPalette(); - TColor transformedPixel = colorPalette[pixelValue]; + TPixel[] colorPalette = this.GetPalette(); + TPixel transformedPixel = colorPalette[pixelValue]; for (int y = 0; y < height; y++) { @@ -115,7 +109,7 @@ namespace ImageSharp.Quantizers if (this.Dither) { // Apply the dithering matrix. We have to reapply the value now as the original has changed. - this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, width, height); + this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, width, height, false); } output[(y * source.Width) + x] = pixelValue; @@ -124,7 +118,7 @@ namespace ImageSharp.Quantizers } /// - protected override TColor[] GetPalette() + protected override TPixel[] GetPalette() { return this.colors; } @@ -137,9 +131,9 @@ namespace ImageSharp.Quantizers /// The quantized value /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private byte QuantizePixel(TColor pixel) + private byte QuantizePixel(TPixel pixel) { - return this.GetClosestColor(pixel, this.GetPalette(), this.colorMap); + return this.GetClosestPixel(pixel, this.GetPalette(), this.colorMap); } } } \ No newline at end of file diff --git a/src/ImageSharp/Quantizers/Options/Quantization.cs b/src/ImageSharp/Quantizers/Quantization.cs similarity index 81% rename from src/ImageSharp/Quantizers/Options/Quantization.cs rename to src/ImageSharp/Quantizers/Quantization.cs index 428c8e8012..039404384d 100644 --- a/src/ImageSharp/Quantizers/Options/Quantization.cs +++ b/src/ImageSharp/Quantizers/Quantization.cs @@ -12,17 +12,20 @@ namespace ImageSharp { /// /// An adaptive Octree quantizer. Fast with good quality. + /// The quantizer only supports a single alpha value. /// Octree, /// /// Xiaolin Wu's Color Quantizer which generates high quality output. + /// The quantizer supports multiple alpha values. /// Wu, /// /// Palette based, Uses the collection of web-safe colors by default. + /// The quantizer supports multiple alpha values. /// Palette } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Quantizers/Quantize.cs b/src/ImageSharp/Quantizers/Quantize.cs index f45cd3f799..a235092787 100644 --- a/src/ImageSharp/Quantizers/Quantize.cs +++ b/src/ImageSharp/Quantizers/Quantize.cs @@ -8,37 +8,38 @@ namespace ImageSharp using System; using System.Threading.Tasks; + using ImageSharp.PixelFormats; using ImageSharp.Quantizers; /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Applies quantization to the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The quantization mode to apply to perform the operation. /// The maximum number of colors to return. Defaults to 256. - /// The . - public static Image Quantize(this Image source, Quantization mode = Quantization.Octree, int maxColors = 256) - where TColor : struct, IPixel + /// The . + public static Image Quantize(this Image source, Quantization mode = Quantization.Octree, int maxColors = 256) + where TPixel : struct, IPixel { - IQuantizer quantizer; + IQuantizer quantizer; switch (mode) { case Quantization.Wu: - quantizer = new WuQuantizer(); + quantizer = new WuQuantizer(); break; case Quantization.Palette: - quantizer = new PaletteQuantizer(); + quantizer = new PaletteQuantizer(); break; default: - quantizer = new OctreeQuantizer(); + quantizer = new OctreeQuantizer(); break; } @@ -48,18 +49,18 @@ namespace ImageSharp /// /// Applies quantization to the image. /// - /// The pixel format. + /// The pixel format. /// The image this method extends. /// The quantizer to apply to perform the operation. /// The maximum number of colors to return. - /// The . - public static Image Quantize(this Image source, IQuantizer quantizer, int maxColors) - where TColor : struct, IPixel + /// The . + public static Image Quantize(this Image source, IQuantizer quantizer, int maxColors) + where TPixel : struct, IPixel { - QuantizedImage quantized = quantizer.Quantize(source, maxColors); + QuantizedImage quantized = quantizer.Quantize(source, maxColors); int palleteCount = quantized.Palette.Length - 1; - using (PixelAccessor pixels = new PixelAccessor(quantized.Width, quantized.Height)) + using (PixelAccessor pixels = new PixelAccessor(quantized.Width, quantized.Height)) { Parallel.For( 0, @@ -70,7 +71,7 @@ namespace ImageSharp for (int x = 0; x < pixels.Width; x++) { int i = x + (y * pixels.Width); - TColor color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; + TPixel color = quantized.Palette[Math.Min(palleteCount, quantized.Pixels[i])]; pixels[x, y] = color; } }); diff --git a/src/ImageSharp/Quantizers/QuantizedImage.cs b/src/ImageSharp/Quantizers/QuantizedImage{TPixel}.cs similarity index 82% rename from src/ImageSharp/Quantizers/QuantizedImage.cs rename to src/ImageSharp/Quantizers/QuantizedImage{TPixel}.cs index 528da77172..c8b2c7df60 100644 --- a/src/ImageSharp/Quantizers/QuantizedImage.cs +++ b/src/ImageSharp/Quantizers/QuantizedImage{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -6,23 +6,23 @@ namespace ImageSharp.Quantizers { using System; - using System.Threading.Tasks; + using ImageSharp.PixelFormats; /// /// Represents a quantized image where the pixels indexed by a color palette. /// - /// The pixel format. - public class QuantizedImage - where TColor : struct, IPixel + /// The pixel format. + public class QuantizedImage + where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The image width. /// The image height. /// The color palette. /// The quantized pixels. - public QuantizedImage(int width, int height, TColor[] palette, byte[] pixels) + public QuantizedImage(int width, int height, TPixel[] palette, byte[] pixels) { Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); @@ -53,7 +53,7 @@ namespace ImageSharp.Quantizers /// /// Gets the color palette of this . /// - public TColor[] Palette { get; } + public TPixel[] Palette { get; } /// /// Gets the pixels of this . diff --git a/src/ImageSharp/Quantizers/Octree/Quantizer.cs b/src/ImageSharp/Quantizers/Quantizer{TPixel}.cs similarity index 83% rename from src/ImageSharp/Quantizers/Octree/Quantizer.cs rename to src/ImageSharp/Quantizers/Quantizer{TPixel}.cs index ccf8da3df6..48f33f98b9 100644 --- a/src/ImageSharp/Quantizers/Octree/Quantizer.cs +++ b/src/ImageSharp/Quantizers/Quantizer{TPixel}.cs @@ -1,22 +1,22 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageSharp.Quantizers { - using System; using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; using ImageSharp.Dithering; + using ImageSharp.PixelFormats; /// /// Encapsulates methods to calculate the color palette of an image. /// - /// The pixel format. - public abstract class Quantizer : IDitheredQuantizer - where TColor : struct, IPixel + /// The pixel format. + public abstract class Quantizer : IQuantizer + where TPixel : struct, IPixel { /// /// Flag used to indicate whether a single pass or two passes are needed for quantization. @@ -24,7 +24,7 @@ namespace ImageSharp.Quantizers private readonly bool singlePass; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// If true, the quantization only needs to loop through the source pixels once @@ -46,7 +46,7 @@ namespace ImageSharp.Quantizers public IErrorDiffuser DitherType { get; set; } = new SierraLite(); /// - public virtual QuantizedImage Quantize(ImageBase image, int maxColors) + public virtual QuantizedImage Quantize(ImageBase image, int maxColors) { Guard.NotNull(image, nameof(image)); @@ -54,9 +54,9 @@ namespace ImageSharp.Quantizers int height = image.Height; int width = image.Width; byte[] quantizedPixels = new byte[width * height]; - TColor[] colorPalette; + TPixel[] colorPalette; - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { // Call the FirstPass function if not a single pass algorithm. // For something like an Octree quantizer, this will run through @@ -66,14 +66,14 @@ namespace ImageSharp.Quantizers this.FirstPass(pixels, width, height); } - // Collect the palette. Octree requires this to be done before the second pass runs. + // Collect the palette. Required before the second pass runs. colorPalette = this.GetPalette(); if (this.Dither) { // We clone the image as we don't want to alter the original. - using (Image clone = new Image(image)) - using (PixelAccessor clonedPixels = clone.Lock()) + using (Image clone = new Image(image)) + using (PixelAccessor clonedPixels = clone.Lock()) { this.SecondPass(clonedPixels, quantizedPixels, width, height); } @@ -84,7 +84,7 @@ namespace ImageSharp.Quantizers } } - return new QuantizedImage(width, height, colorPalette, quantizedPixels); + return new QuantizedImage(width, height, colorPalette, quantizedPixels); } /// @@ -93,7 +93,7 @@ namespace ImageSharp.Quantizers /// The source data /// The width in pixels of the image. /// The height in pixels of the image. - protected virtual void FirstPass(PixelAccessor source, int width, int height) + protected virtual void FirstPass(PixelAccessor source, int width, int height) { // Loop through each row for (int y = 0; y < height; y++) @@ -114,7 +114,7 @@ namespace ImageSharp.Quantizers /// The output pixel array /// The width in pixels of the image /// The height in pixels of the image - protected abstract void SecondPass(PixelAccessor source, byte[] output, int width, int height); + protected abstract void SecondPass(PixelAccessor source, byte[] output, int width, int height); /// /// Override this to process the pixel in the first pass of the algorithm @@ -124,7 +124,7 @@ namespace ImageSharp.Quantizers /// This function need only be overridden if your quantize algorithm needs two passes, /// such as an Octree quantizer. /// - protected virtual void InitialQuantizePixel(TColor pixel) + protected virtual void InitialQuantizePixel(TPixel pixel) { } @@ -132,9 +132,9 @@ namespace ImageSharp.Quantizers /// Retrieve the palette for the quantized image. Can be called more than once so make sure calls are cached. /// /// - /// + /// /// - protected abstract TColor[] GetPalette(); + protected abstract TPixel[] GetPalette(); /// /// Returns the closest color from the palette to the given color by calculating the Euclidean distance. @@ -144,7 +144,7 @@ namespace ImageSharp.Quantizers /// The cache to store the result in. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected byte GetClosestColor(TColor pixel, TColor[] colorPalette, Dictionary cache) + protected byte GetClosestPixel(TPixel pixel, TPixel[] colorPalette, Dictionary cache) { // Check if the color is in the lookup table if (cache.ContainsKey(pixel)) diff --git a/src/ImageSharp/Quantizers/WuArrayPool.cs b/src/ImageSharp/Quantizers/WuArrayPool.cs new file mode 100644 index 0000000000..bd8ee9d6b9 --- /dev/null +++ b/src/ImageSharp/Quantizers/WuArrayPool.cs @@ -0,0 +1,36 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Quantizers +{ + using System.Buffers; + + /// + /// Provides array pooling for the . + /// This is a separate class so that the pools can be shared accross multiple generic quantizer instaces. + /// + internal static class WuArrayPool + { + /// + /// The long array pool. + /// + public static readonly ArrayPool LongPool = ArrayPool.Create(TableLength, 25); + + /// + /// The float array pool. + /// + public static readonly ArrayPool FloatPool = ArrayPool.Create(TableLength, 5); + + /// + /// The byte array pool. + /// + public static readonly ArrayPool BytePool = ArrayPool.Create(TableLength, 5); + + /// + /// The table length. Matches the calculated value in + /// + private const int TableLength = 2471625; + } +} \ No newline at end of file diff --git a/src/ImageSharp/Quantizers/Wu/WuQuantizer.cs b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs similarity index 69% rename from src/ImageSharp/Quantizers/Wu/WuQuantizer.cs rename to src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs index d632cdd73b..fb63c9dcd9 100644 --- a/src/ImageSharp/Quantizers/Wu/WuQuantizer.cs +++ b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -7,8 +7,10 @@ namespace ImageSharp.Quantizers { using System; using System.Buffers; + using System.Collections.Generic; using System.Numerics; - using System.Threading.Tasks; + using System.Runtime.CompilerServices; + using ImageSharp.PixelFormats; /// /// An implementation of Wu's color quantizer with alpha channel. @@ -29,9 +31,9 @@ namespace ImageSharp.Quantizers /// but more expensive versions. /// /// - /// The pixel format. - public sealed class WuQuantizer : IQuantizer - where TColor : struct, IPixel + /// The pixel format. + public class WuQuantizer : Quantizer + where TPixel : struct, IPixel { /// /// The index bits. @@ -59,103 +61,239 @@ namespace ImageSharp.Quantizers private const int TableLength = IndexCount * IndexCount * IndexCount * IndexAlphaCount; /// - /// The long array pool. - /// - private static readonly ArrayPool LongPool = ArrayPool.Create(TableLength, 25); - - /// - /// The double array pool. + /// A buffer for storing pixels /// - private static readonly ArrayPool DoublePool = ArrayPool.Create(TableLength, 5); + private readonly byte[] rgbaBuffer = new byte[4]; /// - /// The byte array pool. + /// A lookup table for colors /// - private static readonly ArrayPool BytePool = ArrayPool.Create(TableLength, 5); + private readonly Dictionary colorMap = new Dictionary(); /// /// Moment of P(c). /// - private readonly long[] vwt; + private long[] vwt; /// /// Moment of r*P(c). /// - private readonly long[] vmr; + private long[] vmr; /// /// Moment of g*P(c). /// - private readonly long[] vmg; + private long[] vmg; /// /// Moment of b*P(c). /// - private readonly long[] vmb; + private long[] vmb; /// /// Moment of a*P(c). /// - private readonly long[] vma; + private long[] vma; /// /// Moment of c^2*P(c). /// - private readonly double[] m2; + private float[] m2; /// /// Color space tag. /// - private readonly byte[] tag; + private byte[] tag; /// - /// A buffer for storing pixels + /// Maximum allowed color depth /// - private readonly byte[] rgbaBuffer = new byte[4]; + private int colors; + + /// + /// The reduced image palette + /// + private TPixel[] palette; + + /// + /// The color cube representing the image palette + /// + private Box[] colorCube; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// + /// + /// The Wu quantizer is a two pass algorithm. The initial pass sets up the 3-D color histogram, + /// the second pass quantizes a color based on the position in the histogram. + /// public WuQuantizer() + : base(false) { - this.vwt = LongPool.Rent(TableLength); - this.vmr = LongPool.Rent(TableLength); - this.vmg = LongPool.Rent(TableLength); - this.vmb = LongPool.Rent(TableLength); - this.vma = LongPool.Rent(TableLength); - this.m2 = DoublePool.Rent(TableLength); - this.tag = BytePool.Rent(TableLength); } /// - public QuantizedImage Quantize(ImageBase image, int maxColors) + public override QuantizedImage Quantize(ImageBase image, int maxColors) { Guard.NotNull(image, nameof(image)); - int colorCount = maxColors.Clamp(1, 256); + this.colors = maxColors.Clamp(1, 255); + this.palette = null; + + try + { + this.vwt = WuArrayPool.LongPool.Rent(TableLength); + this.vmr = WuArrayPool.LongPool.Rent(TableLength); + this.vmg = WuArrayPool.LongPool.Rent(TableLength); + this.vmb = WuArrayPool.LongPool.Rent(TableLength); + this.vma = WuArrayPool.LongPool.Rent(TableLength); + this.m2 = WuArrayPool.FloatPool.Rent(TableLength); + this.tag = WuArrayPool.BytePool.Rent(TableLength); + + return base.Quantize(image, this.colors); + } + finally + { + WuArrayPool.LongPool.Return(this.vwt, true); + WuArrayPool.LongPool.Return(this.vmr, true); + WuArrayPool.LongPool.Return(this.vmg, true); + WuArrayPool.LongPool.Return(this.vmb, true); + WuArrayPool.LongPool.Return(this.vma, true); + WuArrayPool.FloatPool.Return(this.m2, true); + WuArrayPool.BytePool.Return(this.tag, true); + } + } + + /// + protected override TPixel[] GetPalette() + { + if (this.palette == null) + { + this.palette = new TPixel[this.colors]; + for (int k = 0; k < this.colors; k++) + { + this.Mark(this.colorCube[k], (byte)k); + + float weight = Volume(this.colorCube[k], this.vwt); + + if (MathF.Abs(weight) > Constants.Epsilon) + { + float r = Volume(this.colorCube[k], this.vmr) / weight; + float g = Volume(this.colorCube[k], this.vmg) / weight; + float b = Volume(this.colorCube[k], this.vmb) / weight; + float a = Volume(this.colorCube[k], this.vma) / weight; + + TPixel color = default(TPixel); + color.PackFromVector4(new Vector4(r, g, b, a) / 255F); + this.palette[k] = color; + } + } + } + + return this.palette; + } + + /// + protected override void InitialQuantizePixel(TPixel pixel) + { + // Add the color to a 3-D color histogram. + // Colors are expected in r->g->b->a format + pixel.ToXyzwBytes(this.rgbaBuffer, 0); + + byte r = this.rgbaBuffer[0]; + byte g = this.rgbaBuffer[1]; + byte b = this.rgbaBuffer[2]; + byte a = this.rgbaBuffer[3]; + + int inr = r >> (8 - IndexBits); + int ing = g >> (8 - IndexBits); + int inb = b >> (8 - IndexBits); + int ina = a >> (8 - IndexAlphaBits); + + int ind = GetPaletteIndex(inr + 1, ing + 1, inb + 1, ina + 1); + + this.vwt[ind]++; + this.vmr[ind] += r; + this.vmg[ind] += g; + this.vmb[ind] += b; + this.vma[ind] += a; + this.m2[ind] += (r * r) + (g * g) + (b * b) + (a * a); + } + + /// + protected override void FirstPass(PixelAccessor source, int width, int height) + { + // Build up the 3-D color histogram + // Loop through each row + for (int y = 0; y < height; y++) + { + // And loop through each column + for (int x = 0; x < width; x++) + { + // Now I have the pixel, call the FirstPassQuantize function... + this.InitialQuantizePixel(source[x, y]); + } + } - this.Clear(); + this.Get3DMoments(); + this.BuildCube(); + } - using (PixelAccessor imagePixels = image.Lock()) + /// + protected override void SecondPass(PixelAccessor source, byte[] output, int width, int height) + { + // Load up the values for the first pixel. We can use these to speed up the second + // pass of the algorithm by avoiding transforming rows of identical color. + TPixel sourcePixel = source[0, 0]; + TPixel previousPixel = sourcePixel; + byte pixelValue = this.QuantizePixel(sourcePixel); + TPixel[] colorPalette = this.GetPalette(); + TPixel transformedPixel = colorPalette[pixelValue]; + + for (int y = 0; y < height; y++) { - this.Build3DHistogram(imagePixels); - this.Get3DMoments(); + // And loop through each column + for (int x = 0; x < width; x++) + { + // Get the pixel. + sourcePixel = source[x, y]; + + // Check if this is the same as the last pixel. If so use that value + // rather than calculating it again. This is an inexpensive optimization. + if (!previousPixel.Equals(sourcePixel)) + { + // Quantize the pixel + pixelValue = this.QuantizePixel(sourcePixel); + + // And setup the previous pointer + previousPixel = sourcePixel; + + if (this.Dither) + { + transformedPixel = colorPalette[pixelValue]; + } + } - Box[] cube; - this.BuildCube(out cube, ref colorCount); + if (this.Dither) + { + // Apply the dithering matrix. We have to reapply the value now as the original has changed. + this.DitherType.Dither(source, sourcePixel, transformedPixel, x, y, width, height, false); + } - return this.GenerateResult(imagePixels, colorCount, cube); + output[(y * source.Width) + x] = pixelValue; + } } } /// - /// Gets an index. + /// Gets the index index of the given color in the palette. /// /// The red value. /// The green value. /// The blue value. /// The alpha value. /// The index. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetPaletteIndex(int r, int g, int b, int a) { return (r << ((IndexBits * 2) + IndexAlphaBits)) + (r << (IndexBits + IndexAlphaBits + 1)) @@ -169,7 +307,7 @@ namespace ImageSharp.Quantizers /// The cube. /// The moment. /// The result. - private static double Volume(Box cube, long[] moment) + private static float Volume(Box cube, long[] moment) { return moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] - moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] @@ -310,55 +448,6 @@ namespace ImageSharp.Quantizers } } - /// - /// Clears the tables. - /// - private void Clear() - { - Array.Clear(this.vwt, 0, TableLength); - Array.Clear(this.vmr, 0, TableLength); - Array.Clear(this.vmg, 0, TableLength); - Array.Clear(this.vmb, 0, TableLength); - Array.Clear(this.vma, 0, TableLength); - Array.Clear(this.m2, 0, TableLength); - Array.Clear(this.tag, 0, TableLength); - } - - /// - /// Builds a 3-D color histogram of counts, r/g/b, c^2. - /// - /// The pixel accessor. - private void Build3DHistogram(PixelAccessor pixels) - { - for (int y = 0; y < pixels.Height; y++) - { - for (int x = 0; x < pixels.Width; x++) - { - // Colors are expected in r->g->b->a format - pixels[x, y].ToXyzwBytes(this.rgbaBuffer, 0); - - byte r = this.rgbaBuffer[0]; - byte g = this.rgbaBuffer[1]; - byte b = this.rgbaBuffer[2]; - byte a = this.rgbaBuffer[3]; - - int inr = r >> (8 - IndexBits); - int ing = g >> (8 - IndexBits); - int inb = b >> (8 - IndexBits); - int ina = a >> (8 - IndexAlphaBits); - - int ind = GetPaletteIndex(inr + 1, ing + 1, inb + 1, ina + 1); - - this.vwt[ind]++; - this.vmr[ind] += r; - this.vmg[ind] += g; - this.vmb[ind] += b; - this.vma[ind] += a; - this.m2[ind] += (r * r) + (g * g) + (b * b) + (a * a); - } - } - } - /// /// Converts the histogram into moments so that we can rapidly calculate the sums of the above quantities over any desired box. /// @@ -369,14 +458,14 @@ namespace ImageSharp.Quantizers long[] volumeG = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); long[] volumeB = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); long[] volumeA = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); - double[] volume2 = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); + float[] volume2 = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); long[] area = ArrayPool.Shared.Rent(IndexAlphaCount); long[] areaR = ArrayPool.Shared.Rent(IndexAlphaCount); long[] areaG = ArrayPool.Shared.Rent(IndexAlphaCount); long[] areaB = ArrayPool.Shared.Rent(IndexAlphaCount); long[] areaA = ArrayPool.Shared.Rent(IndexAlphaCount); - double[] area2 = ArrayPool.Shared.Rent(IndexAlphaCount); + float[] area2 = ArrayPool.Shared.Rent(IndexAlphaCount); try { @@ -405,7 +494,7 @@ namespace ImageSharp.Quantizers long lineG = 0; long lineB = 0; long lineA = 0; - double line2 = 0; + float line2 = 0; for (int a = 1; a < IndexAlphaCount; a++) { @@ -454,14 +543,14 @@ namespace ImageSharp.Quantizers ArrayPool.Shared.Return(volumeG); ArrayPool.Shared.Return(volumeB); ArrayPool.Shared.Return(volumeA); - ArrayPool.Shared.Return(volume2); + ArrayPool.Shared.Return(volume2); ArrayPool.Shared.Return(area); ArrayPool.Shared.Return(areaR); ArrayPool.Shared.Return(areaG); ArrayPool.Shared.Return(areaB); ArrayPool.Shared.Return(areaA); - ArrayPool.Shared.Return(area2); + ArrayPool.Shared.Return(area2); } } @@ -469,15 +558,15 @@ namespace ImageSharp.Quantizers /// Computes the weighted variance of a box cube. /// /// The cube. - /// The . - private double Variance(Box cube) + /// The . + private float Variance(Box cube) { - double dr = Volume(cube, this.vmr); - double dg = Volume(cube, this.vmg); - double db = Volume(cube, this.vmb); - double da = Volume(cube, this.vma); + float dr = Volume(cube, this.vmr); + float dg = Volume(cube, this.vmg); + float db = Volume(cube, this.vmb); + float da = Volume(cube, this.vma); - double xx = + float xx = this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] - this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] - this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A1)] @@ -515,8 +604,8 @@ namespace ImageSharp.Quantizers /// The whole blue. /// The whole alpha. /// The whole weight. - /// The . - private double Maximize(Box cube, int direction, int first, int last, out int cut, double wholeR, double wholeG, double wholeB, double wholeA, double wholeW) + /// The . + private float Maximize(Box cube, int direction, int first, int last, out int cut, float wholeR, float wholeG, float wholeB, float wholeA, float wholeW) { long baseR = Bottom(cube, direction, this.vmr); long baseG = Bottom(cube, direction, this.vmg); @@ -524,20 +613,20 @@ namespace ImageSharp.Quantizers long baseA = Bottom(cube, direction, this.vma); long baseW = Bottom(cube, direction, this.vwt); - double max = 0.0; + float max = 0F; cut = -1; for (int i = first; i < last; i++) { - double halfR = baseR + Top(cube, direction, i, this.vmr); - double halfG = baseG + Top(cube, direction, i, this.vmg); - double halfB = baseB + Top(cube, direction, i, this.vmb); - double halfA = baseA + Top(cube, direction, i, this.vma); - double halfW = baseW + Top(cube, direction, i, this.vwt); + float halfR = baseR + Top(cube, direction, i, this.vmr); + float halfG = baseG + Top(cube, direction, i, this.vmg); + float halfB = baseB + Top(cube, direction, i, this.vmb); + float halfA = baseA + Top(cube, direction, i, this.vma); + float halfW = baseW + Top(cube, direction, i, this.vwt); - double temp; + float temp; - if (Math.Abs(halfW) < Constants.Epsilon) + if (MathF.Abs(halfW) < Constants.Epsilon) { continue; } @@ -550,7 +639,7 @@ namespace ImageSharp.Quantizers halfA = wholeA - halfA; halfW = wholeW - halfW; - if (Math.Abs(halfW) < Constants.Epsilon) + if (MathF.Abs(halfW) < Constants.Epsilon) { continue; } @@ -575,21 +664,16 @@ namespace ImageSharp.Quantizers /// Returns a value indicating whether the box has been split. private bool Cut(Box set1, Box set2) { - double wholeR = Volume(set1, this.vmr); - double wholeG = Volume(set1, this.vmg); - double wholeB = Volume(set1, this.vmb); - double wholeA = Volume(set1, this.vma); - double wholeW = Volume(set1, this.vwt); - - int cutr; - int cutg; - int cutb; - int cuta; - - double maxr = this.Maximize(set1, 0, set1.R0 + 1, set1.R1, out cutr, wholeR, wholeG, wholeB, wholeA, wholeW); - double maxg = this.Maximize(set1, 1, set1.G0 + 1, set1.G1, out cutg, wholeR, wholeG, wholeB, wholeA, wholeW); - double maxb = this.Maximize(set1, 2, set1.B0 + 1, set1.B1, out cutb, wholeR, wholeG, wholeB, wholeA, wholeW); - double maxa = this.Maximize(set1, 3, set1.A0 + 1, set1.A1, out cuta, wholeR, wholeG, wholeB, wholeA, wholeW); + float wholeR = Volume(set1, this.vmr); + float wholeG = Volume(set1, this.vmg); + float wholeB = Volume(set1, this.vmb); + float wholeA = Volume(set1, this.vma); + float wholeW = Volume(set1, this.vwt); + + float maxr = this.Maximize(set1, 0, set1.R0 + 1, set1.R1, out int cutr, wholeR, wholeG, wholeB, wholeA, wholeW); + float maxg = this.Maximize(set1, 1, set1.G0 + 1, set1.G1, out int cutg, wholeR, wholeG, wholeB, wholeA, wholeW); + float maxb = this.Maximize(set1, 2, set1.B0 + 1, set1.B1, out int cutb, wholeR, wholeG, wholeB, wholeA, wholeW); + float maxa = this.Maximize(set1, 3, set1.A0 + 1, set1.A1, out int cuta, wholeR, wholeG, wholeB, wholeA, wholeW); int dir; @@ -686,40 +770,38 @@ namespace ImageSharp.Quantizers /// /// Builds the cube. /// - /// The cube. - /// The color count. - private void BuildCube(out Box[] cube, ref int colorCount) + private void BuildCube() { - cube = new Box[colorCount]; - double[] vv = new double[colorCount]; + this.colorCube = new Box[this.colors]; + float[] vv = new float[this.colors]; - for (int i = 0; i < colorCount; i++) + for (int i = 0; i < this.colors; i++) { - cube[i] = new Box(); + this.colorCube[i] = new Box(); } - cube[0].R0 = cube[0].G0 = cube[0].B0 = cube[0].A0 = 0; - cube[0].R1 = cube[0].G1 = cube[0].B1 = IndexCount - 1; - cube[0].A1 = IndexAlphaCount - 1; + this.colorCube[0].R0 = this.colorCube[0].G0 = this.colorCube[0].B0 = this.colorCube[0].A0 = 0; + this.colorCube[0].R1 = this.colorCube[0].G1 = this.colorCube[0].B1 = IndexCount - 1; + this.colorCube[0].A1 = IndexAlphaCount - 1; int next = 0; - for (int i = 1; i < colorCount; i++) + for (int i = 1; i < this.colors; i++) { - if (this.Cut(cube[next], cube[i])) + if (this.Cut(this.colorCube[next], this.colorCube[i])) { - vv[next] = cube[next].Volume > 1 ? this.Variance(cube[next]) : 0.0; - vv[i] = cube[i].Volume > 1 ? this.Variance(cube[i]) : 0.0; + vv[next] = this.colorCube[next].Volume > 1 ? this.Variance(this.colorCube[next]) : 0F; + vv[i] = this.colorCube[i].Volume > 1 ? this.Variance(this.colorCube[i]) : 0F; } else { - vv[next] = 0.0; + vv[next] = 0F; i--; } next = 0; - double temp = vv[0]; + float temp = vv[0]; for (int k = 1; k <= i; k++) { if (vv[k] > temp) @@ -731,79 +813,38 @@ namespace ImageSharp.Quantizers if (temp <= 0.0) { - colorCount = i + 1; + this.colors = i + 1; break; } } } /// - /// Generates the quantized result. + /// Process the pixel in the second pass of the algorithm /// - /// The image pixels. - /// The color count. - /// The cube. - /// The result. - private QuantizedImage GenerateResult(PixelAccessor imagePixels, int colorCount, Box[] cube) + /// The pixel to quantize + /// + /// The quantized value + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private byte QuantizePixel(TPixel pixel) { - TColor[] pallette = new TColor[colorCount]; - byte[] pixels = new byte[imagePixels.Width * imagePixels.Height]; - int width = imagePixels.Width; - int height = imagePixels.Height; - - for (int k = 0; k < colorCount; k++) + if (this.Dither) { - this.Mark(cube[k], (byte)k); - - double weight = Volume(cube[k], this.vwt); - - if (Math.Abs(weight) > Constants.Epsilon) - { - float r = (float)(Volume(cube[k], this.vmr) / weight); - float g = (float)(Volume(cube[k], this.vmg) / weight); - float b = (float)(Volume(cube[k], this.vmb) / weight); - float a = (float)(Volume(cube[k], this.vma) / weight); - - TColor color = default(TColor); - color.PackFromVector4(new Vector4(r, g, b, a) / 255F); - pallette[k] = color; - } + // The colors have changed so we need to use Euclidean distance caclulation to find the closest value. + // This palette can never be null here. + return this.GetClosestPixel(pixel, this.palette, this.colorMap); } - Parallel.For( - 0, - height, - imagePixels.ParallelOptions, - y => - { - byte[] rgba = ArrayPool.Shared.Rent(4); - for (int x = 0; x < width; x++) - { - // Expected order r->g->b->a - imagePixels[x, y].ToXyzwBytes(rgba, 0); - - int r = rgba[0] >> (8 - IndexBits); - int g = rgba[1] >> (8 - IndexBits); - int b = rgba[2] >> (8 - IndexBits); - int a = rgba[3] >> (8 - IndexAlphaBits); - - int ind = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1); - pixels[(y * width) + x] = this.tag[ind]; - } - - ArrayPool.Shared.Return(rgba); - }); + // Expected order r->g->b->a + pixel.ToXyzwBytes(this.rgbaBuffer, 0); - // Cleanup - LongPool.Return(this.vwt); - LongPool.Return(this.vmr); - LongPool.Return(this.vmg); - LongPool.Return(this.vmb); - LongPool.Return(this.vma); - DoublePool.Return(this.m2); - BytePool.Return(this.tag); + int r = this.rgbaBuffer[0] >> (8 - IndexBits); + int g = this.rgbaBuffer[1] >> (8 - IndexBits); + int b = this.rgbaBuffer[2] >> (8 - IndexBits); + int a = this.rgbaBuffer[3] >> (8 - IndexAlphaBits); - return new QuantizedImage(width, height, pallette, pixels); + return this.tag[GetPaletteIndex(r + 1, g + 1, b + 1, a + 1)]; } } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs index e912ea29f6..30d93e3d94 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs @@ -6,7 +6,8 @@ using BenchmarkDotNet.Attributes; using ImageSharp; - + using ImageSharp.Memory; + /// /// Compares two implementation candidates for general BulkPixelOperations.ToVector4(): /// - One iterating with pointers @@ -14,9 +15,9 @@ /// public unsafe class PackFromVector4ReferenceVsPointer { - private PinnedBuffer destination; + private Buffer destination; - private PinnedBuffer source; + private Buffer source; [Params(16, 128, 1024)] public int Count { get; set; } @@ -24,8 +25,10 @@ [Setup] public void Setup() { - this.destination = new PinnedBuffer(this.Count); - this.source = new PinnedBuffer(this.Count * 4); + this.destination = new Buffer(this.Count); + this.source = new Buffer(this.Count * 4); + this.source.Pin(); + this.destination.Pin(); } [Cleanup] @@ -38,15 +41,15 @@ [Benchmark(Baseline = true)] public void PackUsingPointers() { - Vector4* sp = (Vector4*)this.source.Pointer; - byte* dp = (byte*)this.destination.Pointer; + Vector4* sp = (Vector4*)this.source.Pin(); + byte* dp = (byte*)this.destination.Pin(); int count = this.Count; - int size = sizeof(ImageSharp.Color); + int size = sizeof(Rgba32); for (int i = 0; i < count; i++) { Vector4 v = Unsafe.Read(sp); - ImageSharp.Color c = default(ImageSharp.Color); + Rgba32 c = default(Rgba32); c.PackFromVector4(v); Unsafe.Write(dp, c); @@ -59,7 +62,7 @@ public void PackUsingReferences() { ref Vector4 sp = ref this.source.Array[0]; - ref ImageSharp.Color dp = ref this.destination.Array[0]; + ref Rgba32 dp = ref this.destination.Array[0]; int count = this.Count; for (int i = 0; i < count; i++) diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs index 1c541d28b3..501ae79497 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs @@ -3,14 +3,15 @@ namespace ImageSharp.Benchmarks.Color.Bulk { using BenchmarkDotNet.Attributes; - using Color = ImageSharp.Color; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; - public abstract class PackFromXyzw - where TColor : struct, IPixel + public abstract class PackFromXyzw + where TPixel : struct, IPixel { - private PinnedBuffer destination; + private Buffer destination; - private PinnedBuffer source; + private Buffer source; [Params(16, 128, 1024)] public int Count { get; set; } @@ -18,8 +19,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Setup] public void Setup() { - this.destination = new PinnedBuffer(this.Count); - this.source = new PinnedBuffer(this.Count * 4); + this.destination = new Buffer(this.Count); + this.source = new Buffer(this.Count * 4); } [Cleanup] @@ -33,12 +34,12 @@ namespace ImageSharp.Benchmarks.Color.Bulk public void PerElement() { byte[] s = this.source.Array; - TColor[] d = this.destination.Array; + TPixel[] d = this.destination.Array; for (int i = 0; i < this.Count; i++) { int i4 = i * 4; - TColor c = default(TColor); + TPixel c = default(TPixel); c.PackFromBytes(s[i4], s[i4 + 1], s[i4 + 2], s[i4 + 3]); d[i] = c; } @@ -47,17 +48,17 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Benchmark] public void CommonBulk() { - new BulkPixelOperations().PackFromXyzwBytes(this.source, this.destination, this.Count); + new PixelOperations().PackFromXyzwBytes(this.source, this.destination, this.Count); } [Benchmark] public void OptimizedBulk() { - BulkPixelOperations.Instance.PackFromXyzwBytes(this.source, this.destination, this.Count); + PixelOperations.Instance.PackFromXyzwBytes(this.source, this.destination, this.Count); } } - public class PackFromXyzw_Color : PackFromXyzw + public class PackFromXyzw_Rgba32 : PackFromXyzw { } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs index b48eaa35af..65a2988b98 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs @@ -5,12 +5,15 @@ namespace ImageSharp.Benchmarks.Color.Bulk using BenchmarkDotNet.Attributes; - public abstract class ToVector4 - where TColor : struct, IPixel + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + + public abstract class ToVector4 + where TPixel : struct, IPixel { - private PinnedBuffer source; + private Buffer source; - private PinnedBuffer destination; + private Buffer destination; [Params(64, 300, 1024)] public int Count { get; set; } @@ -18,8 +21,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Setup] public void Setup() { - this.source = new PinnedBuffer(this.Count); - this.destination = new PinnedBuffer(this.Count); + this.source = new Buffer(this.Count); + this.destination = new Buffer(this.Count); } [Cleanup] @@ -32,12 +35,12 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Benchmark(Baseline = true)] public void PerElement() { - TColor[] s = this.source.Array; + TPixel[] s = this.source.Array; Vector4[] d = this.destination.Array; for (int i = 0; i < this.Count; i++) { - TColor c = s[i]; + TPixel c = s[i]; d[i] = c.ToVector4(); } } @@ -45,17 +48,17 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Benchmark] public void CommonBulk() { - new BulkPixelOperations().ToVector4(this.source, this.destination, this.Count); + new PixelOperations().ToVector4(this.source, this.destination, this.Count); } [Benchmark] public void OptimizedBulk() { - BulkPixelOperations.Instance.ToVector4(this.source, this.destination, this.Count); + PixelOperations.Instance.ToVector4(this.source, this.destination, this.Count); } } - public class ToVector4_Color : ToVector4 + public class ToVector4_Rgba32 : ToVector4 { } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs index bc59dba4eb..3c75fc2d19 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs @@ -3,14 +3,15 @@ namespace ImageSharp.Benchmarks.Color.Bulk { using BenchmarkDotNet.Attributes; - using Color = ImageSharp.Color; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; - public abstract class ToXyz - where TColor : struct, IPixel + public abstract class ToXyz + where TPixel : struct, IPixel { - private PinnedBuffer source; + private Buffer source; - private PinnedBuffer destination; + private Buffer destination; [Params(16, 128, 1024)] public int Count { get; set; } @@ -18,8 +19,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Setup] public void Setup() { - this.source = new PinnedBuffer(this.Count); - this.destination = new PinnedBuffer(this.Count * 3); + this.source = new Buffer(this.Count); + this.destination = new Buffer(this.Count * 3); } [Cleanup] @@ -32,12 +33,12 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Benchmark(Baseline = true)] public void PerElement() { - TColor[] s = this.source.Array; + TPixel[] s = this.source.Array; byte[] d = this.destination.Array; for (int i = 0; i < this.Count; i++) { - TColor c = s[i]; + TPixel c = s[i]; c.ToXyzBytes(d, i * 4); } } @@ -45,17 +46,17 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Benchmark] public void CommonBulk() { - new BulkPixelOperations().ToXyzBytes(this.source, this.destination, this.Count); + new PixelOperations().ToXyzBytes(this.source, this.destination, this.Count); } [Benchmark] public void OptimizedBulk() { - BulkPixelOperations.Instance.ToXyzBytes(this.source, this.destination, this.Count); + PixelOperations.Instance.ToXyzBytes(this.source, this.destination, this.Count); } } - public class ToXyz_Color : ToXyz + public class ToXyz_Rgba32 : ToXyz { } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs index a4ec6f6dc3..f64bf561b0 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs @@ -8,14 +8,15 @@ namespace ImageSharp.Benchmarks.Color.Bulk { using BenchmarkDotNet.Attributes; - using Color = ImageSharp.Color; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; - public abstract class ToXyzw - where TColor : struct, IPixel + public abstract class ToXyzw + where TPixel : struct, IPixel { - private PinnedBuffer source; + private Buffer source; - private PinnedBuffer destination; + private Buffer destination; [Params(16, 128, 1024)] public int Count { get; set; } @@ -23,8 +24,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Setup] public void Setup() { - this.source = new PinnedBuffer(this.Count); - this.destination = new PinnedBuffer(this.Count * 4); + this.source = new Buffer(this.Count); + this.destination = new Buffer(this.Count * 4); } [Cleanup] @@ -37,12 +38,12 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Benchmark(Baseline = true)] public void PerElement() { - TColor[] s = this.source.Array; + TPixel[] s = this.source.Array; byte[] d = this.destination.Array; for (int i = 0; i < this.Count; i++) { - TColor c = s[i]; + TPixel c = s[i]; c.ToXyzwBytes(d, i * 4); } } @@ -50,21 +51,21 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Benchmark] public void CommonBulk() { - new BulkPixelOperations().ToXyzwBytes(this.source, this.destination, this.Count); + new PixelOperations().ToXyzwBytes(this.source, this.destination, this.Count); } [Benchmark] public void OptimizedBulk() { - BulkPixelOperations.Instance.ToXyzwBytes(this.source, this.destination, this.Count); + PixelOperations.Instance.ToXyzwBytes(this.source, this.destination, this.Count); } } - - public class ToXyzw_Color : ToXyzw + + public class ToXyzw_Rgba32 : ToXyzw { } - public class ToXyzw_Argb : ToXyzw + public class ToXyzw_Argb32 : ToXyzw { } } diff --git a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs index 3ee28d06b4..a641baafe5 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs @@ -7,7 +7,8 @@ namespace ImageSharp.Benchmarks { using BenchmarkDotNet.Attributes; - using CoreColor = ImageSharp.Color; + using ImageSharp.PixelFormats; + using SystemColor = System.Drawing.Color; public class ColorEquality @@ -21,7 +22,7 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Color Equals")] public bool ColorEqual() { - return new CoreColor(128, 128, 128, 128).Equals(new CoreColor(128, 128, 128, 128)); + return new Rgba32(128, 128, 128, 128).Equals(new Rgba32(128, 128, 128, 128)); } } } diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs index 3e60cae4dc..183782d915 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs @@ -12,9 +12,7 @@ namespace ImageSharp.Benchmarks using BenchmarkDotNet.Attributes; - using CoreColor = ImageSharp.Color; - using CoreImage = ImageSharp.Image; - using CorePoint = ImageSharp.Point; + using ImageSharp.PixelFormats; public class DrawBeziers : BenchmarkBase { @@ -47,10 +45,10 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Draw Beziers")] public void DrawLinesCore() { - using (CoreImage image = new CoreImage(800, 800)) + using (Image image = new Image(800, 800)) { image.DrawBeziers( - CoreColor.HotPink, + Rgba32.HotPink, 10, new[] { new Vector2(10, 500), diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs index ee97866e24..5ecdec2c29 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs @@ -12,9 +12,7 @@ namespace ImageSharp.Benchmarks using BenchmarkDotNet.Attributes; - using CoreColor = ImageSharp.Color; - using CoreImage = ImageSharp.Image; - using CorePoint = ImageSharp.Point; + using ImageSharp.PixelFormats; public class DrawLines : BenchmarkBase { @@ -23,7 +21,6 @@ namespace ImageSharp.Benchmarks { using (Bitmap destination = new Bitmap(800, 800)) { - using (Graphics graphics = Graphics.FromImage(destination)) { graphics.InterpolationMode = InterpolationMode.Default; @@ -46,10 +43,10 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Draw Lines")] public void DrawLinesCore() { - using (CoreImage image = new CoreImage(800, 800)) + using (Image image = new Image(800, 800)) { image.DrawLines( - CoreColor.HotPink, + Rgba32.HotPink, 10, new[] { new Vector2(10, 10), diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs index 047cacb421..d3ff33956e 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs @@ -9,12 +9,12 @@ namespace ImageSharp.Benchmarks using System.Drawing.Drawing2D; using BenchmarkDotNet.Attributes; - using CoreImage = ImageSharp.Image; - using CorePoint = ImageSharp.Point; - using CoreColor = ImageSharp.Color; + using System.IO; using System.Numerics; + using ImageSharp.PixelFormats; + public class DrawPolygon : BenchmarkBase { [Benchmark(Baseline = true, Description = "System.Drawing Draw Polygon")] @@ -45,10 +45,10 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Draw Polygon")] public void DrawPolygonCore() { - using (CoreImage image = new CoreImage(800, 800)) + using (Image image = new Image(800, 800)) { image.DrawPolygon( - CoreColor.HotPink, + Rgba32.HotPink, 10, new[] { new Vector2(10, 10), diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs index 782306deb7..deba595545 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillPolygon.cs @@ -13,8 +13,7 @@ namespace ImageSharp.Benchmarks using BenchmarkDotNet.Attributes; - using CoreColor = ImageSharp.Color; - using CoreImage = ImageSharp.Image; + using ImageSharp.PixelFormats; public class FillPolygon : BenchmarkBase { @@ -54,10 +53,10 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Fill Polygon")] public void DrawSolidPolygonCore() { - using (CoreImage image = new CoreImage(800, 800)) + using (Image image = new Image(800, 800)) { image.FillPolygon( - CoreColor.HotPink, + Rgba32.HotPink, new[] { new Vector2(10, 10), new Vector2(550, 50), @@ -74,10 +73,10 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Fill Polygon - cached shape")] public void DrawSolidPolygonCoreCahced() { - using (CoreImage image = new CoreImage(800, 800)) + using (Image image = new Image(800, 800)) { image.Fill( - CoreColor.HotPink, + Rgba32.HotPink, this.shape); using (MemoryStream ms = new MemoryStream()) diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs index 691955e8ed..a90a7a4c01 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillRectangle.cs @@ -9,13 +9,13 @@ namespace ImageSharp.Benchmarks using System.Drawing.Drawing2D; using BenchmarkDotNet.Attributes; - using CoreImage = ImageSharp.Image; using CoreRectangle = ImageSharp.Rectangle; - using CoreColor = ImageSharp.Color; using CoreSize = ImageSharp.Size; using System.Numerics; + using ImageSharp.PixelFormats; + public class FillRectangle : BenchmarkBase { [Benchmark(Baseline = true, Description = "System.Drawing Fill Rectangle")] @@ -37,9 +37,9 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Fill Rectangle")] public CoreSize FillRactangleCore() { - using (CoreImage image = new CoreImage(800, 800)) + using (Image image = new Image(800, 800)) { - image.Fill(CoreColor.HotPink, new CoreRectangle(10, 10, 190, 140)); + image.Fill(Rgba32.HotPink, new CoreRectangle(10, 10, 190, 140)); return new CoreSize(image.Width, image.Height); } @@ -48,10 +48,10 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Fill Rectangle - As Polygon")] public CoreSize FillPolygonCore() { - using (CoreImage image = new CoreImage(800, 800)) + using (Image image = new Image(800, 800)) { image.FillPolygon( - CoreColor.HotPink, + Rgba32.HotPink, new[] { new Vector2(10, 10), new Vector2(200, 10), diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs index dcd22d6fb4..aa97efe00b 100644 --- a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs +++ b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs @@ -11,9 +11,9 @@ namespace ImageSharp.Benchmarks using BenchmarkDotNet.Attributes; + using ImageSharp.Drawing.Brushes; using CoreBrushes = ImageSharp.Drawing.Brushes.Brushes; - using CoreColor = ImageSharp.Color; - using CoreImage = ImageSharp.Image; + using ImageSharp.PixelFormats; public class FillWithPattern { @@ -38,9 +38,9 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Fill with Pattern")] public void DrawPatternPolygon3Core() { - using (CoreImage image = new CoreImage(800, 800)) + using (Image image = new Image(800, 800)) { - image.Fill(CoreBrushes.BackwardDiagonal(CoreColor.HotPink)); + image.Fill(CoreBrushes.BackwardDiagonal(Rgba32.HotPink)); using (MemoryStream ms = new MemoryStream()) { diff --git a/tests/ImageSharp.Benchmarks/General/Array2D.cs b/tests/ImageSharp.Benchmarks/General/Array2D.cs index fce92e9be5..9d44fc93de 100644 --- a/tests/ImageSharp.Benchmarks/General/Array2D.cs +++ b/tests/ImageSharp.Benchmarks/General/Array2D.cs @@ -9,6 +9,8 @@ namespace ImageSharp.Benchmarks.General using BenchmarkDotNet.Attributes; + using ImageSharp.Memory; + public class Array2D { private float[] flatArray; diff --git a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs index 72fd6dc248..31e9cc0e3c 100644 --- a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs +++ b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Benchmarks.General using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; - + [Config(typeof(Config.Short))] public class ArrayCopy { @@ -58,8 +58,7 @@ namespace ImageSharp.Benchmarks.General Buffer.MemoryCopy(pinnedSource, pinnedDestination, this.Count, this.Count); } } - - + [Benchmark(Description = "Copy using Marshal.Copy")] public unsafe void CopyUsingMarshalCopy() { @@ -68,5 +67,37 @@ namespace ImageSharp.Benchmarks.General Marshal.Copy(this.source, 0, (IntPtr)pinnedDestination, this.Count); } } + + /***************************************************************************************************************** + *************** RESULTS on i7-4810MQ 2.80GHz + Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1085.0 ******************** + ***************************************************************************************************************** + * + * Method | Count | Mean | StdErr | StdDev | Scaled | Scaled-StdDev | + * ---------------------------------- |------ |------------ |----------- |----------- |------- |-------------- | + * 'Copy using Array.Copy()' | 10 | 20.3074 ns | 0.1194 ns | 0.2068 ns | 1.00 | 0.00 | + * 'Copy using Unsafe' | 10 | 6.1002 ns | 0.1981 ns | 0.3432 ns | 0.30 | 0.01 | + * 'Copy using Buffer.BlockCopy()' | 10 | 10.7879 ns | 0.0984 ns | 0.1705 ns | 0.53 | 0.01 | + * 'Copy using Buffer.MemoryCopy' | 10 | 4.9625 ns | 0.0200 ns | 0.0347 ns | 0.24 | 0.00 | + * 'Copy using Marshal.Copy' | 10 | 16.1782 ns | 0.0919 ns | 0.1592 ns | 0.80 | 0.01 | + * + * 'Copy using Array.Copy()' | 100 | 31.5945 ns | 0.2908 ns | 0.5037 ns | 1.00 | 0.00 | + * 'Copy using Unsafe' | 100 | 10.2722 ns | 0.5202 ns | 0.9010 ns | 0.33 | 0.02 | + * 'Copy using Buffer.BlockCopy()' | 100 | 22.0322 ns | 0.0284 ns | 0.0493 ns | 0.70 | 0.01 | + * 'Copy using Buffer.MemoryCopy' | 100 | 10.2472 ns | 0.0359 ns | 0.0622 ns | 0.32 | 0.00 | + * 'Copy using Marshal.Copy' | 100 | 34.3820 ns | 1.1868 ns | 2.0555 ns | 1.09 | 0.05 | + * + * 'Copy using Array.Copy()' | 1000 | 40.9743 ns | 0.0521 ns | 0.0902 ns | 1.00 | 0.00 | + * 'Copy using Unsafe' | 1000 | 42.7840 ns | 2.0139 ns | 3.4882 ns | 1.04 | 0.07 | + * 'Copy using Buffer.BlockCopy()' | 1000 | 33.7361 ns | 0.0751 ns | 0.1300 ns | 0.82 | 0.00 | + * 'Copy using Buffer.MemoryCopy' | 1000 | 35.7541 ns | 0.0480 ns | 0.0832 ns | 0.87 | 0.00 | + * 'Copy using Marshal.Copy' | 1000 | 42.2028 ns | 0.2769 ns | 0.4795 ns | 1.03 | 0.01 | + * + * 'Copy using Array.Copy()' | 10000 | 200.0438 ns | 0.2251 ns | 0.3899 ns | 1.00 | 0.00 | + * 'Copy using Unsafe' | 10000 | 389.6957 ns | 13.2770 ns | 22.9964 ns | 1.95 | 0.09 | + * 'Copy using Buffer.BlockCopy()' | 10000 | 191.3478 ns | 0.1557 ns | 0.2697 ns | 0.96 | 0.00 | + * 'Copy using Buffer.MemoryCopy' | 10000 | 196.4679 ns | 0.2731 ns | 0.4730 ns | 0.98 | 0.00 | + * 'Copy using Marshal.Copy' | 10000 | 202.5392 ns | 0.5561 ns | 0.9631 ns | 1.01 | 0.00 | + * + */ } } diff --git a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs index 9aa836de59..fc1b46a91a 100644 --- a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs +++ b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs @@ -3,23 +3,22 @@ namespace ImageSharp.Benchmarks.General { using System; using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; - using Color = ImageSharp.Color; + using ImageSharp.Memory; public unsafe class ClearBuffer { - private PinnedBuffer buffer; - + private Buffer buffer; + [Params(32, 128, 512)] public int Count { get; set; } [Setup] public void Setup() { - this.buffer = new PinnedBuffer(this.Count); + this.buffer = new Buffer(this.Count); } [Cleanup] @@ -37,7 +36,7 @@ namespace ImageSharp.Benchmarks.General [Benchmark] public void Unsafe_InitBlock() { - Unsafe.InitBlock((void*)this.buffer.Pointer, default(byte), (uint)this.Count*sizeof(uint)); + Unsafe.InitBlock((void*)this.buffer.Pin(), default(byte), (uint)this.Count * sizeof(uint)); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/IterateArray.cs b/tests/ImageSharp.Benchmarks/General/IterateArray.cs index 48f2316a27..b9e2f7acdc 100644 --- a/tests/ImageSharp.Benchmarks/General/IterateArray.cs +++ b/tests/ImageSharp.Benchmarks/General/IterateArray.cs @@ -5,10 +5,12 @@ namespace ImageSharp.Benchmarks.General using BenchmarkDotNet.Attributes; + using ImageSharp.Memory; + public class IterateArray { // Usual pinned stuff - private PinnedBuffer buffer; + private Buffer buffer; // An array that's not pinned by intent! private Vector4[] array; @@ -19,7 +21,8 @@ namespace ImageSharp.Benchmarks.General [Setup] public void Setup() { - this.buffer = new PinnedBuffer(this.Length); + this.buffer = new Buffer(this.Length); + this.buffer.Pin(); this.array = new Vector4[this.Length]; } @@ -41,7 +44,7 @@ namespace ImageSharp.Benchmarks.General { Vector4 sum = new Vector4(); - Vector4* ptr = (Vector4*) this.buffer.Pointer; + Vector4* ptr = (Vector4*) this.buffer.Pin(); Vector4* end = ptr + this.Length; for (; ptr < end; ptr++) diff --git a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs new file mode 100644 index 0000000000..18c99a744f --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs @@ -0,0 +1,362 @@ +namespace ImageSharp.Benchmarks.General +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + using BenchmarkDotNet.Attributes; + + using ImageSharp.Memory; + + // Pixel indexing benchmarks compare different methods for getting/setting all pixel values in a subsegment of a single pixel row. + public abstract unsafe class PixelIndexing + { + /// + /// https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/Pinnable.cs + /// + protected class Pinnable + { + public T Data; + } + + /// + /// The indexer methods are encapsulated into a struct to make sure everything is inlined. + /// + internal struct Data + { + private Vector4* pointer; + + private Pinnable pinnable; + + private Vector4[] array; + + private int width; + + public Data(Buffer2D buffer) + { + this.pointer = (Vector4*)buffer.Pin(); + this.pinnable = Unsafe.As>(buffer.Array); + this.array = buffer.Array; + this.width = buffer.Width; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 GetPointersBasicImpl(int x, int y) + { + return this.pointer[y * this.width + x]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 GetPointersSrcsUnsafeImpl(int x, int y) + { + // This is the original solution in PixelAccessor: + return Unsafe.Read((byte*)this.pointer + (((y * this.width) + x) * Unsafe.SizeOf())); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 GetReferencesImpl(int x, int y) + { + int elementOffset = (y * this.width) + x; + return Unsafe.Add(ref this.pinnable.Data, elementOffset); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref Vector4 GetReferencesRefReturnsImpl(int x, int y) + { + int elementOffset = (y * this.width) + x; + return ref Unsafe.Add(ref this.pinnable.Data, elementOffset); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void IndexWithPointersBasicImpl(int x, int y, Vector4 v) + { + this.pointer[y * this.width + x] = v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void IndexWithPointersSrcsUnsafeImpl(int x, int y, Vector4 v) + { + Unsafe.Write((byte*)this.pointer + (((y * this.width) + x) * Unsafe.SizeOf()), v); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void IndexWithReferencesOnPinnableIncorrectImpl(int x, int y, Vector4 v) + { + int elementOffset = (y * this.width) + x; + // Incorrect, because also should add a runtime-specific byte offset here. See https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/Span.cs#L68 + Unsafe.Add(ref this.pinnable.Data, elementOffset) = v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref Vector4 IndexWithReferencesOnPinnableIncorrectRefReturnImpl(int x, int y) + { + int elementOffset = (y * this.width) + x; + // Incorrect, because also should add a runtime-specific byte offset here. See https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/Span.cs#L68 + return ref Unsafe.Add(ref this.pinnable.Data, elementOffset); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void IndexWithUnsafeReferenceArithmeticsOnArray0Impl(int x, int y, Vector4 v) + { + int elementOffset = (y * this.width) + x; + Unsafe.Add(ref this.array[0], elementOffset) = v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref Vector4 IndexWithUnsafeReferenceArithmeticsOnArray0RefReturnImpl(int x, int y) + { + int elementOffset = (y * this.width) + x; + return ref Unsafe.Add(ref this.array[0], elementOffset); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void IndexSetArrayStraightforward(int x, int y, Vector4 v) + { + // No magic. + // We just index right into the array as normal people do. + // And it looks like this is the fastest way! + this.array[(y * this.width) + x] = v; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref Vector4 IndexWithReferencesOnArrayStraightforwardRefReturnImpl(int x, int y) + { + // No magic. + // We just index right into the array as normal people do. + // And it looks like this is the fastest way! + return ref this.array[(y * this.width) + x]; + } + } + + internal Buffer2D buffer; + + protected int width; + + protected int startIndex; + + protected int endIndex; + + protected Vector4* pointer; + + protected Vector4[] array; + + protected Pinnable pinnable; + + // [Params(1024)] + public int Count { get; set; } = 1024; + + [Setup] + public void Setup() + { + this.width = 2048; + this.buffer = new Buffer2D(2048, 2048); + this.pointer = (Vector4*)this.buffer.Pin(); + this.array = this.buffer.Array; + this.pinnable = Unsafe.As>(this.array); + + this.startIndex = 2048 / 2 - (this.Count / 2); + this.endIndex = 2048 / 2 + (this.Count / 2); + } + + [Cleanup] + public void Cleanup() + { + this.buffer.Dispose(); + } + + } + + public class PixelIndexingGetter : PixelIndexing + { + [Benchmark(Description = "Index.Get: Pointers+arithmetics", Baseline = true)] + public Vector4 IndexWithPointersBasic() + { + Vector4 sum = Vector4.Zero; + Data data = new Data(this.buffer); + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + sum += data.GetPointersBasicImpl(x, y); + } + + return sum; + } + + [Benchmark(Description = "Index.Get: Pointers+SRCS.Unsafe")] + public Vector4 IndexWithPointersSrcsUnsafe() + { + Vector4 sum = Vector4.Zero; + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + sum += data.GetPointersSrcsUnsafeImpl(x, y); + } + + return sum; + } + + [Benchmark(Description = "Index.Get: References")] + public Vector4 IndexWithReferences() + { + Vector4 sum = Vector4.Zero; + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + sum += data.GetReferencesImpl(x, y); + } + + return sum; + } + + [Benchmark(Description = "Index.Get: References|refreturns")] + public Vector4 IndexWithReferencesRefReturns() + { + Vector4 sum = Vector4.Zero; + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + sum += data.GetReferencesRefReturnsImpl(x, y); + } + + return sum; + } + } + + public class PixelIndexingSetter : PixelIndexing + { + [Benchmark(Description = "!!! Index.Set: Pointers|arithmetics", Baseline = true)] + public void IndexWithPointersBasic() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + data.IndexWithPointersBasicImpl(x, y, v); + } + } + + [Benchmark(Description = "Index.Set: Pointers|SRCS.Unsafe")] + public void IndexWithPointersSrcsUnsafe() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + data.IndexWithPointersSrcsUnsafeImpl(x, y, v); + } + } + + [Benchmark(Description = "Index.Set: References|IncorrectPinnable")] + public void IndexWithReferencesPinnableBasic() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + data.IndexWithReferencesOnPinnableIncorrectImpl(x, y, v); + } + } + + [Benchmark(Description = "Index.Set: References|IncorrectPinnable|refreturn")] + public void IndexWithReferencesPinnableRefReturn() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + data.IndexWithReferencesOnPinnableIncorrectRefReturnImpl(x, y) = v; + } + } + + [Benchmark(Description = "Index.Set: References|Array[0]Unsafe")] + public void IndexWithReferencesArrayBasic() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + data.IndexWithUnsafeReferenceArithmeticsOnArray0Impl(x, y, v); + } + } + + [Benchmark(Description = "Index.Set: References|Array[0]Unsafe|refreturn")] + public void IndexWithReferencesArrayRefReturn() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + data.IndexWithUnsafeReferenceArithmeticsOnArray0RefReturnImpl(x, y) = v; + } + } + + [Benchmark(Description = "!!! Index.Set: References|Array+Straight")] + public void IndexWithReferencesArrayStraightforward() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + // No magic. + // We just index right into the array as normal people do. + // And it looks like this is the fastest way! + data.IndexSetArrayStraightforward(x, y, v); + } + } + + + [Benchmark(Description = "!!! Index.Set: References|Array+Straight|refreturn")] + public void IndexWithReferencesArrayStraightforwardRefReturn() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + int y = this.startIndex; + for (int x = this.startIndex; x < this.endIndex; x++) + { + // No magic. + // We just index right into the array as normal people do. + // And it looks like this is the fastest way! + data.IndexWithReferencesOnArrayStraightforwardRefReturnImpl(x, y) = v; + } + } + + [Benchmark(Description = "!!! Index.Set: SmartUnsafe")] + public void SmartUnsafe() + { + Vector4 v = new Vector4(1, 2, 3, 4); + Data data = new Data(this.buffer); + + // This method is basically an unsafe variant of .GetRowSpan(y) + indexing individual pixels in the row. + // If a user seriously needs by-pixel manipulation to be performant, we should provide this option. + + ref Vector4 rowStart = ref data.IndexWithReferencesOnArrayStraightforwardRefReturnImpl(this.startIndex, this.startIndex); + + for (int i = 0; i < this.Count; i++) + { + // We don't have to add 'Width * y' here! + Unsafe.Add(ref rowStart, i) = v; + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs index 151145e128..ee33cf210a 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs @@ -24,7 +24,7 @@ namespace ImageSharp.Benchmarks.General.Vectorization for (int i = 0; i < this.InputSize; i++) { - this.input[i] = (uint)i; + this.input[i] = i; } } @@ -39,7 +39,7 @@ namespace ImageSharp.Benchmarks.General.Vectorization } [Benchmark] - public void Simd() + public void SimdMultiplyByVector() { Vector v = new Vector(this.testValue); @@ -50,5 +50,18 @@ namespace ImageSharp.Benchmarks.General.Vectorization a.CopyTo(this.result, i); } } + + [Benchmark] + public void SimdMultiplyByScalar() + { + float v = this.testValue; + + for (int i = 0; i < this.input.Length; i += Vector.Count) + { + Vector a = new Vector(this.input, i); + a = a * v; + a.CopyTo(this.result, i); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs new file mode 100644 index 0000000000..018f113349 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs @@ -0,0 +1,110 @@ +namespace ImageSharp.Benchmarks.General.Vectorization +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + + using BenchmarkDotNet.Attributes; + using ImageSharp.Memory; + + /// + /// This benchmark compares different methods for fetching memory data into + /// checking if JIT has limitations. Normally SIMD acceleration should be here for all methods. + /// + public class VectorFetching + { + private float testValue; + + private float[] data; + + [Params(64)] + public int InputSize { get; set; } + + [Setup] + public void Setup() + { + this.data = new float[this.InputSize]; + this.testValue = 42; + + for (int i = 0; i < this.InputSize; i++) + { + this.data[i] = i; + } + } + + [Benchmark(Baseline = true)] + public void Baseline() + { + float v = this.testValue; + for (int i = 0; i < this.data.Length; i++) + { + this.data[i] = this.data[i] * v; + } + } + + [Benchmark] + public void FetchWithVectorConstructor() + { + Vector v = new Vector(this.testValue); + + for (int i = 0; i < this.data.Length; i += Vector.Count) + { + Vector a = new Vector(this.data, i); + a = a * v; + a.CopyTo(this.data, i); + } + } + + [Benchmark] + public void FetchWithUnsafeCast() + { + Vector v = new Vector(this.testValue); + ref Vector start = ref Unsafe.As>(ref this.data[0]); + + int n = this.InputSize / Vector.Count; + + for (int i = 0; i < n; i++) + { + ref Vector p = ref Unsafe.Add(ref start, i); + + Vector a = p; + a = a * v; + + p = a; + } + } + + [Benchmark] + public void FetchWithUnsafeCastNoTempVector() + { + Vector v = new Vector(this.testValue); + ref Vector start = ref Unsafe.As>(ref this.data[0]); + + int n = this.InputSize / Vector.Count; + + for (int i = 0; i < n; i++) + { + ref Vector a = ref Unsafe.Add(ref start, i); + a = a * v; + } + } + + [Benchmark] + public void FetchWithSpanUtility() + { + Vector v = new Vector(this.testValue); + + Span span = new Span(this.data); + + ref Vector start = ref span.FetchVector(); + + int n = this.InputSize / Vector.Count; + + for (int i = 0; i < n; i++) + { + ref Vector a = ref Unsafe.Add(ref start, i); + a = a * v; + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs b/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs index 335d8247d3..5b5d14750a 100644 --- a/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs +++ b/tests/ImageSharp.Benchmarks/Image/CopyPixels.cs @@ -9,19 +9,18 @@ namespace ImageSharp.Benchmarks.Image using BenchmarkDotNet.Attributes; - using CoreColor = ImageSharp.Color; - using CoreImage = ImageSharp.Image; + using ImageSharp.PixelFormats; public class CopyPixels : BenchmarkBase { [Benchmark(Description = "Copy by Pixel")] - public CoreColor CopyByPixel() + public Rgba32 CopyByPixel() { - using (CoreImage source = new CoreImage(1024, 768)) - using (CoreImage target = new CoreImage(1024, 768)) + using (Image source = new Image(1024, 768)) + using (Image target = new Image(1024, 768)) { - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) + using (PixelAccessor targetPixels = target.Lock()) { Parallel.For( 0, diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs b/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs index acde8e0dbe..87baa8b7ee 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs @@ -11,6 +11,7 @@ namespace ImageSharp.Benchmarks.Image using BenchmarkDotNet.Attributes; using CoreImage = ImageSharp.Image; + using CoreSize = ImageSharp.Size; public class DecodeBmp : BenchmarkBase @@ -43,7 +44,7 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream(this.bmpBytes)) { - using (CoreImage image = CoreImage.Load(memoryStream)) + using (Image image = CoreImage.Load(memoryStream)) { return new CoreSize(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs b/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs index 6786cfdc0f..a1fddc5025 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs @@ -10,6 +10,7 @@ namespace ImageSharp.Benchmarks.Image using BenchmarkDotNet.Attributes; using ImageSharp; + using CoreImage = ImageSharp.Image; public class DecodeFilteredPng : BenchmarkBase { @@ -31,7 +32,7 @@ namespace ImageSharp.Benchmarks.Image private Size LoadPng(MemoryStream stream) { - using (Image image = Image.Load(stream)) + using (Image image = CoreImage.Load(stream)) { return new Size(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs b/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs index a9bb4c7b3a..02620fe744 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs @@ -11,6 +11,7 @@ namespace ImageSharp.Benchmarks.Image using BenchmarkDotNet.Attributes; using CoreImage = ImageSharp.Image; + using CoreSize = ImageSharp.Size; public class DecodeGif : BenchmarkBase @@ -43,7 +44,7 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream(this.gifBytes)) { - using (CoreImage image = CoreImage.Load(memoryStream)) + using (Image image = CoreImage.Load(memoryStream)) { return new CoreSize(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs index 6ce2303703..ab45f95f43 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs @@ -11,6 +11,7 @@ namespace ImageSharp.Benchmarks.Image using BenchmarkDotNet.Attributes; using CoreImage = ImageSharp.Image; + using CoreSize = ImageSharp.Size; public class DecodeJpeg : BenchmarkBase @@ -43,7 +44,7 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream(this.jpegBytes)) { - using (CoreImage image = CoreImage.Load(memoryStream)) + using (Image image = CoreImage.Load(memoryStream)) { return new CoreSize(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs b/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs index 5c3c1e115b..44c90d253d 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeJpegMultiple.cs @@ -8,8 +8,7 @@ namespace ImageSharp.Benchmarks.Image using System.Collections.Generic; using BenchmarkDotNet.Attributes; - using Image = ImageSharp.Image; - using ImageSharpSize = ImageSharp.Size; + using CoreImage = ImageSharp.Image; [Config(typeof(Config.Short))] public class DecodeJpegMultiple : MultiImageBenchmarkBase @@ -25,8 +24,8 @@ namespace ImageSharp.Benchmarks.Image public void DecodeJpegImageSharp() { this.ForEachStream( - ms => ImageSharp.Image.Load(ms) - ); + ms => CoreImage.Load(ms) + ); } [Benchmark(Baseline = true, Description = "DecodeJpegMultiple - System.Drawing")] diff --git a/tests/ImageSharp.Benchmarks/Image/DecodePng.cs b/tests/ImageSharp.Benchmarks/Image/DecodePng.cs index 620a48a3b1..9b71fd0583 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodePng.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodePng.cs @@ -11,6 +11,7 @@ namespace ImageSharp.Benchmarks.Image using BenchmarkDotNet.Attributes; using CoreImage = ImageSharp.Image; + using CoreSize = ImageSharp.Size; public class DecodePng : BenchmarkBase @@ -43,7 +44,7 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream(this.pngBytes)) { - using (CoreImage image = CoreImage.Load(memoryStream)) + using (Image image = CoreImage.Load(memoryStream)) { return new CoreSize(image.Width, image.Height); } diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs b/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs index 6ed5773388..52a0399124 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs @@ -10,6 +10,7 @@ namespace ImageSharp.Benchmarks.Image using System.IO; using BenchmarkDotNet.Attributes; + using CoreImage = ImageSharp.Image; public class EncodeBmp : BenchmarkBase @@ -17,7 +18,7 @@ namespace ImageSharp.Benchmarks.Image // System.Drawing needs this. private Stream bmpStream; private Image bmpDrawing; - private CoreImage bmpCore; + private Image bmpCore; [Setup] public void ReadImages() @@ -25,7 +26,7 @@ namespace ImageSharp.Benchmarks.Image if (this.bmpStream == null) { this.bmpStream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"); - this.bmpCore = CoreImage.Load(this.bmpStream); + this.bmpCore = CoreImage.Load(this.bmpStream); this.bmpStream.Position = 0; this.bmpDrawing = Image.FromStream(this.bmpStream); } diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs b/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs index fabeba1bde..5eaa8940b4 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs @@ -10,6 +10,7 @@ namespace ImageSharp.Benchmarks.Image using System.IO; using BenchmarkDotNet.Attributes; + using CoreImage = ImageSharp.Image; public class EncodeGif : BenchmarkBase @@ -17,7 +18,7 @@ namespace ImageSharp.Benchmarks.Image // System.Drawing needs this. private Stream bmpStream; private Image bmpDrawing; - private CoreImage bmpCore; + private Image bmpCore; [Setup] public void ReadImages() @@ -25,7 +26,7 @@ namespace ImageSharp.Benchmarks.Image if (this.bmpStream == null) { this.bmpStream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"); - this.bmpCore = CoreImage.Load(this.bmpStream); + this.bmpCore = CoreImage.Load(this.bmpStream); this.bmpStream.Position = 0; this.bmpDrawing = Image.FromStream(this.bmpStream); } diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs b/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs index 1318c1674a..4d25c82efd 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs @@ -13,6 +13,8 @@ namespace ImageSharp.Benchmarks.Image using ImageSharp.Formats; using ImageSharp.Quantizers; + using CoreImage = ImageSharp.Image; + /// /// Benchmarks saving png files using different quantizers. System.Drawing cannot save indexed png files so we cannot compare. /// @@ -20,7 +22,7 @@ namespace ImageSharp.Benchmarks.Image { // System.Drawing needs this. private Stream bmpStream; - private Image bmpCore; + private Image bmpCore; [Params(false)] public bool LargeImage { get; set; } @@ -34,7 +36,7 @@ namespace ImageSharp.Benchmarks.Image ? "../ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg420exif.jpg" : "../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"; this.bmpStream = File.OpenRead(path); - this.bmpCore = Image.Load(this.bmpStream); + this.bmpCore = CoreImage.Load(this.bmpStream); this.bmpStream.Position = 0; } } @@ -51,7 +53,7 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - PngEncoderOptions options = new PngEncoderOptions() { Quantizer = new OctreeQuantizer(), Quality = 256 }; + PngEncoderOptions options = new PngEncoderOptions() { Quantizer = new OctreeQuantizer(), Quality = 256 }; this.bmpCore.SaveAsPng(memoryStream, options); } @@ -62,7 +64,7 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - PngEncoderOptions options = new PngEncoderOptions { Quantizer = new OctreeQuantizer { Dither = false }, Quality = 256 }; + PngEncoderOptions options = new PngEncoderOptions { Quantizer = new OctreeQuantizer { Dither = false }, Quality = 256 }; this.bmpCore.SaveAsPng(memoryStream, options); } @@ -73,7 +75,7 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - PngEncoderOptions options = new PngEncoderOptions { Quantizer = new PaletteQuantizer(), Quality = 256 }; + PngEncoderOptions options = new PngEncoderOptions { Quantizer = new PaletteQuantizer(), Quality = 256 }; this.bmpCore.SaveAsPng(memoryStream, options); } @@ -84,7 +86,7 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - PngEncoderOptions options = new PngEncoderOptions { Quantizer = new PaletteQuantizer { Dither = false }, Quality = 256 }; + PngEncoderOptions options = new PngEncoderOptions { Quantizer = new PaletteQuantizer { Dither = false }, Quality = 256 }; this.bmpCore.SaveAsPng(memoryStream, options); } @@ -95,7 +97,7 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - PngEncoderOptions options = new PngEncoderOptions() { Quantizer = new WuQuantizer(), Quality = 256 }; + PngEncoderOptions options = new PngEncoderOptions() { Quantizer = new WuQuantizer(), Quality = 256 }; this.bmpCore.SaveAsPng(memoryStream, options); } diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs b/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs index 7649812ecc..efd4e8ac51 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs @@ -10,6 +10,7 @@ namespace ImageSharp.Benchmarks.Image using System.IO; using BenchmarkDotNet.Attributes; + using CoreImage = ImageSharp.Image; public class EncodeJpeg : BenchmarkBase @@ -17,7 +18,7 @@ namespace ImageSharp.Benchmarks.Image // System.Drawing needs this. private Stream bmpStream; private Image bmpDrawing; - private CoreImage bmpCore; + private Image bmpCore; [Setup] public void ReadImages() @@ -25,7 +26,7 @@ namespace ImageSharp.Benchmarks.Image if (this.bmpStream == null) { this.bmpStream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"); - this.bmpCore = CoreImage.Load(this.bmpStream); + this.bmpCore = CoreImage.Load(this.bmpStream); this.bmpStream.Position = 0; this.bmpDrawing = Image.FromStream(this.bmpStream); } diff --git a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs index 4c1feb6c2a..11182ac485 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs @@ -21,7 +21,7 @@ namespace ImageSharp.Benchmarks.Image // System.Drawing needs this. private Stream bmpStream; private Image bmpDrawing; - private CoreImage bmpCore; + private Image bmpCore; [Params(false)] public bool LargeImage { get; set; } @@ -38,7 +38,7 @@ namespace ImageSharp.Benchmarks.Image ? "../ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg420exif.jpg" : "../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp"; this.bmpStream = File.OpenRead(path); - this.bmpCore = CoreImage.Load(this.bmpStream); + this.bmpCore = CoreImage.Load(this.bmpStream); this.bmpStream.Position = 0; this.bmpDrawing = Image.FromStream(this.bmpStream); } @@ -66,10 +66,10 @@ namespace ImageSharp.Benchmarks.Image { using (MemoryStream memoryStream = new MemoryStream()) { - Quantizer quantizer = this.UseOctreeQuantizer - ? (Quantizer) - new OctreeQuantizer() - : new PaletteQuantizer(); + Quantizer quantizer = this.UseOctreeQuantizer + ? (Quantizer) + new OctreeQuantizer() + : new PaletteQuantizer(); PngEncoderOptions options = new PngEncoderOptions() { Quantizer = quantizer }; this.bmpCore.SaveAsPng(memoryStream, options); diff --git a/tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs b/tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs index 78295e27d7..28616213b2 100644 --- a/tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs +++ b/tests/ImageSharp.Benchmarks/Image/GetSetPixel.cs @@ -9,8 +9,8 @@ namespace ImageSharp.Benchmarks.Image using BenchmarkDotNet.Attributes; - using CoreColor = ImageSharp.Color; - using CoreImage = ImageSharp.Image; + using ImageSharp.PixelFormats; + using SystemColor = System.Drawing.Color; public class GetSetPixel : BenchmarkBase @@ -26,13 +26,13 @@ namespace ImageSharp.Benchmarks.Image } [Benchmark(Description = "ImageSharp GetSet pixel")] - public CoreColor ResizeCore() + public Rgba32 ResizeCore() { - using (CoreImage image = new CoreImage(400, 400)) + using (Image image = new Image(400, 400)) { - using (PixelAccessor imagePixels = image.Lock()) + using (PixelAccessor imagePixels = image.Lock()) { - imagePixels[200, 200] = CoreColor.White; + imagePixels[200, 200] = Rgba32.White; return imagePixels[200, 200]; } } diff --git a/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs index a084ca025c..dfee978ccb 100644 --- a/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs @@ -15,13 +15,13 @@ namespace ImageSharp.Benchmarks.Image using BenchmarkDotNet.Attributes; - using Image = ImageSharp.Image; + using CoreImage = ImageSharp.Image; public abstract class MultiImageBenchmarkBase : BenchmarkBase { protected Dictionary FileNamesToBytes = new Dictionary(); - protected Dictionary FileNamesToImageSharpImages = new Dictionary(); + protected Dictionary> FileNamesToImageSharpImages = new Dictionary>(); protected Dictionary FileNamesToSystemDrawingImages = new Dictionary(); /// @@ -154,7 +154,7 @@ namespace ImageSharp.Benchmarks.Image using (MemoryStream ms1 = new MemoryStream(bytes)) { - this.FileNamesToImageSharpImages[fn] = Image.Load(ms1); + this.FileNamesToImageSharpImages[fn] = CoreImage.Load(ms1); } @@ -162,7 +162,7 @@ namespace ImageSharp.Benchmarks.Image } } - protected IEnumerable> FileNames2ImageSharpImages + protected IEnumerable>> FileNames2ImageSharpImages => this.EnumeratePairsByBenchmarkSettings( this.FileNamesToImageSharpImages, @@ -176,9 +176,9 @@ namespace ImageSharp.Benchmarks.Image protected virtual int LargeImageThresholdInPixels => 700000; - protected void ForEachImageSharpImage(Func operation) + protected void ForEachImageSharpImage(Func, object> operation) { - foreach (KeyValuePair kv in this.FileNames2ImageSharpImages) + foreach (KeyValuePair> kv in this.FileNames2ImageSharpImages) { try { @@ -194,7 +194,7 @@ namespace ImageSharp.Benchmarks.Image } } - protected void ForEachImageSharpImage(Func operation) + protected void ForEachImageSharpImage(Func, MemoryStream, object> operation) { using (MemoryStream workStream = new MemoryStream()) { diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs new file mode 100644 index 0000000000..1da69f1a8f --- /dev/null +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs @@ -0,0 +1,103 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Benchmarks +{ + using System; + + using BenchmarkDotNet.Attributes; + using ImageSharp.PixelFormats; + using CoreSize = ImageSharp.Size; + using System.Numerics; + + using ImageSharp.Memory; + using ImageSharp.PixelFormats.PixelBlenders; + + public class PorterDuffBulkVsPixel : BenchmarkBase + { + private void BulkVectorConvert(Span destination, Span background, Span source, Span amount) + where TPixel : struct, IPixel + { + Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); + + using (Buffer buffer = new Buffer(destination.Length * 3)) + { + Span destinationSpan = buffer.Slice(0, destination.Length); + Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); + Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); + + PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); + PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + + for (int i = 0; i < destination.Length; i++) + { + destinationSpan[i] = PorterDuffFunctions.NormalBlendFunction(backgroundSpan[i], sourceSpan[i], amount[i]); + } + + PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + } + } + private void BulkPixelConvert(Span destination, Span background, Span source, Span amount) + where TPixel : struct, IPixel + { + Guard.MustBeGreaterThanOrEqualTo(destination.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(source.Length, background.Length, nameof(destination)); + Guard.MustBeGreaterThanOrEqualTo(amount.Length, background.Length, nameof(destination)); + + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.NormalBlendFunction(destination[i], source[i], amount[i]); + } + } + + [Benchmark(Description = "ImageSharp BulkVectorConvert")] + public CoreSize BulkVectorConvert() + { + using (Image image = new Image(800, 800)) + { + Buffer amounts = new Buffer(image.Width); + + for (int x = 0; x < image.Width; x++) + { + amounts[x] = 1; + } + using (PixelAccessor pixels = image.Lock()) + { + for (int y = 0; y < image.Height; y++) + { + Span span = pixels.GetRowSpan(y); + BulkVectorConvert(span, span, span, amounts); + } + } + return new CoreSize(image.Width, image.Height); + } + } + + [Benchmark(Description = "ImageSharp BulkPixelConvert")] + public CoreSize BulkPixelConvert() + { + using (Image image = new Image(800, 800)) + { + Buffer amounts = new Buffer(image.Width); + + for (int x = 0; x < image.Width; x++) + { + amounts[x] = 1; + } + using (PixelAccessor pixels = image.Lock()) + { + for (int y = 0; y < image.Height; y++) + { + Span span = pixels.GetRowSpan(y); + BulkPixelConvert(span, span, span, amounts); + } + } + return new CoreSize(image.Width, image.Height); + } + } + } +} diff --git a/tests/ImageSharp.Benchmarks/Samplers/Crop.cs b/tests/ImageSharp.Benchmarks/Samplers/Crop.cs index a3cdef92eb..9fa9794637 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Crop.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Crop.cs @@ -10,7 +10,8 @@ namespace ImageSharp.Benchmarks using BenchmarkDotNet.Attributes; - using CoreImage = ImageSharp.Image; + using ImageSharp.PixelFormats; + using CoreSize = ImageSharp.Size; public class Crop : BenchmarkBase @@ -38,7 +39,7 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Crop")] public CoreSize CropResizeCore() { - using (CoreImage image = new CoreImage(800, 800)) + using (Image image = new Image(800, 800)) { image.Crop(100, 100); return new CoreSize(image.Width, image.Height); diff --git a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs index 28661b9d63..d4920ff081 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs @@ -9,12 +9,13 @@ namespace ImageSharp.Benchmarks using BenchmarkDotNet.Attributes; - using CoreImage = ImageSharp.Image; using Processing; + using CoreImage = ImageSharp.Image; + public class DetectEdges : BenchmarkBase { - private CoreImage image; + private Image image; [Setup] public void ReadImage() @@ -23,7 +24,7 @@ namespace ImageSharp.Benchmarks { using (FileStream stream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp")) { - this.image = CoreImage.Load(stream); + this.image = CoreImage.Load(stream); } } } diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs new file mode 100644 index 0000000000..7608d30659 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs @@ -0,0 +1,164 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Benchmarks +{ + + using BenchmarkDotNet.Attributes; + using ImageSharp.PixelFormats; + using ImageSharp.Processing.Processors; + using CoreSize = ImageSharp.Size; + using ImageSharp.Processing; + using System.Numerics; + using System; + using System.Threading.Tasks; + + using ImageSharp.Memory; + + public class Glow : BenchmarkBase + { + private GlowProcessor bulk; + private GlowProcessorParallel parallel; + + [Setup] + public void Setup() + { + this.bulk = new GlowProcessor(NamedColors.Beige, GraphicsOptions.Default) { Radius = 800 * .5f, }; + this.parallel = new GlowProcessorParallel(NamedColors.Beige) { Radius = 800 * .5f, }; + + } + [Benchmark(Description = "ImageSharp Glow - Bulk")] + public CoreSize GlowBulk() + { + using (Image image = new Image(800, 800)) + { + image.ApplyProcessor(bulk, image.Bounds); + return new CoreSize(image.Width, image.Height); + } + } + + [Benchmark(Description = "ImageSharp Glow - Parallel")] + public CoreSize GLowSimple() + { + using (Image image = new Image(800, 800)) + { + image.ApplyProcessor(parallel, image.Bounds); + return new CoreSize(image.Width, image.Height); + } + } + + internal class GlowProcessorParallel : ImageProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The color or the glow. + public GlowProcessorParallel(TPixel color) + { + this.GlowColor = color; + } + + /// + /// Gets or sets the glow color to apply. + /// + public TPixel GlowColor { get; set; } + + /// + /// Gets or sets the the radius. + /// + public float Radius { get; set; } + + /// + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + { + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + TPixel glowColor = this.GlowColor; + Vector2 centre = Rectangle.Center(sourceRectangle).ToVector2(); + float maxDistance = this.Radius > 0 ? MathF.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; + + // Align start/end positions. + int minX = Math.Max(0, startX); + int maxX = Math.Min(source.Width, endX); + int minY = Math.Max(0, startY); + int maxY = Math.Min(source.Height, endY); + + // Reset offset if necessary. + if (minX > 0) + { + startX = 0; + } + + if (minY > 0) + { + startY = 0; + } + + int width = maxX - minX; + using (Buffer rowColors = new Buffer(width)) + using (PixelAccessor sourcePixels = source.Lock()) + { + for (int i = 0; i < width; i++) + { + rowColors[i] = glowColor; + } + + Parallel.For( + minY, + maxY, + this.ParallelOptions, + y => + { + int offsetY = y - startY; + + for (int x = minX; x < maxX; x++) + { + int offsetX = x - startX; + float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY)); + Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4(); + TPixel packed = default(TPixel); + packed.PackFromVector4(PremultipliedLerp(sourceColor, glowColor.ToVector4(), 1 - (.95F * (distance / maxDistance)))); + sourcePixels[offsetX, offsetY] = packed; + } + }); + } + } + public static Vector4 PremultipliedLerp(Vector4 backdrop, Vector4 source, float amount) + { + amount = amount.Clamp(0, 1); + + // Santize on zero alpha + if (MathF.Abs(backdrop.W) < Constants.Epsilon) + { + source.W *= amount; + return source; + } + + if (MathF.Abs(source.W) < Constants.Epsilon) + { + return backdrop; + } + + // Premultiply the source vector. + // Oddly premultiplying the background vector creates dark outlines when pixels + // Have low alpha values. + source = new Vector4(source.X, source.Y, source.Z, 1) * (source.W * amount); + + // This should be implementing the following formula + // https://en.wikipedia.org/wiki/Alpha_compositing + // Vout = Vs + Vb (1 - Vsa) + // Aout = Vsa + Vsb (1 - Vsa) + Vector3 inverseW = new Vector3(1 - source.W); + Vector3 xyzB = new Vector3(backdrop.X, backdrop.Y, backdrop.Z); + Vector3 xyzS = new Vector3(source.X, source.Y, source.Z); + + return new Vector4(xyzS + (xyzB * inverseW), source.W + (backdrop.W * (1 - source.W))); + } + } + } +} diff --git a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs index 04570ce8f3..d96be70f74 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs @@ -9,8 +9,10 @@ namespace ImageSharp.Benchmarks using System.Drawing.Drawing2D; using BenchmarkDotNet.Attributes; + + using ImageSharp.PixelFormats; + using CoreSize = ImageSharp.Size; - using CoreImage = ImageSharp.Image; public class Resize : BenchmarkBase { @@ -37,21 +39,41 @@ namespace ImageSharp.Benchmarks [Benchmark(Description = "ImageSharp Resize")] public CoreSize ResizeCore() { - using (CoreImage image = new CoreImage(2000, 2000)) + using (Image image = new Image(2000, 2000)) { image.Resize(400, 400); return new CoreSize(image.Width, image.Height); } } - [Benchmark(Description = "ImageSharp Compand Resize")] - public CoreSize ResizeCoreCompand() - { - using (CoreImage image = new CoreImage(2000, 2000)) - { - image.Resize(400, 400, true); - return new CoreSize(image.Width, image.Height); - } - } + //[Benchmark(Description = "ImageSharp Vector Resize")] + //public CoreSize ResizeCoreVector() + //{ + // using (Image image = new Image(2000, 2000)) + // { + // image.Resize(400, 400); + // return new CoreSize(image.Width, image.Height); + // } + //} + + //[Benchmark(Description = "ImageSharp Compand Resize")] + //public CoreSize ResizeCoreCompand() + //{ + // using (Image image = new Image(2000, 2000)) + // { + // image.Resize(400, 400, true); + // return new CoreSize(image.Width, image.Height); + // } + //} + + //[Benchmark(Description = "ImageSharp Vector Compand Resize")] + //public CoreSize ResizeCoreVectorCompand() + //{ + // using (Image image = new Image(2000, 2000)) + // { + // image.Resize(400, 400, true); + // return new CoreSize(image.Width, image.Height); + // } + //} } } diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index 23a5c59a30..53cdffaeaf 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -29,4 +29,7 @@ + + + \ No newline at end of file diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs index dad603523c..7ea459aaee 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Sandbox46/Program.cs @@ -7,7 +7,7 @@ namespace ImageSharp.Sandbox46 { using System; using System.Runtime.DesignerServices; - + using ImageSharp.Tests; using ImageSharp.Tests.Colors; @@ -53,10 +53,10 @@ namespace ImageSharp.Sandbox46 private static void RunToVector4ProfilingTest() { - BulkPixelOperationsTests.Color tests = new BulkPixelOperationsTests.Color(new ConsoleOutput()); + PixelOperationsTests.Color32 tests = new PixelOperationsTests.Color32(new ConsoleOutput()); tests.Benchmark_ToVector4(); } - + private static void RunDecodeJpegProfilingTests() { Console.WriteLine("RunDecodeJpegProfilingTests..."); diff --git a/tests/ImageSharp.Tests/Colors/ColorConstructorTests.cs b/tests/ImageSharp.Tests/Colors/ColorConstructorTests.cs index 83c02635a2..eac0644d9a 100644 --- a/tests/ImageSharp.Tests/Colors/ColorConstructorTests.cs +++ b/tests/ImageSharp.Tests/Colors/ColorConstructorTests.cs @@ -7,6 +7,9 @@ namespace ImageSharp.Tests.Colors { using System.Collections.Generic; using System.Numerics; + + using ImageSharp.PixelFormats; + using Xunit; public class ColorConstructorTests @@ -30,7 +33,7 @@ namespace ImageSharp.Tests.Colors // using float array to work around a bug in xunit corruptint the state of any Vector4 passed as MemberData float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - yield return new object[] { new Argb(vector4), vector4Components }; + yield return new object[] { new Argb32(vector4), vector4Components }; yield return new object[] { new Bgra4444(vector4), vector4Components }; yield return new object[] { new Bgra5551(vector4), vector4Components }; yield return new object[] { new Byte4(vector4), vector4Components }; @@ -63,7 +66,7 @@ namespace ImageSharp.Tests.Colors // using float array to work around a bug in xunit corruptint the state of any Vector4 passed as MemberData float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - yield return new object[] { new Argb(vector3), vector4Components }; + yield return new object[] { new Argb32(vector3), vector4Components }; yield return new object[] { new Bgr565(vector3), vector4Components }; } } @@ -88,7 +91,7 @@ namespace ImageSharp.Tests.Colors // using float array to work around a bug in xunit corruptint the state of any Vector4 passed as MemberData float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - yield return new object[] { new Argb(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; + yield return new object[] { new Argb32(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; yield return new object[] { new Bgra4444(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; yield return new object[] { new Bgra5551(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; yield return new object[] { new Byte4(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; @@ -121,7 +124,7 @@ namespace ImageSharp.Tests.Colors // using float array to work around a bug in xunit corruptint the state of any Vector4 passed as MemberData float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - yield return new object[] { new Argb(vector3.X, vector3.Y, vector3.Z), vector4Components }; + yield return new object[] { new Argb32(vector3.X, vector3.Y, vector3.Z), vector4Components }; yield return new object[] { new Bgr565(vector3.X, vector3.Y, vector3.Z), vector4Components }; } } diff --git a/tests/ImageSharp.Tests/Colors/ColorConversionTests.cs b/tests/ImageSharp.Tests/Colors/ColorConversionTests.cs index 9ed1c67a70..4b45d0ab48 100644 --- a/tests/ImageSharp.Tests/Colors/ColorConversionTests.cs +++ b/tests/ImageSharp.Tests/Colors/ColorConversionTests.cs @@ -8,6 +8,7 @@ namespace ImageSharp.Tests using System; using System.Diagnostics.CodeAnalysis; using ImageSharp.Colors.Spaces; + using ImageSharp.PixelFormats; using Xunit; /// @@ -20,7 +21,7 @@ namespace ImageSharp.Tests public class ColorConversionTests { /// - /// Tests the implicit conversion from to . + /// Tests the implicit conversion from to . /// [Fact] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", @@ -28,7 +29,7 @@ namespace ImageSharp.Tests public void ColorToYCbCr() { // White - Color color = Color.White; + Rgba32 color = Rgba32.White; YCbCr yCbCr = color; Assert.Equal(255, yCbCr.Y); @@ -36,14 +37,14 @@ namespace ImageSharp.Tests Assert.Equal(128, yCbCr.Cr); // Black - Color color2 = Color.Black; + Rgba32 color2 = Rgba32.Black; YCbCr yCbCr2 = color2; Assert.Equal(0, yCbCr2.Y); Assert.Equal(128, yCbCr2.Cb); Assert.Equal(128, yCbCr2.Cr); // Gray - Color color3 = Color.Gray; + Rgba32 color3 = Rgba32.Gray; YCbCr yCbCr3 = color3; Assert.Equal(128, yCbCr3.Y); Assert.Equal(128, yCbCr3.Cb); @@ -51,7 +52,7 @@ namespace ImageSharp.Tests } /// - /// Tests the implicit conversion from to . + /// Tests the implicit conversion from to . /// [Fact] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", @@ -60,7 +61,7 @@ namespace ImageSharp.Tests { // White YCbCr yCbCr = new YCbCr(255, 128, 128); - Color color = yCbCr; + Rgba32 color = yCbCr; Assert.Equal(255, color.R); Assert.Equal(255, color.G); @@ -69,7 +70,7 @@ namespace ImageSharp.Tests // Black YCbCr yCbCr2 = new YCbCr(0, 128, 128); - Color color2 = yCbCr2; + Rgba32 color2 = yCbCr2; Assert.Equal(0, color2.R); Assert.Equal(0, color2.G); @@ -78,7 +79,7 @@ namespace ImageSharp.Tests // Gray YCbCr yCbCr3 = new YCbCr(128, 128, 128); - Color color3 = yCbCr3; + Rgba32 color3 = yCbCr3; Assert.Equal(128, color3.R); Assert.Equal(128, color3.G); @@ -87,7 +88,7 @@ namespace ImageSharp.Tests } /// - /// Tests the implicit conversion from to . + /// Tests the implicit conversion from to . /// Comparison values obtained from /// http://colormine.org/convert/rgb-to-xyz /// @@ -95,7 +96,7 @@ namespace ImageSharp.Tests public void ColorToCieXyz() { // White - Color color = Color.White; + Rgba32 color = Rgba32.White; CieXyz ciexyz = color; Assert.Equal(95.05f, ciexyz.X, 3); @@ -103,21 +104,21 @@ namespace ImageSharp.Tests Assert.Equal(108.900f, ciexyz.Z, 3); // Black - Color color2 = Color.Black; + Rgba32 color2 = Rgba32.Black; CieXyz ciexyz2 = color2; Assert.Equal(0, ciexyz2.X, 3); Assert.Equal(0, ciexyz2.Y, 3); Assert.Equal(0, ciexyz2.Z, 3); // Gray - Color color3 = Color.Gray; + Rgba32 color3 = Rgba32.Gray; CieXyz ciexyz3 = color3; Assert.Equal(20.518, ciexyz3.X, 3); Assert.Equal(21.586, ciexyz3.Y, 3); Assert.Equal(23.507, ciexyz3.Z, 3); // Cyan - Color color4 = Color.Cyan; + Rgba32 color4 = Rgba32.Cyan; CieXyz ciexyz4 = color4; Assert.Equal(53.810f, ciexyz4.X, 3); Assert.Equal(78.740f, ciexyz4.Y, 3); @@ -125,7 +126,7 @@ namespace ImageSharp.Tests } /// - /// Tests the implicit conversion from to . + /// Tests the implicit conversion from to . /// Comparison values obtained from /// http://colormine.org/convert/rgb-to-xyz /// @@ -134,7 +135,7 @@ namespace ImageSharp.Tests { // Dark moderate pink. CieXyz ciexyz = new CieXyz(13.337f, 9.297f, 14.727f); - Color color = ciexyz; + Rgba32 color = ciexyz; Assert.Equal(128, color.R); Assert.Equal(64, color.G); @@ -142,7 +143,7 @@ namespace ImageSharp.Tests // Ochre CieXyz ciexyz2 = new CieXyz(31.787f, 26.147f, 4.885f); - Color color2 = ciexyz2; + Rgba32 color2 = ciexyz2; Assert.Equal(204, color2.R); Assert.Equal(119, color2.G); @@ -150,7 +151,7 @@ namespace ImageSharp.Tests // Black CieXyz ciexyz3 = new CieXyz(0, 0, 0); - Color color3 = ciexyz3; + Rgba32 color3 = ciexyz3; Assert.Equal(0, color3.R); Assert.Equal(0, color3.G); @@ -167,7 +168,7 @@ namespace ImageSharp.Tests } /// - /// Tests the implicit conversion from to . + /// Tests the implicit conversion from to . /// [Fact] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", @@ -175,7 +176,7 @@ namespace ImageSharp.Tests public void ColorToHsv() { // Black - Color b = Color.Black; + Rgba32 b = Rgba32.Black; Hsv h = b; Assert.Equal(0, h.H, 1); @@ -183,7 +184,7 @@ namespace ImageSharp.Tests Assert.Equal(0, h.V, 1); // White - Color color = Color.White; + Rgba32 color = Rgba32.White; Hsv hsv = color; Assert.Equal(0f, hsv.H, 1); @@ -191,7 +192,7 @@ namespace ImageSharp.Tests Assert.Equal(1f, hsv.V, 1); // Dark moderate pink. - Color color2 = new Color(128, 64, 106); + Rgba32 color2 = new Rgba32(128, 64, 106); Hsv hsv2 = color2; Assert.Equal(320.6f, hsv2.H, 1); @@ -199,7 +200,7 @@ namespace ImageSharp.Tests Assert.Equal(0.502f, hsv2.V, 2); // Ochre. - Color color3 = new Color(204, 119, 34); + Rgba32 color3 = new Rgba32(204, 119, 34); Hsv hsv3 = color3; Assert.Equal(30f, hsv3.H, 1); @@ -208,14 +209,14 @@ namespace ImageSharp.Tests } /// - /// Tests the implicit conversion from to . + /// Tests the implicit conversion from to . /// [Fact] public void HsvToColor() { // Dark moderate pink. Hsv hsv = new Hsv(320.6f, 0.5f, 0.502f); - Color color = hsv; + Rgba32 color = hsv; Assert.Equal(color.R, 128); Assert.Equal(color.G, 64); @@ -223,7 +224,7 @@ namespace ImageSharp.Tests // Ochre Hsv hsv2 = new Hsv(30, 0.833f, 0.8f); - Color color2 = hsv2; + Rgba32 color2 = hsv2; Assert.Equal(color2.R, 204); Assert.Equal(color2.G, 119); @@ -231,7 +232,7 @@ namespace ImageSharp.Tests // White Hsv hsv3 = new Hsv(0, 0, 1); - Color color3 = hsv3; + Rgba32 color3 = hsv3; Assert.Equal(color3.B, 255); Assert.Equal(color3.G, 255); @@ -248,7 +249,7 @@ namespace ImageSharp.Tests } /// - /// Tests the implicit conversion from to . + /// Tests the implicit conversion from to . /// [Fact] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", @@ -256,7 +257,7 @@ namespace ImageSharp.Tests public void ColorToHsl() { // Black - Color b = Color.Black; + Rgba32 b = Rgba32.Black; Hsl h = b; Assert.Equal(0, h.H, 1); @@ -264,7 +265,7 @@ namespace ImageSharp.Tests Assert.Equal(0, h.L, 1); // White - Color color = Color.White; + Rgba32 color = Rgba32.White; Hsl hsl = color; Assert.Equal(0f, hsl.H, 1); @@ -272,7 +273,7 @@ namespace ImageSharp.Tests Assert.Equal(1f, hsl.L, 1); // Dark moderate pink. - Color color2 = new Color(128, 64, 106); + Rgba32 color2 = new Rgba32(128, 64, 106); Hsl hsl2 = color2; Assert.Equal(320.6f, hsl2.H, 1); @@ -280,7 +281,7 @@ namespace ImageSharp.Tests Assert.Equal(0.376f, hsl2.L, 2); // Ochre. - Color color3 = new Color(204, 119, 34); + Rgba32 color3 = new Rgba32(204, 119, 34); Hsl hsl3 = color3; Assert.Equal(30f, hsl3.H, 1); @@ -289,14 +290,14 @@ namespace ImageSharp.Tests } /// - /// Tests the implicit conversion from to . + /// Tests the implicit conversion from to . /// [Fact] public void HslToColor() { // Dark moderate pink. Hsl hsl = new Hsl(320.6f, 0.33f, 0.376f); - Color color = hsl; + Rgba32 color = hsl; Assert.Equal(color.R, 128); Assert.Equal(color.G, 64); @@ -304,7 +305,7 @@ namespace ImageSharp.Tests // Ochre Hsl hsl2 = new Hsl(30, 0.714f, 0.467f); - Color color2 = hsl2; + Rgba32 color2 = hsl2; Assert.Equal(color2.R, 204); Assert.Equal(color2.G, 119); @@ -312,7 +313,7 @@ namespace ImageSharp.Tests // White Hsl hsl3 = new Hsl(0, 0, 1); - Color color3 = hsl3; + Rgba32 color3 = hsl3; Assert.Equal(color3.R, 255); Assert.Equal(color3.G, 255); @@ -329,7 +330,7 @@ namespace ImageSharp.Tests } /// - /// Tests the implicit conversion from to . + /// Tests the implicit conversion from to . /// [Fact] [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", @@ -337,7 +338,7 @@ namespace ImageSharp.Tests public void ColorToCmyk() { // White - Color color = Color.White; + Rgba32 color = Rgba32.White; Cmyk cmyk = color; Assert.Equal(0, cmyk.C, 1); @@ -346,7 +347,7 @@ namespace ImageSharp.Tests Assert.Equal(0, cmyk.K, 1); // Black - Color color2 = Color.Black; + Rgba32 color2 = Rgba32.Black; Cmyk cmyk2 = color2; Assert.Equal(0, cmyk2.C, 1); Assert.Equal(0, cmyk2.M, 1); @@ -354,7 +355,7 @@ namespace ImageSharp.Tests Assert.Equal(1, cmyk2.K, 1); // Gray - Color color3 = Color.Gray; + Rgba32 color3 = Rgba32.Gray; Cmyk cmyk3 = color3; Assert.Equal(0f, cmyk3.C, 1); Assert.Equal(0f, cmyk3.M, 1); @@ -362,7 +363,7 @@ namespace ImageSharp.Tests Assert.Equal(0.498, cmyk3.K, 2); // Checked with other online converters. // Cyan - Color color4 = Color.Cyan; + Rgba32 color4 = Rgba32.Cyan; Cmyk cmyk4 = color4; Assert.Equal(1, cmyk4.C, 1); Assert.Equal(0f, cmyk4.M, 1); @@ -371,14 +372,14 @@ namespace ImageSharp.Tests } /// - /// Tests the implicit conversion from to . + /// Tests the implicit conversion from to . /// [Fact] public void CmykToColor() { // Dark moderate pink. Cmyk cmyk = new Cmyk(0f, .5f, .171f, .498f); - Color color = cmyk; + Rgba32 color = cmyk; Assert.Equal(color.R, 128); Assert.Equal(color.G, 64); @@ -386,7 +387,7 @@ namespace ImageSharp.Tests // Ochre Cmyk cmyk2 = new Cmyk(0, .416f, .833f, .199f); - Color color2 = cmyk2; + Rgba32 color2 = cmyk2; Assert.Equal(color2.R, 204); Assert.Equal(color2.G, 119); @@ -394,7 +395,7 @@ namespace ImageSharp.Tests // White Cmyk cmyk3 = new Cmyk(0, 0, 0, 0); - Color color3 = cmyk3; + Rgba32 color3 = cmyk3; Assert.Equal(color3.R, 255); Assert.Equal(color3.G, 255); @@ -411,7 +412,7 @@ namespace ImageSharp.Tests } /// - /// Tests the implicit conversion from to . + /// Tests the implicit conversion from to . /// Comparison values obtained from /// http://colormine.org/convert/rgb-to-lab /// @@ -419,7 +420,7 @@ namespace ImageSharp.Tests public void ColorToCieLab() { // White - Color color = Color.White; + Rgba32 color = Rgba32.White; CieLab cielab = color; Assert.Equal(100, cielab.L, 3); @@ -427,21 +428,21 @@ namespace ImageSharp.Tests Assert.Equal(-0.010, cielab.B, 3); // Black - Color color2 = Color.Black; + Rgba32 color2 = Rgba32.Black; CieLab cielab2 = color2; Assert.Equal(0, cielab2.L, 3); Assert.Equal(0, cielab2.A, 3); Assert.Equal(0, cielab2.B, 3); // Gray - Color color3 = Color.Gray; + Rgba32 color3 = Rgba32.Gray; CieLab cielab3 = color3; Assert.Equal(53.585, cielab3.L, 3); Assert.Equal(0.003, cielab3.A, 3); Assert.Equal(-0.006, cielab3.B, 3); // Cyan - Color color4 = Color.Cyan; + Rgba32 color4 = Rgba32.Cyan; CieLab cielab4 = color4; Assert.Equal(91.117, cielab4.L, 3); Assert.Equal(-48.080, cielab4.A, 3); @@ -449,7 +450,7 @@ namespace ImageSharp.Tests } /// - /// Tests the implicit conversion from to . + /// Tests the implicit conversion from to . /// /// Comparison values obtained from /// http://colormine.org/convert/rgb-to-lab @@ -458,7 +459,7 @@ namespace ImageSharp.Tests { // Dark moderate pink. CieLab cielab = new CieLab(36.5492f, 33.3173f, -12.0615f); - Color color = cielab; + Rgba32 color = cielab; Assert.Equal(color.R, 128); Assert.Equal(color.G, 64); @@ -466,7 +467,7 @@ namespace ImageSharp.Tests // Ochre CieLab cielab2 = new CieLab(58.1758f, 27.3399f, 56.8240f); - Color color2 = cielab2; + Rgba32 color2 = cielab2; Assert.Equal(color2.R, 204); Assert.Equal(color2.G, 119); @@ -474,7 +475,7 @@ namespace ImageSharp.Tests // Black CieLab cielab3 = new CieLab(0, 0, 0); - Color color3 = cielab3; + Rgba32 color3 = cielab3; Assert.Equal(color3.R, 0); Assert.Equal(color3.G, 0); diff --git a/tests/ImageSharp.Tests/Colors/ColorDefinitionTests.cs b/tests/ImageSharp.Tests/Colors/ColorDefinitionTests.cs index 899ce4f77a..f2113852f3 100644 --- a/tests/ImageSharp.Tests/Colors/ColorDefinitionTests.cs +++ b/tests/ImageSharp.Tests/Colors/ColorDefinitionTests.cs @@ -12,17 +12,19 @@ namespace ImageSharp.Tests using System.Reflection; using ImageSharp.Colors.Spaces; + using ImageSharp.PixelFormats; + using Xunit; public class ColorDefinitionTests { - public static IEnumerable ColorNames => typeof(NamedColors).GetTypeInfo().GetFields().Select(x => new[] { x.Name }); + public static IEnumerable ColorNames => typeof(NamedColors).GetTypeInfo().GetFields().Select(x => new[] { x.Name }); [Theory] [MemberData(nameof(ColorNames))] public void AllColorsAreOnGenericAndBaseColor(string name) { - FieldInfo generic = typeof(NamedColors).GetTypeInfo().GetField(name); - FieldInfo specific = typeof(Color).GetTypeInfo().GetField(name); + FieldInfo generic = typeof(NamedColors).GetTypeInfo().GetField(name); + FieldInfo specific = typeof(Rgba32).GetTypeInfo().GetField(name); Assert.NotNull(specific); Assert.NotNull(generic); @@ -30,8 +32,8 @@ namespace ImageSharp.Tests Assert.True(specific.Attributes.HasFlag(FieldAttributes.Static), "specific must be static"); Assert.True(generic.Attributes.HasFlag(FieldAttributes.Public), "generic must be public"); Assert.True(generic.Attributes.HasFlag(FieldAttributes.Static), "generic must be static"); - Color expected = (Color)generic.GetValue(null); - Color actual = (Color)specific.GetValue(null); + Rgba32 expected = (Rgba32)generic.GetValue(null); + Rgba32 actual = (Rgba32)specific.GetValue(null); Assert.Equal(expected, actual); } } diff --git a/tests/ImageSharp.Tests/Colors/ColorEqualityTests.cs b/tests/ImageSharp.Tests/Colors/ColorEqualityTests.cs index b5b09c8288..efec4ea38c 100644 --- a/tests/ImageSharp.Tests/Colors/ColorEqualityTests.cs +++ b/tests/ImageSharp.Tests/Colors/ColorEqualityTests.cs @@ -8,6 +8,8 @@ namespace ImageSharp.Tests.Colors using System; using System.Numerics; using ImageSharp.Colors.Spaces; + using ImageSharp.PixelFormats; + using Xunit; /// @@ -19,7 +21,7 @@ namespace ImageSharp.Tests.Colors new TheoryData() { { new Alpha8(.5F), new Alpha8(.5F), typeof(Alpha8) }, - { new Argb(Vector4.One), new Argb(Vector4.One), typeof(Argb) }, + { new Argb32(Vector4.One), new Argb32(Vector4.One), typeof(Argb32) }, { new Bgr565(Vector3.One), new Bgr565(Vector3.One), typeof(Bgr565) }, { new Bgra4444(Vector4.One), new Bgra4444(Vector4.One), typeof(Bgra4444) }, { new Bgra5551(Vector4.One), new Bgra5551(Vector4.One), typeof(Bgra5551) }, @@ -33,6 +35,7 @@ namespace ImageSharp.Tests.Colors { new NormalizedShort4(Vector4.One), new NormalizedShort4(Vector4.One), typeof(NormalizedShort4) }, { new Rg32(Vector2.One), new Rg32(Vector2.One), typeof(Rg32) }, { new Rgba1010102(Vector4.One), new Rgba1010102(Vector4.One), typeof(Rgba1010102) }, + { new Rgba32(Vector4.One), new Rgba32(Vector4.One), typeof(Rgba32) }, { new Rgba64(Vector4.One), new Rgba64(Vector4.One), typeof(Rgba64) }, { new Short2(Vector2.One * 0x7FFF), new Short2(Vector2.One * 0x7FFF), typeof(Short2) }, { new Short4(Vector4.One * 0x7FFF), new Short4(Vector4.One * 0x7FFF), typeof(Short4) }, @@ -75,7 +78,7 @@ namespace ImageSharp.Tests.Colors { // Valid object against null { new Alpha8(.5F), null, typeof(Alpha8) }, - { new Argb(Vector4.One), null, typeof(Argb) }, + { new Argb32(Vector4.One), null, typeof(Argb32) }, { new Bgr565(Vector3.One), null, typeof(Bgr565) }, { new Bgra4444(Vector4.One), null, typeof(Bgra4444) }, { new Bgra5551(Vector4.One), null, typeof(Bgra5551) }, @@ -110,7 +113,7 @@ namespace ImageSharp.Tests.Colors new TheoryData() { // Valid objects of different types but not equal - { new Alpha8(.5F), new Argb(Vector4.Zero), null }, + { new Alpha8(.5F), new Argb32(Vector4.Zero), null }, { new HalfSingle(-1F), new NormalizedShort2(Vector2.Zero), null }, { new Rgba1010102(Vector4.One), new Bgra5551(Vector4.Zero), null }, }; @@ -130,7 +133,7 @@ namespace ImageSharp.Tests.Colors { // Valid objects of the same type but not equal { new Alpha8(.5F), new Alpha8(.8F), typeof(Alpha8) }, - { new Argb(Vector4.One), new Argb(Vector4.Zero), typeof(Argb) }, + { new Argb32(Vector4.One), new Argb32(Vector4.Zero), typeof(Argb32) }, { new Bgr565(Vector3.One), new Bgr565(Vector3.Zero), typeof(Bgr565) }, { new Bgra4444(Vector4.One), new Bgra4444(Vector4.Zero), typeof(Bgra4444) }, { new Bgra5551(Vector4.One), new Bgra5551(Vector4.Zero), typeof(Bgra5551) }, @@ -144,6 +147,7 @@ namespace ImageSharp.Tests.Colors { new NormalizedShort4(Vector4.One), new NormalizedShort4(Vector4.Zero), typeof(NormalizedShort4) }, { new Rg32(Vector2.One), new Rg32(Vector2.Zero), typeof(Rg32) }, { new Rgba1010102(Vector4.One), new Rgba1010102(Vector4.Zero), typeof(Rgba1010102) }, + { new Rgba32(Vector4.One), new Rgba32(Vector4.Zero), typeof(Rgba32) }, { new Rgba64(Vector4.One), new Rgba64(Vector4.Zero), typeof(Rgba64) }, { new Short2(Vector2.One * 0x7FFF), new Short2(Vector2.Zero), typeof(Short2) }, { new Short4(Vector4.One * 0x7FFF), new Short4(Vector4.Zero), typeof(Short4) }, @@ -289,10 +293,10 @@ namespace ImageSharp.Tests.Colors [MemberData(nameof(EqualityDataColorSpaces))] public void EqualityObject(object first, object second, Type type) { - // Arrange - // Cast to the known object types, this is so that we can hit the - // equality operator on the concrete type, otherwise it goes to the - // default "object" one :) + // Arrange + // Cast to the known object types, this is so that we can hit the + // equality operator on the concrete type, otherwise it goes to the + // default "object" one :) dynamic firstObject = Convert.ChangeType(first, type); dynamic secondObject = Convert.ChangeType(second, type); @@ -308,10 +312,10 @@ namespace ImageSharp.Tests.Colors [MemberData(nameof(NotEqualityDataColorSpaces))] public void NotEqualityObject(object first, object second, Type type) { - // Arrange - // Cast to the known object types, this is so that we can hit the - // equality operator on the concrete type, otherwise it goes to the - // default "object" one :) + // Arrange + // Cast to the known object types, this is so that we can hit the + // equality operator on the concrete type, otherwise it goes to the + // default "object" one :) dynamic firstObject = Convert.ChangeType(first, type); dynamic secondObject = Convert.ChangeType(second, type); @@ -327,10 +331,10 @@ namespace ImageSharp.Tests.Colors [MemberData(nameof(EqualityDataColorSpaces))] public void EqualityOperator(object first, object second, Type type) { - // Arrange - // Cast to the known object types, this is so that we can hit the - // equality operator on the concrete type, otherwise it goes to the - // default "object" one :) + // Arrange + // Cast to the known object types, this is so that we can hit the + // equality operator on the concrete type, otherwise it goes to the + // default "object" one :) dynamic firstObject = Convert.ChangeType(first, type); dynamic secondObject = Convert.ChangeType(second, type); @@ -346,10 +350,10 @@ namespace ImageSharp.Tests.Colors [MemberData(nameof(NotEqualityDataColorSpaces))] public void NotEqualityOperator(object first, object second, Type type) { - // Arrange - // Cast to the known object types, this is so that we can hit the - // equality operator on the concrete type, otherwise it goes to the - // default "object" one :) + // Arrange + // Cast to the known object types, this is so that we can hit the + // equality operator on the concrete type, otherwise it goes to the + // default "object" one :) dynamic firstObject = Convert.ChangeType(first, type); dynamic secondObject = Convert.ChangeType(second, type); @@ -364,10 +368,10 @@ namespace ImageSharp.Tests.Colors [MemberData(nameof(AlmostEqualsData))] public void AlmostEquals(object first, object second, Type type, float precision) { - // Arrange - // Cast to the known object types, this is so that we can hit the - // equality operator on the concrete type, otherwise it goes to the - // default "object" one :) + // Arrange + // Cast to the known object types, this is so that we can hit the + // equality operator on the concrete type, otherwise it goes to the + // default "object" one :) dynamic firstObject = Convert.ChangeType(first, type); dynamic secondObject = Convert.ChangeType(second, type); @@ -382,10 +386,10 @@ namespace ImageSharp.Tests.Colors [MemberData(nameof(AlmostNotEqualsData))] public void AlmostNotEquals(object first, object second, Type type, float precision) { - // Arrange - // Cast to the known object types, this is so that we can hit the - // equality operator on the concrete type, otherwise it goes to the - // default "object" one :) + // Arrange + // Cast to the known object types, this is so that we can hit the + // equality operator on the concrete type, otherwise it goes to the + // default "object" one :) dynamic firstObject = Convert.ChangeType(first, type); dynamic secondObject = Convert.ChangeType(second, type); diff --git a/tests/ImageSharp.Tests/Colors/ColorPackingTests.cs b/tests/ImageSharp.Tests/Colors/ColorPackingTests.cs index 7b743e53ae..563b6be3c4 100644 --- a/tests/ImageSharp.Tests/Colors/ColorPackingTests.cs +++ b/tests/ImageSharp.Tests/Colors/ColorPackingTests.cs @@ -7,6 +7,9 @@ namespace ImageSharp.Tests.Colors { using System.Collections.Generic; using System.Numerics; + + using ImageSharp.PixelFormats; + using Xunit; public class ColorPackingTests @@ -29,7 +32,7 @@ namespace ImageSharp.Tests.Colors { float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - yield return new object[] { new Argb(), vector4Components }; + yield return new object[] { new Argb32(), vector4Components }; yield return new object[] { new Bgra4444(), vector4Components }; yield return new object[] { new Bgra5551(), vector4Components }; yield return new object[] { new Byte4(), vector4Components }; @@ -60,7 +63,7 @@ namespace ImageSharp.Tests.Colors { float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - yield return new object[] { new Argb(), vector4Components }; + yield return new object[] { new Argb32(), vector4Components }; yield return new object[] { new Bgr565(), vector4Components }; } } diff --git a/tests/ImageSharp.Tests/Colors/ColorTransformTests.cs b/tests/ImageSharp.Tests/Colors/ColorTransformTests.cs deleted file mode 100644 index 064bdf2d02..0000000000 --- a/tests/ImageSharp.Tests/Colors/ColorTransformTests.cs +++ /dev/null @@ -1,116 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests.Colors -{ - using Xunit; - - /// - /// Tests the color transform algorithms. Test results match the output of CSS equivalents. - /// - /// - public class ColorTransformTests - { - /// - /// Orange backdrop - /// - private static readonly Color Backdrop = new Color(204, 102, 0); - - /// - /// Blue source - /// - private static readonly Color Source = new Color(0, 102, 153); - - [Fact] - public void Normal() - { - Color normal = Color.Normal(Backdrop, Source); - Assert.True(normal == Source); - } - - [Fact] - public void Multiply() - { - Assert.True(Color.Multiply(Backdrop, Color.Black) == Color.Black); - Assert.True(Color.Multiply(Backdrop, Color.White) == Backdrop); - - Color multiply = Color.Multiply(Backdrop, Source); - Assert.True(multiply == new Color(0, 41, 0)); - } - - [Fact] - public void Screen() - { - Assert.True(Color.Screen(Backdrop, Color.Black) == Backdrop); - Assert.True(Color.Screen(Backdrop, Color.White) == Color.White); - - Color screen = Color.Screen(Backdrop, Source); - Assert.True(screen == new Color(204, 163, 153)); - } - - [Fact] - public void HardLight() - { - Color hardLight = Color.HardLight(Backdrop, Source); - Assert.True(hardLight == new Color(0, 82, 51)); - } - - [Fact] - public void Overlay() - { - Color overlay = Color.Overlay(Backdrop, Source); - Assert.True(overlay == new Color(153, 82, 0)); - } - - [Fact] - public void Darken() - { - Color darken = Color.Darken(Backdrop, Source); - Assert.True(darken == new Color(0, 102, 0)); - } - - [Fact] - public void Lighten() - { - Color lighten = Color.Lighten(Backdrop, Source); - Assert.True(lighten == new Color(204, 102, 153)); - } - - [Fact] - public void SoftLight() - { - Color softLight = Color.SoftLight(Backdrop, Source); - Assert.True(softLight == new Color(163, 90, 0)); - } - - [Fact] - public void ColorDodge() - { - Color colorDodge = Color.ColorDodge(Backdrop, Source); - Assert.True(colorDodge == new Color(204, 170, 0)); - } - - [Fact] - public void ColorBurn() - { - Color colorBurn = Color.ColorBurn(Backdrop, Source); - Assert.True(colorBurn == new Color(0, 0, 0)); - } - - [Fact] - public void Difference() - { - Color difference = Color.Difference(Backdrop, Source); - Assert.True(difference == new Color(204, 0, 153)); - } - - [Fact] - public void Exclusion() - { - Color exclusion = Color.Exclusion(Backdrop, Source); - Assert.True(exclusion == new Color(204, 122, 153)); - } - } -} diff --git a/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs b/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs index 3e2b6fcd5c..5ec7c21bbf 100644 --- a/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs +++ b/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs @@ -9,6 +9,8 @@ namespace ImageSharp.Tests.Colors using System.Diagnostics; using System.Numerics; + using ImageSharp.PixelFormats; + using Xunit; /// @@ -62,29 +64,29 @@ namespace ImageSharp.Tests.Colors } [Fact] - public void Argb() + public void Argb32() { // Test the limits. - Assert.Equal((uint)0x0, new Argb(Vector4.Zero).PackedValue); - Assert.Equal(0xFFFFFFFF, new Argb(Vector4.One).PackedValue); + Assert.Equal((uint)0x0, new Argb32(Vector4.Zero).PackedValue); + Assert.Equal(0xFFFFFFFF, new Argb32(Vector4.One).PackedValue); // Test ToVector4. - Assert.True(Equal(Vector4.One, new Argb(Vector4.One).ToVector4())); - Assert.True(Equal(Vector4.Zero, new Argb(Vector4.Zero).ToVector4())); - Assert.True(Equal(Vector4.UnitX, new Argb(Vector4.UnitX).ToVector4())); - Assert.True(Equal(Vector4.UnitY, new Argb(Vector4.UnitY).ToVector4())); - Assert.True(Equal(Vector4.UnitZ, new Argb(Vector4.UnitZ).ToVector4())); - Assert.True(Equal(Vector4.UnitW, new Argb(Vector4.UnitW).ToVector4())); + Assert.True(Equal(Vector4.One, new Argb32(Vector4.One).ToVector4())); + Assert.True(Equal(Vector4.Zero, new Argb32(Vector4.Zero).ToVector4())); + Assert.True(Equal(Vector4.UnitX, new Argb32(Vector4.UnitX).ToVector4())); + Assert.True(Equal(Vector4.UnitY, new Argb32(Vector4.UnitY).ToVector4())); + Assert.True(Equal(Vector4.UnitZ, new Argb32(Vector4.UnitZ).ToVector4())); + Assert.True(Equal(Vector4.UnitW, new Argb32(Vector4.UnitW).ToVector4())); // Test clamping. - Assert.True(Equal(Vector4.Zero, new Argb(Vector4.One * -1234.0f).ToVector4())); - Assert.True(Equal(Vector4.One, new Argb(Vector4.One * +1234.0f).ToVector4())); + Assert.True(Equal(Vector4.Zero, new Argb32(Vector4.One * -1234.0f).ToVector4())); + Assert.True(Equal(Vector4.One, new Argb32(Vector4.One * +1234.0f).ToVector4())); float x = +0.1f; float y = -0.3f; float z = +0.5f; float w = -0.7f; - Argb argb = new Argb(x, y, z, w); + Argb32 argb = new Argb32(x, y, z, w); Assert.Equal(0x001a0080u, argb.PackedValue); // Test ordering @@ -711,6 +713,51 @@ namespace ImageSharp.Tests.Colors Assert.Equal(rgba, new byte[] { 25, 0, 128, 0 }); } + [Fact] + public void Color() + { + // Test the limits. + Assert.Equal((uint)0x0, new Rgba32(Vector4.Zero).PackedValue); + Assert.Equal(0xFFFFFFFF, new Rgba32(Vector4.One).PackedValue); + + // Test ToVector4. + Assert.True(Equal(Vector4.One, new Rgba32(Vector4.One).ToVector4())); + Assert.True(Equal(Vector4.Zero, new Rgba32(Vector4.Zero).ToVector4())); + Assert.True(Equal(Vector4.UnitX, new Rgba32(Vector4.UnitX).ToVector4())); + Assert.True(Equal(Vector4.UnitY, new Rgba32(Vector4.UnitY).ToVector4())); + Assert.True(Equal(Vector4.UnitZ, new Rgba32(Vector4.UnitZ).ToVector4())); + Assert.True(Equal(Vector4.UnitW, new Rgba32(Vector4.UnitW).ToVector4())); + + // Test clamping. + Assert.True(Equal(Vector4.Zero, new Rgba32(Vector4.One * -1234.0f).ToVector4())); + Assert.True(Equal(Vector4.One, new Rgba32(Vector4.One * +1234.0f).ToVector4())); + + float x = +0.1f; + float y = -0.3f; + float z = +0.5f; + float w = -0.7f; + Rgba32 rgba32 = new Rgba32(x, y, z, w); + Assert.Equal(0x80001Au, rgba32.PackedValue); + + // Test ordering + byte[] rgb = new byte[3]; + byte[] rgba = new byte[4]; + byte[] bgr = new byte[3]; + byte[] bgra = new byte[4]; + + rgba32.ToXyzBytes(rgb, 0); + Assert.Equal(rgb, new byte[] { 0x1a, 0, 0x80 }); + + rgba32.ToXyzwBytes(rgba, 0); + Assert.Equal(rgba, new byte[] { 0x1a, 0, 0x80, 0 }); + + rgba32.ToZyxBytes(bgr, 0); + Assert.Equal(bgr, new byte[] { 0x80, 0, 0x1a }); + + rgba32.ToZyxwBytes(bgra, 0); + Assert.Equal(bgra, new byte[] { 0x80, 0, 0x1a, 0 }); + } + [Fact] public void Rgba64() { diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/PixelOperationsTests.cs similarity index 76% rename from tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs rename to tests/ImageSharp.Tests/Colors/PixelOperationsTests.cs index 60e25aa043..c91218ccc7 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/PixelOperationsTests.cs @@ -5,14 +5,17 @@ namespace ImageSharp.Tests.Colors using System; using System.Numerics; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + using Xunit; using Xunit.Abstractions; - public class BulkPixelOperationsTests + public class PixelOperationsTests { - public class Color : BulkPixelOperationsTests + public class Color32 : PixelOperationsTests { - public Color(ITestOutputHelper output) + public Color32(ITestOutputHelper output) : base(output) { } @@ -23,20 +26,20 @@ namespace ImageSharp.Tests.Colors [Fact] public void IsSpecialImplementation() { - Assert.IsType(BulkPixelOperations.Instance); + Assert.IsType(PixelOperations.Instance); } - + [Fact] public void ToVector4SimdAligned() { - ImageSharp.Color[] source = CreatePixelTestData(64); + Rgba32[] source = CreatePixelTestData(64); Vector4[] expected = CreateExpectedVector4Data(source); TestOperation( source, expected, - (s, d) => ImageSharp.Color.BulkOperations.ToVector4SimdAligned(s, d, 64) - ); + (s, d) => Rgba32.PixelOperations.ToVector4SimdAligned(s, d, 64) + ); } // [Fact] // Profiling benchmark - enable manually! @@ -45,20 +48,20 @@ namespace ImageSharp.Tests.Colors int times = 200000; int count = 1024; - using (PinnedBuffer source = new PinnedBuffer(count)) - using (PinnedBuffer dest = new PinnedBuffer(count)) + using (Buffer source = new Buffer(count)) + using (Buffer dest = new Buffer(count)) { this.Measure( times, () => { - BulkPixelOperations.Instance.ToVector4(source, dest, count); + PixelOperations.Instance.ToVector4(source, dest, count); }); } } } - public class Argb : BulkPixelOperationsTests + public class Argb : PixelOperationsTests { // For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class: public Argb(ITestOutputHelper output) @@ -71,28 +74,28 @@ namespace ImageSharp.Tests.Colors [Theory] [WithBlankImages(1, 1, PixelTypes.All)] - public void GetGlobalInstance(TestImageProvider dummy) - where TColor:struct, IPixel + public void GetGlobalInstance(TestImageProvider dummy) + where TPixel : struct, IPixel { - Assert.NotNull(BulkPixelOperations.Instance); + Assert.NotNull(PixelOperations.Instance); } } - public abstract class BulkPixelOperationsTests : MeasureFixture - where TColor : struct, IPixel + public abstract class PixelOperationsTests : MeasureFixture + where TPixel : struct, IPixel { - protected BulkPixelOperationsTests(ITestOutputHelper output) + protected PixelOperationsTests(ITestOutputHelper output) : base(output) { } public static TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; - private static BulkPixelOperations Operations => BulkPixelOperations.Instance; + private static PixelOperations Operations => PixelOperations.Instance; - internal static TColor[] CreateExpectedPixelData(Vector4[] source) + internal static TPixel[] CreateExpectedPixelData(Vector4[] source) { - TColor[] expected = new TColor[source.Length]; + TPixel[] expected = new TPixel[source.Length]; for (int i = 0; i < expected.Length; i++) { @@ -106,16 +109,16 @@ namespace ImageSharp.Tests.Colors public void PackFromVector4(int count) { Vector4[] source = CreateVector4TestData(count); - TColor[] expected = CreateExpectedPixelData(source); + TPixel[] expected = CreateExpectedPixelData(source); TestOperation( source, expected, (s, d) => Operations.PackFromVector4(s, d, count) - ); + ); } - internal static Vector4[] CreateExpectedVector4Data(TColor[] source) + internal static Vector4[] CreateExpectedVector4Data(TPixel[] source) { Vector4[] expected = new Vector4[source.Length]; @@ -130,14 +133,14 @@ namespace ImageSharp.Tests.Colors [MemberData(nameof(ArraySizesData))] public void ToVector4(int count) { - TColor[] source = CreatePixelTestData(count); + TPixel[] source = CreatePixelTestData(count); Vector4[] expected = CreateExpectedVector4Data(source); TestOperation( source, expected, (s, d) => Operations.ToVector4(s, d, count) - ); + ); } @@ -146,7 +149,7 @@ namespace ImageSharp.Tests.Colors public void PackFromXyzBytes(int count) { byte[] source = CreateByteTestData(count * 3); - TColor[] expected = new TColor[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { @@ -156,17 +159,17 @@ namespace ImageSharp.Tests.Colors } TestOperation( - source, - expected, + source, + expected, (s, d) => Operations.PackFromXyzBytes(s, d, count) - ); + ); } [Theory] [MemberData(nameof(ArraySizesData))] public void ToXyzBytes(int count) { - TColor[] source = CreatePixelTestData(count); + TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * 3]; for (int i = 0; i < count; i++) @@ -179,7 +182,7 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => Operations.ToXyzBytes(s, d, count) - ); + ); } [Theory] @@ -187,7 +190,7 @@ namespace ImageSharp.Tests.Colors public void PackFromXyzwBytes(int count) { byte[] source = CreateByteTestData(count * 4); - TColor[] expected = new TColor[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { @@ -200,14 +203,14 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => Operations.PackFromXyzwBytes(s, d, count) - ); + ); } [Theory] [MemberData(nameof(ArraySizesData))] public void ToXyzwBytes(int count) { - TColor[] source = CreatePixelTestData(count); + TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * 4]; for (int i = 0; i < count; i++) @@ -220,7 +223,7 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => Operations.ToXyzwBytes(s, d, count) - ); + ); } [Theory] @@ -228,7 +231,7 @@ namespace ImageSharp.Tests.Colors public void PackFromZyxBytes(int count) { byte[] source = CreateByteTestData(count * 3); - TColor[] expected = new TColor[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { @@ -241,14 +244,14 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => Operations.PackFromZyxBytes(s, d, count) - ); + ); } [Theory] [MemberData(nameof(ArraySizesData))] public void ToZyxBytes(int count) { - TColor[] source = CreatePixelTestData(count); + TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * 3]; for (int i = 0; i < count; i++) @@ -261,7 +264,7 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => Operations.ToZyxBytes(s, d, count) - ); + ); } [Theory] @@ -269,7 +272,7 @@ namespace ImageSharp.Tests.Colors public void PackFromZyxwBytes(int count) { byte[] source = CreateByteTestData(count * 4); - TColor[] expected = new TColor[count]; + TPixel[] expected = new TPixel[count]; for (int i = 0; i < count; i++) { @@ -282,14 +285,14 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => Operations.PackFromZyxwBytes(s, d, count) - ); + ); } [Theory] [MemberData(nameof(ArraySizesData))] public void ToZyxwBytes(int count) { - TColor[] source = CreatePixelTestData(count); + TPixel[] source = CreatePixelTestData(count); byte[] expected = new byte[count * 4]; for (int i = 0; i < count; i++) @@ -302,26 +305,26 @@ namespace ImageSharp.Tests.Colors source, expected, (s, d) => Operations.ToZyxwBytes(s, d, count) - ); + ); } - + private class TestBuffers : IDisposable where TSource : struct where TDest : struct { - public PinnedBuffer SourceBuffer { get; } - public PinnedBuffer ActualDestBuffer { get; } - public PinnedBuffer ExpectedDestBuffer { get; } + public Buffer SourceBuffer { get; } + public Buffer ActualDestBuffer { get; } + public Buffer ExpectedDestBuffer { get; } + + public Span Source => this.SourceBuffer; + public Span ActualDest => this.ActualDestBuffer; - public BufferSpan Source => this.SourceBuffer; - public BufferSpan ActualDest => this.ActualDestBuffer; - public TestBuffers(TSource[] source, TDest[] expectedDest) { - this.SourceBuffer = new PinnedBuffer(source); - this.ExpectedDestBuffer = new PinnedBuffer(expectedDest); - this.ActualDestBuffer = new PinnedBuffer(expectedDest.Length); + this.SourceBuffer = new Buffer(source); + this.ExpectedDestBuffer = new Buffer(expectedDest); + this.ActualDestBuffer = new Buffer(expectedDest.Length); } public void Dispose() @@ -364,13 +367,13 @@ namespace ImageSharp.Tests.Colors internal static void TestOperation( TSource[] source, TDest[] expected, - Action, BufferSpan> action) + Action, Buffer> action) where TSource : struct where TDest : struct { using (TestBuffers buffers = new TestBuffers(source, expected)) { - action(buffers.Source, buffers.ActualDest); + action(buffers.Source, buffers.ActualDestBuffer); buffers.Verify(); } } @@ -387,9 +390,9 @@ namespace ImageSharp.Tests.Colors return result; } - internal static TColor[] CreatePixelTestData(int length) + internal static TPixel[] CreatePixelTestData(int length) { - TColor[] result = new TColor[length]; + TPixel[] result = new TPixel[length]; Random rnd = new Random(42); // Deterministic random values diff --git a/tests/ImageSharp.Tests/Colors/ColorTests.cs b/tests/ImageSharp.Tests/Colors/Rgba32Tests.cs similarity index 72% rename from tests/ImageSharp.Tests/Colors/ColorTests.cs rename to tests/ImageSharp.Tests/Colors/Rgba32Tests.cs index 312e664662..5509cbddcf 100644 --- a/tests/ImageSharp.Tests/Colors/ColorTests.cs +++ b/tests/ImageSharp.Tests/Colors/Rgba32Tests.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -8,12 +8,14 @@ namespace ImageSharp.Tests using System; using System.Numerics; + using ImageSharp.PixelFormats; + using Xunit; /// - /// Tests the struct. + /// Tests the struct. /// - public class ColorTests + public class Rgba32Tests { /// /// Tests the equality operators for equality. @@ -21,12 +23,12 @@ namespace ImageSharp.Tests [Fact] public void AreEqual() { - Color color1 = new Color(0, 0, 0); - Color color2 = new Color(0, 0, 0, 1F); - Color color3 = Color.FromHex("#000"); - Color color4 = Color.FromHex("#000F"); - Color color5 = Color.FromHex("#000000"); - Color color6 = Color.FromHex("#000000FF"); + Rgba32 color1 = new Rgba32(0, 0, 0); + Rgba32 color2 = new Rgba32(0, 0, 0, 1F); + Rgba32 color3 = Rgba32.FromHex("#000"); + Rgba32 color4 = Rgba32.FromHex("#000F"); + Rgba32 color5 = Rgba32.FromHex("#000000"); + Rgba32 color6 = Rgba32.FromHex("#000000FF"); Assert.Equal(color1, color2); Assert.Equal(color1, color3); @@ -41,11 +43,11 @@ namespace ImageSharp.Tests [Fact] public void AreNotEqual() { - Color color1 = new Color(255, 0, 0, 255); - Color color2 = new Color(0, 0, 0, 255); - Color color3 = Color.FromHex("#000"); - Color color4 = Color.FromHex("#000000"); - Color color5 = Color.FromHex("#FF000000"); + Rgba32 color1 = new Rgba32(255, 0, 0, 255); + Rgba32 color2 = new Rgba32(0, 0, 0, 255); + Rgba32 color3 = Rgba32.FromHex("#000"); + Rgba32 color4 = Rgba32.FromHex("#000000"); + Rgba32 color5 = Rgba32.FromHex("#FF000000"); Assert.NotEqual(color1, color2); Assert.NotEqual(color1, color3); @@ -59,25 +61,25 @@ namespace ImageSharp.Tests [Fact] public void ConstructorAssignsProperties() { - Color color1 = new Color(1, .1f, .133f, .864f); + Rgba32 color1 = new Rgba32(1, .1f, .133f, .864f); Assert.Equal(255, color1.R); Assert.Equal((byte)Math.Round(.1f * 255), color1.G); Assert.Equal((byte)Math.Round(.133f * 255), color1.B); Assert.Equal((byte)Math.Round(.864f * 255), color1.A); - Color color2 = new Color(1, .1f, .133f); + Rgba32 color2 = new Rgba32(1, .1f, .133f); Assert.Equal(255, color2.R); Assert.Equal(Math.Round(.1f * 255), color2.G); Assert.Equal(Math.Round(.133f * 255), color2.B); Assert.Equal(255, color2.A); - Color color4 = new Color(new Vector3(1, .1f, .133f)); + Rgba32 color4 = new Rgba32(new Vector3(1, .1f, .133f)); Assert.Equal(255, color4.R); Assert.Equal(Math.Round(.1f * 255), color4.G); Assert.Equal(Math.Round(.133f * 255), color4.B); Assert.Equal(255, color4.A); - Color color5 = new Color(new Vector4(1, .1f, .133f, .5f)); + Rgba32 color5 = new Rgba32(new Vector4(1, .1f, .133f, .5f)); Assert.Equal(255, color5.R); Assert.Equal(Math.Round(.1f * 255), color5.G); Assert.Equal(Math.Round(.133f * 255), color5.B); @@ -90,7 +92,7 @@ namespace ImageSharp.Tests [Fact] public void FromAndToHex() { - Color color = Color.FromHex("#AABBCCDD"); + Rgba32 color = Rgba32.FromHex("#AABBCCDD"); Assert.Equal(170, color.R); Assert.Equal(187, color.G); Assert.Equal(204, color.B); @@ -118,12 +120,14 @@ namespace ImageSharp.Tests [Fact] public unsafe void ByteLayout() { - Color color = new Color(1, 2, 3, 4); + Rgba32 color = new Rgba32(1, 2, 3, 4); byte* colorBase = (byte*)&color; Assert.Equal(1, colorBase[0]); Assert.Equal(2, colorBase[1]); Assert.Equal(3, colorBase[2]); Assert.Equal(4, colorBase[3]); + + Assert.Equal(4, sizeof(Rgba32)); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/RgbaVectorTests.cs b/tests/ImageSharp.Tests/Colors/RgbaVectorTests.cs new file mode 100644 index 0000000000..1f5df6d880 --- /dev/null +++ b/tests/ImageSharp.Tests/Colors/RgbaVectorTests.cs @@ -0,0 +1,134 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + using ImageSharp.PixelFormats; + + using Xunit; + + /// + /// Tests the struct. + /// + public class RgbaVectorTests + { + /// + /// Tests the equality operators for equality. + /// + [Fact] + public void AreEqual() + { + RgbaVector color1 = new RgbaVector(0, 0, 0F); + RgbaVector color2 = new RgbaVector(0, 0, 0, 1F); + RgbaVector color3 = RgbaVector.FromHex("#000"); + RgbaVector color4 = RgbaVector.FromHex("#000F"); + RgbaVector color5 = RgbaVector.FromHex("#000000"); + RgbaVector color6 = RgbaVector.FromHex("#000000FF"); + + Assert.Equal(color1, color2); + Assert.Equal(color1, color3); + Assert.Equal(color1, color4); + Assert.Equal(color1, color5); + Assert.Equal(color1, color6); + } + + /// + /// Tests the equality operators for inequality. + /// + [Fact] + public void AreNotEqual() + { + RgbaVector color1 = new RgbaVector(1, 0, 0, 1); + RgbaVector color2 = new RgbaVector(0, 0, 0, 1); + RgbaVector color3 = RgbaVector.FromHex("#000"); + RgbaVector color4 = RgbaVector.FromHex("#000000"); + RgbaVector color5 = RgbaVector.FromHex("#FF000000"); + + Assert.NotEqual(color1, color2); + Assert.NotEqual(color1, color3); + Assert.NotEqual(color1, color4); + Assert.NotEqual(color1, color5); + } + + /// + /// Tests whether the color constructor correctly assign properties. + /// + [Fact] + public void ConstructorAssignsProperties() + { + RgbaVector color1 = new RgbaVector(1, .1F, .133F, .864F); + Assert.Equal(1F, color1.R); + Assert.Equal(.1F, color1.G); + Assert.Equal(.133F, color1.B); + Assert.Equal(.864F, color1.A); + + RgbaVector color2 = new RgbaVector(1, .1f, .133f); + Assert.Equal(1F, color2.R); + Assert.Equal(.1F, color2.G); + Assert.Equal(.133F, color2.B); + Assert.Equal(1F, color2.A); + + RgbaVector color4 = new RgbaVector(new Vector3(1, .1f, .133f)); + Assert.Equal(1F, color4.R); + Assert.Equal(.1F, color4.G); + Assert.Equal(.133F, color4.B); + Assert.Equal(1F, color4.A); + + RgbaVector color5 = new RgbaVector(new Vector4(1, .1f, .133f, .5f)); + Assert.Equal(1F, color5.R); + Assert.Equal(.1F, color5.G); + Assert.Equal(.133F, color5.B); + Assert.Equal(.5F, color5.A); + } + + /// + /// Tests whether FromHex and ToHex work correctly. + /// + [Fact] + public void FromAndToHex() + { + RgbaVector color = RgbaVector.FromHex("#AABBCCDD"); + Assert.Equal(170 / 255F, color.R); + Assert.Equal(187 / 255F, color.G); + Assert.Equal(204 / 255F, color.B); + Assert.Equal(221 / 255F, color.A); + + color.A = 170 / 255F; + color.B = 187 / 255F; + color.G = 204 / 255F; + color.R = 221 / 255F; + + Assert.Equal("DDCCBBAA", color.ToHex()); + + color.R = 0; + + Assert.Equal("00CCBBAA", color.ToHex()); + + color.A = 255 / 255F; + + Assert.Equal("00CCBBFF", color.ToHex()); + } + + /// + /// Tests that the individual float elements are layed out in RGBA order. + /// + [Fact] + public void FloatLayout() + { + RgbaVector color = new RgbaVector(1F, 2, 3, 4); + Vector4 colorBase = Unsafe.As(ref Unsafe.Add(ref color, 0)); + float[] ordered = new float[4]; + colorBase.CopyTo(ordered); + + Assert.Equal(1, ordered[0]); + Assert.Equal(2, ordered[1]); + Assert.Equal(3, ordered[2]); + Assert.Equal(4, ordered[3]); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/UnPackedPixelTests.cs b/tests/ImageSharp.Tests/Colors/UnPackedPixelTests.cs new file mode 100644 index 0000000000..25a61453b2 --- /dev/null +++ b/tests/ImageSharp.Tests/Colors/UnPackedPixelTests.cs @@ -0,0 +1,150 @@ +namespace ImageSharp.Tests.Colors +{ + using System.Numerics; + + using ImageSharp.PixelFormats; + + using Xunit; + + public class UnPackedPixelTests + { + [Fact] + public void Color_Types_From_Bytes_Produce_Equal_Scaled_Component_OutPut() + { + Rgba32 color = new Rgba32(24, 48, 96, 192); + RgbaVector colorVector = new RgbaVector(24, 48, 96, 192); + + Assert.Equal(color.R, (byte)(colorVector.R * 255)); + Assert.Equal(color.G, (byte)(colorVector.G * 255)); + Assert.Equal(color.B, (byte)(colorVector.B * 255)); + Assert.Equal(color.A, (byte)(colorVector.A * 255)); + } + + [Fact] + public void Color_Types_From_Floats_Produce_Equal_Scaled_Component_OutPut() + { + Rgba32 color = new Rgba32(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); + RgbaVector colorVector = new RgbaVector(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); + + Assert.Equal(color.R, (byte)(colorVector.R * 255)); + Assert.Equal(color.G, (byte)(colorVector.G * 255)); + Assert.Equal(color.B, (byte)(colorVector.B * 255)); + Assert.Equal(color.A, (byte)(colorVector.A * 255)); + } + + [Fact] + public void Color_Types_From_Vector4_Produce_Equal_Scaled_Component_OutPut() + { + Rgba32 color = new Rgba32(new Vector4(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F)); + RgbaVector colorVector = new RgbaVector(new Vector4(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F)); + + Assert.Equal(color.R, (byte)(colorVector.R * 255)); + Assert.Equal(color.G, (byte)(colorVector.G * 255)); + Assert.Equal(color.B, (byte)(colorVector.B * 255)); + Assert.Equal(color.A, (byte)(colorVector.A * 255)); + } + + [Fact] + public void Color_Types_From_Vector3_Produce_Equal_Scaled_Component_OutPut() + { + Rgba32 color = new Rgba32(new Vector3(24 / 255F, 48 / 255F, 96 / 255F)); + RgbaVector colorVector = new RgbaVector(new Vector3(24 / 255F, 48 / 255F, 96 / 255F)); + + Assert.Equal(color.R, (byte)(colorVector.R * 255)); + Assert.Equal(color.G, (byte)(colorVector.G * 255)); + Assert.Equal(color.B, (byte)(colorVector.B * 255)); + Assert.Equal(color.A, (byte)(colorVector.A * 255)); + } + + [Fact] + public void Color_Types_From_Hex_Produce_Equal_Scaled_Component_OutPut() + { + Rgba32 color = Rgba32.FromHex("183060C0"); + RgbaVector colorVector = RgbaVector.FromHex("183060C0"); + + Assert.Equal(color.R, (byte)(colorVector.R * 255)); + Assert.Equal(color.G, (byte)(colorVector.G * 255)); + Assert.Equal(color.B, (byte)(colorVector.B * 255)); + Assert.Equal(color.A, (byte)(colorVector.A * 255)); + } + + [Fact] + public void Color_Types_To_Vector4_Produce_Equal_OutPut() + { + Rgba32 color = new Rgba32(24, 48, 96, 192); + RgbaVector colorVector = new RgbaVector(24, 48, 96, 192); + + Assert.Equal(color.ToVector4(), colorVector.ToVector4()); + } + + [Fact] + public void Color_Types_To_RgbBytes_Produce_Equal_OutPut() + { + Rgba32 color = new Rgba32(24, 48, 96, 192); + RgbaVector colorVector = new RgbaVector(24, 48, 96, 192); + + byte[] rgb = new byte[3]; + byte[] rgbVector = new byte[3]; + + color.ToXyzBytes(rgb, 0); + colorVector.ToXyzBytes(rgbVector, 0); + + Assert.Equal(rgb, rgbVector); + } + + [Fact] + public void Color_Types_To_RgbaBytes_Produce_Equal_OutPut() + { + Rgba32 color = new Rgba32(24, 48, 96, 192); + RgbaVector colorVector = new RgbaVector(24, 48, 96, 192); + + byte[] rgba = new byte[4]; + byte[] rgbaVector = new byte[4]; + + color.ToXyzwBytes(rgba, 0); + colorVector.ToXyzwBytes(rgbaVector, 0); + + Assert.Equal(rgba, rgbaVector); + } + + [Fact] + public void Color_Types_To_BgrBytes_Produce_Equal_OutPut() + { + Rgba32 color = new Rgba32(24, 48, 96, 192); + RgbaVector colorVector = new RgbaVector(24, 48, 96, 192); + + byte[] bgr = new byte[3]; + byte[] bgrVector = new byte[3]; + + color.ToZyxBytes(bgr, 0); + colorVector.ToZyxBytes(bgrVector, 0); + + Assert.Equal(bgr, bgrVector); + } + + [Fact] + public void Color_Types_To_BgraBytes_Produce_Equal_OutPut() + { + Rgba32 color = new Rgba32(24, 48, 96, 192); + RgbaVector colorVector = new RgbaVector(24, 48, 96, 192); + + byte[] bgra = new byte[4]; + byte[] bgraVector = new byte[4]; + + color.ToZyxwBytes(bgra, 0); + colorVector.ToZyxwBytes(bgraVector, 0); + + Assert.Equal(bgra, bgraVector); + } + + [Fact] + public void Color_Types_To_Hex_Produce_Equal_OutPut() + { + Rgba32 color = new Rgba32(24, 48, 96, 192); + RgbaVector colorVector = new RgbaVector(24, 48, 96, 192); + + // 183060C0 + Assert.Equal(color.ToHex(), colorVector.ToHex()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Common/PinnedImageBufferTests.cs b/tests/ImageSharp.Tests/Common/Buffer2DTests.cs similarity index 61% rename from tests/ImageSharp.Tests/Common/PinnedImageBufferTests.cs rename to tests/ImageSharp.Tests/Common/Buffer2DTests.cs index a23f93a70b..5f44a132d7 100644 --- a/tests/ImageSharp.Tests/Common/PinnedImageBufferTests.cs +++ b/tests/ImageSharp.Tests/Common/Buffer2DTests.cs @@ -1,20 +1,36 @@ // ReSharper disable InconsistentNaming namespace ImageSharp.Tests.Common { + using System; using System.Runtime.CompilerServices; + using ImageSharp.Memory; + using Xunit; using static TestStructs; - public unsafe class PinnedImageBufferTests + public unsafe class Buffer2DTests { + // ReSharper disable once ClassNeverInstantiated.Local + private class Assert : Xunit.Assert + { + public static void SpanPointsTo(Span span, Buffer buffer, int bufferOffset = 0) + where T : struct + { + ref T actual = ref span.DangerousGetPinnableReference(); + ref T expected = ref Unsafe.Add(ref buffer[0], bufferOffset); + + Assert.True(Unsafe.AreSame(ref expected, ref actual), "span does not point to the expected position"); + } + } + [Theory] [InlineData(7, 42)] [InlineData(1025, 17)] public void Construct(int width, int height) { - using (PinnedImageBuffer buffer = new PinnedImageBuffer(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); @@ -28,7 +44,7 @@ namespace ImageSharp.Tests.Common public void Construct_FromExternalArray(int width, int height) { Foo[] array = new Foo[width * height + 10]; - using (PinnedImageBuffer buffer = new PinnedImageBuffer(array, width, height)) + using (Buffer2D buffer = new Buffer2D(array, width, height)) { Assert.Equal(width, buffer.Width); Assert.Equal(height, buffer.Height); @@ -42,7 +58,7 @@ namespace ImageSharp.Tests.Common { for (int i = 0; i < 100; i++) { - using (PinnedImageBuffer buffer = PinnedImageBuffer.CreateClean(42, 42)) + using (Buffer2D buffer = Buffer2D.CreateClean(42, 42)) { for (int j = 0; j < buffer.Length; j++) { @@ -59,13 +75,13 @@ namespace ImageSharp.Tests.Common [InlineData(17, 42, 41)] public void GetRowSpanY(int width, int height, int y) { - using (PinnedImageBuffer buffer = new PinnedImageBuffer(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { - BufferSpan span = buffer.GetRowSpan(y); + Span span = buffer.GetRowSpan(y); - Assert.Equal(width * y, span.Start); + // Assert.Equal(width * y, span.Start); Assert.Equal(width, span.Length); - Assert.Equal(buffer.Pointer + sizeof(Foo) * width * y, span.PointerAtOffset); + Assert.SpanPointsTo(span, buffer, width * y); } } @@ -75,13 +91,13 @@ namespace ImageSharp.Tests.Common [InlineData(17, 42, 0, 41)] public void GetRowSpanXY(int width, int height, int x, int y) { - using (PinnedImageBuffer buffer = new PinnedImageBuffer(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { - BufferSpan span = buffer.GetRowSpan(x, y); + Span span = buffer.GetRowSpan(x, y); - Assert.Equal(width * y + x, span.Start); + // Assert.Equal(width * y + x, span.Start); Assert.Equal(width - x, span.Length); - Assert.Equal(buffer.Pointer + sizeof(Foo) * (width * y + x), span.PointerAtOffset); + Assert.SpanPointsTo(span, buffer, width * y + x); } } @@ -91,7 +107,7 @@ namespace ImageSharp.Tests.Common [InlineData(99, 88, 98, 87)] public void Indexer(int width, int height, int x, int y) { - using (PinnedImageBuffer buffer = new PinnedImageBuffer(width, height)) + using (Buffer2D buffer = new Buffer2D(width, height)) { Foo[] array = buffer.Array; diff --git a/tests/ImageSharp.Tests/Common/BufferSpanTests.cs b/tests/ImageSharp.Tests/Common/BufferSpanTests.cs index aee032accd..af33a981ba 100644 --- a/tests/ImageSharp.Tests/Common/BufferSpanTests.cs +++ b/tests/ImageSharp.Tests/Common/BufferSpanTests.cs @@ -1,28 +1,60 @@ // ReSharper disable ObjectCreationAsStatement // ReSharper disable InconsistentNaming + namespace ImageSharp.Tests.Common { using System; + using System.Numerics; using System.Runtime.CompilerServices; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + using Xunit; using static TestStructs; - public unsafe class BufferSpanTests + public unsafe class SpanTests { + // ReSharper disable once ClassNeverInstantiated.Local + private class Assert : Xunit.Assert + { + public static void SameRefs(ref T1 a, ref T2 b) + { + ref T1 bb = ref Unsafe.As(ref b); + + True(Unsafe.AreSame(ref a, ref bb), "References are not same!"); + } + } + + [Fact] + public void FetchVector() + { + float[] stuff = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + + Span span = new Span(stuff); + + ref Vector v = ref span.FetchVector(); + + Assert.Equal(0, v[0]); + Assert.Equal(1, v[1]); + Assert.Equal(2, v[2]); + Assert.Equal(3, v[3]); + } + [Fact] public void AsBytes() { Foo[] fooz = { new Foo(1, 2), new Foo(3, 4), new Foo(5, 6) }; - using (PinnedBuffer colorBuf = new PinnedBuffer(fooz)) + using (Buffer colorBuf = new Buffer(fooz)) { - BufferSpan orig = colorBuf.Slice(1); - BufferSpan asBytes = (BufferSpan < byte > )orig; + Span orig = colorBuf.Slice(1); + Span asBytes = orig.AsBytes(); - Assert.Equal(asBytes.Start, sizeof(Foo)); - Assert.Equal(orig.PointerAtOffset, asBytes.PointerAtOffset); + // Assert.Equal(asBytes.Start, sizeof(Foo)); + Assert.Equal(orig.Length * Unsafe.SizeOf(), asBytes.Length); + Assert.SameRefs(ref orig.DangerousGetPinnableReference(), ref asBytes.DangerousGetPinnableReference()); } } @@ -32,16 +64,14 @@ namespace ImageSharp.Tests.Common public void Basic() { Foo[] array = Foo.CreateArray(3); - fixed (Foo* p = array) - { - // Act: - BufferSpan span = new BufferSpan(array, p); - // Assert: - Assert.Equal(array, span.Array); - Assert.Equal((IntPtr)p, span.PointerAtOffset); - Assert.Equal(3, span.Length); - } + // Act: + Span span = new Span(array); + + // Assert: + Assert.Equal(array, span.ToArray()); + Assert.Equal(3, span.Length); + Assert.SameRefs(ref array[0], ref span.DangerousGetPinnableReference()); } [Fact] @@ -49,17 +79,13 @@ namespace ImageSharp.Tests.Common { Foo[] array = Foo.CreateArray(4); int start = 2; - fixed (Foo* p = array) - { - // Act: - BufferSpan span = new BufferSpan(array, p, start); - - // Assert: - Assert.Equal(array, span.Array); - Assert.Equal(start, span.Start); - Assert.Equal((IntPtr)(p + start), span.PointerAtOffset); - Assert.Equal(array.Length - start, span.Length); - } + + // Act: + Span span = new Span(array, start); + + // Assert: + Assert.SameRefs(ref array[start], ref span.DangerousGetPinnableReference()); + Assert.Equal(array.Length - start, span.Length); } [Fact] @@ -68,17 +94,12 @@ namespace ImageSharp.Tests.Common Foo[] array = Foo.CreateArray(10); int start = 2; int length = 3; - fixed (Foo* p = array) - { - // Act: - BufferSpan span = new BufferSpan(array, p, start, length); - - // Assert: - Assert.Equal(array, span.Array); - Assert.Equal(start, span.Start); - Assert.Equal((IntPtr)(p + start), span.PointerAtOffset); - Assert.Equal(length, span.Length); - } + // Act: + Span span = new Span(array, start, length); + + // Assert: + Assert.SameRefs(ref array[start], ref span.DangerousGetPinnableReference()); + Assert.Equal(length, span.Length); } } @@ -92,19 +113,14 @@ namespace ImageSharp.Tests.Common int start1 = 2; int totalOffset = start0 + start1; - fixed (Foo* p = array) - { - BufferSpan span = new BufferSpan(array, p, start0); + Span span = new Span(array, start0); - // Act: - span = span.Slice(start1); + // Act: + span = span.Slice(start1); - // Assert: - Assert.Equal(array, span.Array); - Assert.Equal(totalOffset, span.Start); - Assert.Equal((IntPtr)(p + totalOffset), span.PointerAtOffset); - Assert.Equal(array.Length - totalOffset, span.Length); - } + // Assert: + Assert.SameRefs(ref array[totalOffset], ref span.DangerousGetPinnableReference()); + Assert.Equal(array.Length - totalOffset, span.Length); } [Fact] @@ -116,46 +132,35 @@ namespace ImageSharp.Tests.Common int totalOffset = start0 + start1; int sliceLength = 3; - fixed (Foo* p = array) - { - BufferSpan span = new BufferSpan(array, p, start0); + Span span = new Span(array, start0); - // Act: - span = span.Slice(start1, sliceLength); + // Act: + span = span.Slice(start1, sliceLength); - // Assert: - Assert.Equal(array, span.Array); - Assert.Equal(totalOffset, span.Start); - Assert.Equal((IntPtr)(p + totalOffset), span.PointerAtOffset); - Assert.Equal(sliceLength, span.Length); - } + // Assert: + Assert.SameRefs(ref array[totalOffset], ref span.DangerousGetPinnableReference()); + Assert.Equal(sliceLength, span.Length); } } + //[Theory] + //[InlineData(4)] + //[InlineData(1500)] + //public void Clear(int count) + //{ + // Foo[] array = Foo.CreateArray(count + 42); + // int offset = 2; + // Span ap = new Span(array, offset); - [Theory] - [InlineData(4)] - [InlineData(1500)] - public void Clear(int count) - { - Foo[] array = Foo.CreateArray(count + 42); - - int offset = 2; - fixed (Foo* p = array) - { - BufferSpan ap = new BufferSpan(array, p, offset); - - // Act: - ap.Clear(count); - - Assert.NotEqual(default(Foo), array[offset-1]); - Assert.Equal(default(Foo), array[offset]); - Assert.Equal(default(Foo), array[offset + count-1]); - Assert.NotEqual(default(Foo), array[offset + count]); - } - } + // // Act: + // ap.Clear(count); + // Assert.NotEqual(default(Foo), array[offset - 1]); + // Assert.Equal(default(Foo), array[offset]); + // Assert.Equal(default(Foo), array[offset + count - 1]); + // Assert.NotEqual(default(Foo), array[offset + count]); + //} public class Indexer { @@ -175,14 +180,11 @@ namespace ImageSharp.Tests.Common public void Read(int length, int start, int index) { Foo[] a = Foo.CreateArray(length); - fixed (Foo* p = a) - { - BufferSpan span = new BufferSpan(a, p, start); + Span span = new Span(a, start); - Foo element = span[index]; + Foo element = span[index]; - Assert.Equal(a[start + index], element); - } + Assert.Equal(a[start + index], element); } [Theory] @@ -190,17 +192,46 @@ namespace ImageSharp.Tests.Common public void Write(int length, int start, int index) { Foo[] a = Foo.CreateArray(length); - fixed (Foo* p = a) - { - BufferSpan span = new BufferSpan(a, p, start); + Span span = new Span(a, start); - span[index] = new Foo(666, 666); + span[index] = new Foo(666, 666); - Assert.Equal(new Foo(666, 666), a[start + index]); - } + Assert.Equal(new Foo(666, 666), a[start + index]); + } + + [Theory] + [InlineData(10, 0, 0, 5)] + [InlineData(10, 1, 1, 5)] + [InlineData(10, 1, 1, 6)] + [InlineData(10, 1, 1, 7)] + public void AsBytes_Read(int length, int start, int index, int byteOffset) + { + Foo[] a = Foo.CreateArray(length); + Span span = new Span(a, start); + + Span bytes = span.AsBytes(); + + byte actual = bytes[index * Unsafe.SizeOf() + byteOffset]; + + ref byte baseRef = ref Unsafe.As(ref a[0]); + byte expected = Unsafe.Add(ref baseRef, (start + index) * Unsafe.SizeOf() + byteOffset); + + Assert.Equal(expected, actual); } } + [Theory] + [InlineData(0, 4)] + [InlineData(2, 4)] + [InlineData(3, 4)] + public void DangerousGetPinnableReference(int start, int length) + { + Foo[] a = Foo.CreateArray(length); + Span span = new Span(a, start); + ref Foo r = ref span.DangerousGetPinnableReference(); + + Assert.True(Unsafe.AreSame(ref a[start], ref r)); + } public class Copy { @@ -238,14 +269,10 @@ namespace ImageSharp.Tests.Common Foo[] source = Foo.CreateArray(count + 2); Foo[] dest = new Foo[count + 5]; - fixed (Foo* pSource = source) - fixed (Foo* pDest = dest) - { - BufferSpan apSource = new BufferSpan(source, pSource, 1); - BufferSpan apDest = new BufferSpan(dest, pDest, 1); + Span apSource = new Span(source, 1); + Span apDest = new Span(dest, 1); - BufferSpan.Copy(apSource, apDest, count-1); - } + SpanHelper.Copy(apSource, apDest, count - 1); AssertNotDefault(source, 1); AssertNotDefault(dest, 1); @@ -253,7 +280,7 @@ namespace ImageSharp.Tests.Common Assert.NotEqual(source[0], dest[0]); Assert.Equal(source[1], dest[1]); Assert.Equal(source[2], dest[2]); - Assert.Equal(source[count-1], dest[count-1]); + Assert.Equal(source[count - 1], dest[count - 1]); Assert.NotEqual(source[count], dest[count]); } @@ -265,14 +292,10 @@ namespace ImageSharp.Tests.Common AlignedFoo[] source = AlignedFoo.CreateArray(count + 2); AlignedFoo[] dest = new AlignedFoo[count + 5]; - fixed (AlignedFoo* pSource = source) - fixed (AlignedFoo* pDest = dest) - { - BufferSpan apSource = new BufferSpan(source, pSource, 1); - BufferSpan apDest = new BufferSpan(dest, pDest, 1); + Span apSource = new Span(source, 1); + Span apDest = new Span(dest, 1); - BufferSpan.Copy(apSource, apDest, count - 1); - } + SpanHelper.Copy(apSource, apDest, count - 1); AssertNotDefault(source, 1); AssertNotDefault(dest, 1); @@ -289,17 +312,13 @@ namespace ImageSharp.Tests.Common [InlineData(1500)] public void IntToInt(int count) { - int[] source = CreateTestInts(count+2); + int[] source = CreateTestInts(count + 2); int[] dest = new int[count + 5]; - fixed (int* pSource = source) - fixed (int* pDest = dest) - { - BufferSpan apSource = new BufferSpan(source, pSource, 1); - BufferSpan apDest = new BufferSpan(dest, pDest, 1); + Span apSource = new Span(source, 1); + Span apDest = new Span(dest, 1); - BufferSpan.Copy(apSource, apDest, count -1); - } + SpanHelper.Copy(apSource, apDest, count - 1); AssertNotDefault(source, 1); AssertNotDefault(dest, 1); @@ -317,17 +336,13 @@ namespace ImageSharp.Tests.Common public void GenericToBytes(int count) { int destCount = count * sizeof(Foo); - Foo[] source = Foo.CreateArray(count+2); - byte[] dest = new byte[destCount + sizeof(Foo)*2]; + Foo[] source = Foo.CreateArray(count + 2); + byte[] dest = new byte[destCount + sizeof(Foo) * 2]; - fixed (Foo* pSource = source) - fixed (byte* pDest = dest) - { - BufferSpan apSource = new BufferSpan(source, pSource, 1); - BufferSpan apDest = new BufferSpan(dest, pDest, sizeof(Foo)); + Span apSource = new Span(source, 1); + Span apDest = new Span(dest, sizeof(Foo)); - BufferSpan.Copy(apSource, apDest, count - 1); - } + SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(Foo)); AssertNotDefault(source, 1); @@ -347,14 +362,10 @@ namespace ImageSharp.Tests.Common AlignedFoo[] source = AlignedFoo.CreateArray(count + 2); byte[] dest = new byte[destCount + sizeof(AlignedFoo) * 2]; - fixed (AlignedFoo* pSource = source) - fixed (byte* pDest = dest) - { - BufferSpan apSource = new BufferSpan(source, pSource, 1); - BufferSpan apDest = new BufferSpan(dest, pDest, sizeof(AlignedFoo)); + Span apSource = new Span(source, 1); + Span apDest = new Span(dest, sizeof(AlignedFoo)); - BufferSpan.Copy(apSource, apDest, count - 1); - } + SpanHelper.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(AlignedFoo)); AssertNotDefault(source, 1); @@ -371,17 +382,13 @@ namespace ImageSharp.Tests.Common public void IntToBytes(int count) { int destCount = count * sizeof(int); - int[] source = CreateTestInts(count+2); + int[] source = CreateTestInts(count + 2); byte[] dest = new byte[destCount + sizeof(int) + 1]; - fixed (int* pSource = source) - fixed (byte* pDest = dest) - { - BufferSpan apSource = new BufferSpan(source, pSource); - BufferSpan apDest = new BufferSpan(dest, pDest); + Span apSource = new Span(source); + Span apDest = new Span(dest); - BufferSpan.Copy(apSource, apDest, count); - } + SpanHelper.Copy(apSource.AsBytes(), apDest, count * sizeof(int)); AssertNotDefault(source, 1); @@ -398,15 +405,11 @@ namespace ImageSharp.Tests.Common int srcCount = count * sizeof(Foo); byte[] source = CreateTestBytes(srcCount); Foo[] dest = new Foo[count + 2]; - - fixed(byte* pSource = source) - fixed (Foo* pDest = dest) - { - BufferSpan apSource = new BufferSpan(source, pSource); - BufferSpan apDest = new BufferSpan(dest, pDest); - BufferSpan.Copy(apSource, apDest, count); - } + Span apSource = new Span(source); + Span apDest = new Span(dest); + + SpanHelper.Copy(apSource, apDest.AsBytes(), count * sizeof(Foo)); AssertNotDefault(source, sizeof(Foo) + 1); AssertNotDefault(dest, 1); @@ -418,14 +421,14 @@ namespace ImageSharp.Tests.Common } [Fact] - public void ColorToBytes() + public void Color32ToBytes() { - Color[] colors = { new Color(0, 1, 2, 3), new Color(4, 5, 6, 7), new Color(8, 9, 10, 11), }; + Rgba32[] colors = { new Rgba32(0, 1, 2, 3), new Rgba32(4, 5, 6, 7), new Rgba32(8, 9, 10, 11), }; - using (PinnedBuffer colorBuf = new PinnedBuffer(colors)) - using (PinnedBuffer byteBuf = new PinnedBuffer(colors.Length*4)) + using (Buffer colorBuf = new Buffer(colors)) + using (Buffer byteBuf = new Buffer(colors.Length * 4)) { - BufferSpan.Copy(colorBuf, byteBuf, colorBuf.Length); + SpanHelper.Copy(colorBuf.Span.AsBytes(), byteBuf, colorBuf.Length * sizeof(Rgba32)); byte[] a = byteBuf.Array; @@ -436,7 +439,6 @@ namespace ImageSharp.Tests.Common } } - internal static bool ElementsAreEqual(Foo[] array, byte[] rawArray, int index) { fixed (Foo* pArray = array) diff --git a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs b/tests/ImageSharp.Tests/Common/BufferTests.cs similarity index 54% rename from tests/ImageSharp.Tests/Common/PinnedBufferTests.cs rename to tests/ImageSharp.Tests/Common/BufferTests.cs index 5e812d5a01..010bf40b31 100644 --- a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs +++ b/tests/ImageSharp.Tests/Common/BufferTests.cs @@ -1,29 +1,48 @@ -namespace ImageSharp.Tests.Common +// ReSharper disable InconsistentNaming +namespace ImageSharp.Tests.Common { using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading.Tasks; + using ImageSharp.Memory; + using Xunit; using static TestStructs; - public unsafe class PinnedBufferTests + public unsafe class BufferTests { + // ReSharper disable once ClassNeverInstantiated.Local + private class Assert : Xunit.Assert + { + public static void SpanPointsTo(Span span, Buffer buffer, int bufferOffset = 0) + where T : struct + { + ref T actual = ref span.DangerousGetPinnableReference(); + ref T expected = ref Unsafe.Add(ref buffer[0], bufferOffset); + + Assert.True(Unsafe.AreSame(ref expected, ref actual), "span does not point to the expected position"); + } + + public static void Equal(void* expected, void* actual) + { + Assert.Equal((IntPtr)expected, (IntPtr)actual); + } + } + [Theory] [InlineData(42)] [InlineData(1111)] public void ConstructWithOwnArray(int count) { - using (PinnedBuffer buffer = new PinnedBuffer(count)) + using (Buffer buffer = new Buffer(count)) { Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.NotNull(buffer.Array); Assert.Equal(count, buffer.Length); Assert.True(buffer.Array.Length >= count); - - VerifyPointer(buffer); } } @@ -33,23 +52,19 @@ public void ConstructWithExistingArray(int count) { Foo[] array = new Foo[count]; - using (PinnedBuffer buffer = new PinnedBuffer(array)) + using (Buffer buffer = new Buffer(array)) { Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.Equal(array, buffer.Array); Assert.Equal(count, buffer.Length); - - VerifyPointer(buffer); } } - [Theory] - [InlineData(42)] - [InlineData(1111)] - public void Clear(int count) + [Fact] + public void Clear() { Foo[] a = { new Foo() { A = 1, B = 2 }, new Foo() { A = 3, B = 4 } }; - using (PinnedBuffer buffer = new PinnedBuffer(a)) + using (Buffer buffer = new Buffer(a)) { buffer.Clear(); @@ -63,7 +78,7 @@ { for (int i = 0; i < 100; i++) { - using (PinnedBuffer buffer = PinnedBuffer.CreateClean(42)) + using (Buffer buffer = Buffer.CreateClean(42)) { for (int j = 0; j < buffer.Length; j++) { @@ -90,7 +105,7 @@ { Foo[] a = Foo.CreateArray(length); - using (PinnedBuffer buffer = new PinnedBuffer(a)) + using (Buffer buffer = new Buffer(a)) { Foo element = buffer[index]; @@ -104,7 +119,7 @@ { Foo[] a = Foo.CreateArray(length); - using (PinnedBuffer buffer = new PinnedBuffer(a)) + using (Buffer buffer = new Buffer(a)) { buffer[index] = new Foo(666, 666); @@ -116,24 +131,24 @@ [Fact] public void Dispose() { - PinnedBuffer buffer = new PinnedBuffer(42); + Buffer buffer = new Buffer(42); buffer.Dispose(); Assert.True(buffer.IsDisposedOrLostArrayOwnership); } - + [Theory] [InlineData(7)] [InlineData(123)] public void CastToSpan(int bufferLength) { - using (PinnedBuffer buffer = new PinnedBuffer(bufferLength)) + using (Buffer buffer = new Buffer(bufferLength)) { - BufferSpan span = buffer; + Span span = buffer; - Assert.Equal(buffer.Array, span.Array); - Assert.Equal(0, span.Start); - Assert.Equal(buffer.Pointer, span.PointerAtOffset); + //Assert.Equal(buffer.Array, span.ToArray()); + //Assert.Equal(0, span.Start); + Assert.SpanPointsTo(span, buffer); Assert.Equal(span.Length, bufferLength); } } @@ -141,13 +156,13 @@ [Fact] public void Span() { - using (PinnedBuffer buffer = new PinnedBuffer(42)) + using (Buffer buffer = new Buffer(42)) { - BufferSpan span = buffer.Span; + Span span = buffer.Span; - Assert.Equal(buffer.Array, span.Array); - Assert.Equal(0, span.Start); - Assert.Equal(buffer.Pointer, span.PointerAtOffset); + // Assert.Equal(buffer.Array, span.ToArray()); + // Assert.Equal(0, span.Start); + Assert.SpanPointsTo(span, buffer); Assert.Equal(span.Length, 42); } } @@ -160,13 +175,11 @@ [InlineData(123, 17)] public void WithStartOnly(int bufferLength, int start) { - using (PinnedBuffer buffer = new PinnedBuffer(bufferLength)) + using (Buffer buffer = new Buffer(bufferLength)) { - BufferSpan span = buffer.Slice(start); + Span span = buffer.Slice(start); - Assert.Equal(buffer.Array, span.Array); - Assert.Equal(start, span.Start); - Assert.Equal(buffer.Pointer + start * Unsafe.SizeOf(), span.PointerAtOffset); + Assert.SpanPointsTo(span, buffer, start); Assert.Equal(span.Length, bufferLength - start); } } @@ -176,13 +189,11 @@ [InlineData(123, 17, 42)] public void WithStartAndLength(int bufferLength, int start, int spanLength) { - using (PinnedBuffer buffer = new PinnedBuffer(bufferLength)) + using (Buffer buffer = new Buffer(bufferLength)) { - BufferSpan span = buffer.Slice(start, spanLength); + Span span = buffer.Slice(start, spanLength); - Assert.Equal(buffer.Array, span.Array); - Assert.Equal(start, span.Start); - Assert.Equal(buffer.Pointer + start * Unsafe.SizeOf(), span.PointerAtOffset); + Assert.SpanPointsTo(span, buffer, start); Assert.Equal(span.Length, spanLength); } } @@ -192,9 +203,9 @@ public void UnPinAndTakeArrayOwnership() { Foo[] data = null; - using (PinnedBuffer buffer = new PinnedBuffer(42)) + using (Buffer buffer = new Buffer(42)) { - data = buffer.UnPinAndTakeArrayOwnership(); + data = buffer.TakeArrayOwnership(); Assert.True(buffer.IsDisposedOrLostArrayOwnership); } @@ -202,10 +213,41 @@ Assert.True(data.Length >= 42); } - private static void VerifyPointer(PinnedBuffer buffer) + public class Pin { - IntPtr ptr = (IntPtr)Unsafe.AsPointer(ref buffer.Array[0]); - Assert.Equal(ptr, buffer.Pointer); + [Fact] + public void ReturnsPinnedPointerToTheBeginningOfArray() + { + using (Buffer buffer = new Buffer(42)) + { + Foo* actual = (Foo*)buffer.Pin(); + fixed (Foo* expected = buffer.Array) + { + Assert.Equal(expected, actual); + } + } + } + + [Fact] + public void SecondCallReturnsTheSamePointer() + { + using (Buffer buffer = new Buffer(42)) + { + IntPtr ptr1 = buffer.Pin(); + IntPtr ptr2 = buffer.Pin(); + + Assert.Equal(ptr1, ptr2); + } + } + + [Fact] + public void WhenCalledOnDisposedBuffer_ThrowsInvalidOperationException() + { + Buffer buffer = new Buffer(42); + buffer.Dispose(); + + Assert.Throws(() => buffer.Pin()); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs b/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs index 7db7a4820b..efdcaa8484 100644 --- a/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs +++ b/tests/ImageSharp.Tests/Common/Fast2DArrayTests.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests.Common { using System; + using ImageSharp.Memory; + using Xunit; public class Fast2DArrayTests diff --git a/tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs b/tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs index 403dffba9c..1291160b22 100644 --- a/tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs +++ b/tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs @@ -8,6 +8,9 @@ namespace ImageSharp.Tests { using System.Linq; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + using Xunit; /// @@ -18,7 +21,7 @@ namespace ImageSharp.Tests [Fact] public void PixelDataPoolRentsMinimumSize() { - Color[] pixels = PixelDataPool.Rent(1024); + Rgba32[] pixels = PixelDataPool.Rent(1024); Assert.True(pixels.Length >= 1024); } @@ -26,9 +29,9 @@ namespace ImageSharp.Tests [Fact] public void PixelDataPoolDoesNotThrowWhenReturningNonPooled() { - Color[] pixels = new Color[1024]; + Rgba32[] pixels = new Rgba32[1024]; - PixelDataPool.Return(pixels); + PixelDataPool.Return(pixels); Assert.True(pixels.Length >= 1024); } @@ -39,7 +42,7 @@ namespace ImageSharp.Tests public void CalculateMaxArrayLength(bool isRawData) { int max = isRawData ? PixelDataPool.CalculateMaxArrayLength() - : PixelDataPool.CalculateMaxArrayLength(); + : PixelDataPool.CalculateMaxArrayLength(); Assert.Equal(max < int.MaxValue, !isRawData); } diff --git a/tests/ImageSharp.Tests/Common/TestStructs.cs b/tests/ImageSharp.Tests/Common/TestStructs.cs index 9e762bbd16..f7f09bea20 100644 --- a/tests/ImageSharp.Tests/Common/TestStructs.cs +++ b/tests/ImageSharp.Tests/Common/TestStructs.cs @@ -25,6 +25,8 @@ namespace ImageSharp.Tests.Common } return result; } + + public override string ToString() => $"({this.A},{this.B})"; } diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index c749239d71..aa3c4edfc1 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -7,10 +7,13 @@ namespace ImageSharp.Tests { using System; using System.Collections.Generic; + using System.IO; using System.Linq; using ImageSharp.Formats; using ImageSharp.IO; + using ImageSharp.PixelFormats; + using Xunit; /// @@ -21,7 +24,7 @@ namespace ImageSharp.Tests [Fact] public void DefaultsToLocalFileSystem() { - Configuration configuration = Configuration.CreateDefaultInstance(); + var configuration = Configuration.CreateDefaultInstance(); ImageSharp.IO.IFileSystem fs = configuration.FileSystem; @@ -31,7 +34,7 @@ namespace ImageSharp.Tests [Fact] public void IfAutoloadWellknwonFormatesIsTrueAllFormateAreLoaded() { - Configuration configuration = Configuration.CreateDefaultInstance(); + var configuration = Configuration.CreateDefaultInstance(); Assert.Equal(4, configuration.ImageFormats.Count); } @@ -93,7 +96,7 @@ namespace ImageSharp.Tests [Fact] public void TestAddImageFormatThrowsWithNullEncoder() { - TestFormat format = new TestFormat { Encoder = null }; + var format = new TestFormat { Encoder = null }; Assert.Throws(() => { @@ -108,7 +111,7 @@ namespace ImageSharp.Tests [Fact] public void TestAddImageFormatThrowsWithNullDecoder() { - TestFormat format = new TestFormat { Decoder = null }; + var format = new TestFormat { Decoder = null }; Assert.Throws(() => { @@ -123,7 +126,7 @@ namespace ImageSharp.Tests [Fact] public void TestAddImageFormatThrowsWithNullOrEmptyMimeType() { - TestFormat format = new TestFormat { MimeType = null }; + var format = new TestFormat { MimeType = null }; Assert.Throws(() => { @@ -145,7 +148,7 @@ namespace ImageSharp.Tests [Fact] public void TestAddImageFormatThrowsWithNullOrEmptyExtension() { - TestFormat format = new TestFormat { Extension = null }; + var format = new TestFormat { Extension = null }; Assert.Throws(() => { @@ -167,7 +170,7 @@ namespace ImageSharp.Tests [Fact] public void TestAddImageFormatThrowsWenSupportedExtensionsIsNullOrEmpty() { - TestFormat format = new TestFormat { SupportedExtensions = null }; + var format = new TestFormat { SupportedExtensions = null }; Assert.Throws(() => { @@ -189,7 +192,7 @@ namespace ImageSharp.Tests [Fact] public void TestAddImageFormatThrowsWithoutDefaultExtension() { - TestFormat format = new TestFormat { Extension = "test" }; + var format = new TestFormat { Extension = "test" }; Assert.Throws(() => { @@ -204,7 +207,7 @@ namespace ImageSharp.Tests [Fact] public void TestAddImageFormatThrowsWithEmptySupportedExtension() { - TestFormat format = new TestFormat + var format = new TestFormat { Extension = "test", SupportedExtensions = new[] { "test", string.Empty } @@ -236,7 +239,7 @@ namespace ImageSharp.Tests { Configuration.Default.AddImageFormat(new PngFormat()); - Image image = new Image(1, 1); + var image = new Image(1, 1); Assert.Equal(image.Configuration.ParallelOptions, Configuration.Default.ParallelOptions); Assert.Equal(image.Configuration.ImageFormats, Configuration.Default.ImageFormats); } @@ -249,8 +252,8 @@ namespace ImageSharp.Tests { Configuration.Default.AddImageFormat(new PngFormat()); - Image image = new Image(1, 1); - Image image2 = new Image(image); + var image = new Image(1, 1); + var image2 = new Image(image); Assert.Equal(image2.Configuration.ParallelOptions, image.Configuration.ParallelOptions); Assert.True(image2.Configuration.ImageFormats.SequenceEqual(image.Configuration.ImageFormats)); } diff --git a/tests/ImageSharp.Tests/Drawing/BeziersTests.cs b/tests/ImageSharp.Tests/Drawing/BeziersTests.cs index a1d4d3fd59..ff68921994 100644 --- a/tests/ImageSharp.Tests/Drawing/BeziersTests.cs +++ b/tests/ImageSharp.Tests/Drawing/BeziersTests.cs @@ -11,6 +11,9 @@ namespace ImageSharp.Tests.Drawing using System.Diagnostics.CodeAnalysis; using System.IO; using System.Numerics; + + using ImageSharp.PixelFormats; + using Xunit; public class Beziers : FileTestBase @@ -19,12 +22,12 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedByBezierLine() { string path = this.CreateOutputDirectory("Drawing", "BezierLine"); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Simple.png")) { - image.BackgroundColor(Color.Blue) - .DrawBeziers(Color.HotPink, 5, + image.BackgroundColor(Rgba32.Blue) + .DrawBeziers(Rgba32.HotPink, 5, new[] { new Vector2(10, 400), new Vector2(30, 10), @@ -34,21 +37,21 @@ namespace ImageSharp.Tests.Drawing .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { //top of curve - Assert.Equal(Color.HotPink, sourcePixels[138, 115]); + Assert.Equal(Rgba32.HotPink, sourcePixels[138, 115]); //start points - Assert.Equal(Color.HotPink, sourcePixels[10, 400]); - Assert.Equal(Color.HotPink, sourcePixels[300, 400]); + Assert.Equal(Rgba32.HotPink, sourcePixels[10, 400]); + Assert.Equal(Rgba32.HotPink, sourcePixels[300, 400]); //curve points should not be never be set - Assert.Equal(Color.Blue, sourcePixels[30, 10]); - Assert.Equal(Color.Blue, sourcePixels[240, 30]); + Assert.Equal(Rgba32.Blue, sourcePixels[30, 10]); + Assert.Equal(Rgba32.Blue, sourcePixels[240, 30]); // inside shape should be empty - Assert.Equal(Color.Blue, sourcePixels[200, 250]); + Assert.Equal(Rgba32.Blue, sourcePixels[200, 250]); } } } @@ -59,13 +62,13 @@ namespace ImageSharp.Tests.Drawing { string path = this.CreateOutputDirectory("Drawing", "BezierLine"); - Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) { - image.BackgroundColor(Color.Blue) + image.BackgroundColor(Rgba32.Blue) .DrawBeziers(color, 10, new[] { @@ -78,9 +81,9 @@ namespace ImageSharp.Tests.Drawing } //shift background color towards forground color by the opacity amount - Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); + Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { //top of curve Assert.Equal(mergedColor, sourcePixels[138, 115]); @@ -90,11 +93,11 @@ namespace ImageSharp.Tests.Drawing Assert.Equal(mergedColor, sourcePixels[300, 400]); //curve points should not be never be set - Assert.Equal(Color.Blue, sourcePixels[30, 10]); - Assert.Equal(Color.Blue, sourcePixels[240, 30]); + Assert.Equal(Rgba32.Blue, sourcePixels[30, 10]); + Assert.Equal(Rgba32.Blue, sourcePixels[240, 30]); // inside shape should be empty - Assert.Equal(Color.Blue, sourcePixels[200, 250]); + Assert.Equal(Rgba32.Blue, sourcePixels[200, 250]); } } } diff --git a/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs b/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs new file mode 100644 index 0000000000..6c742c2e0f --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs @@ -0,0 +1,56 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Drawing +{ + using System; + using System.Linq; + using System.Collections.Generic; + using System.Text; + using ImageSharp.PixelFormats; + using Xunit; + + public class BlendedShapes + { + public static IEnumerable modes = ((PixelBlenderMode[])Enum.GetValues(typeof(PixelBlenderMode))) + .Select(x=> new object[] { x }); + + [Theory] + [WithBlankImages(nameof(modes), 100, 100, PixelTypes.StandardImageClass)] + public void DrawBlendedValues(TestImageProvider provider, PixelBlenderMode mode) + where TPixel : struct, IPixel + { + using (var img = provider.GetImage()) + { + img.Fill(NamedColors.DarkBlue, new Rectangle(0, 40, 100, 20)); + img.Fill(NamedColors.HotPink, new Rectangle(40, 0, 20, 100), new ImageSharp.GraphicsOptions(true) + { + BlenderMode = mode + }); + img.DebugSave(provider, new { mode }); + } + } + + [Theory] + [WithBlankImages(nameof(modes), 100, 100, PixelTypes.StandardImageClass)] + public void DrawBlendedValues_transparent(TestImageProvider provider, PixelBlenderMode mode) + where TPixel : struct, IPixel + { + using (var img = provider.GetImage()) + { + img.Fill(NamedColors.DarkBlue, new Rectangle(0, 40, 100, 20)); + img.Fill(NamedColors.HotPink, new Rectangle(20, 0, 40, 100), new ImageSharp.GraphicsOptions(true) + { + BlenderMode = mode + }); + img.Fill(NamedColors.Transparent, new Rectangle(40, 0, 20, 100), new ImageSharp.GraphicsOptions(true) + { + BlenderMode = mode + }); + img.DebugSave(provider, new { mode }); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs index 3a59de624c..030034a8f2 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs @@ -6,30 +6,42 @@ namespace ImageSharp.Tests { using System.IO; - + using System.Linq; + using ImageSharp.PixelFormats; using Xunit; public class DrawImageTest : FileTestBase { - [Fact] - public void ImageShouldApplyDrawImageFilter() - { - string path = this.CreateOutputDirectory("Drawing", "DrawImage"); + private const PixelTypes PixelTypes = Tests.PixelTypes.StandardImageClass; + + public static readonly string[] TestFiles = { + TestImages.Jpeg.Baseline.Calliphora, + TestImages.Bmp.Car, + TestImages.Png.Splash, + TestImages.Gif.Rings + }; - using (Image blend = TestFile.Create(TestImages.Bmp.Car).CreateImage()) + object[][] Modes = System.Enum.GetValues(typeof(PixelBlenderMode)).Cast().Select(x => new object[] { x }).ToArray(); + + [Theory] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Normal)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Multiply)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Add)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Substract)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Screen)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Darken)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Lighten)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.Overlay)] + [WithFileCollection(nameof(TestFiles), PixelTypes, PixelBlenderMode.HardLight)] + public void ImageShouldApplyDrawImage(TestImageProvider provider, PixelBlenderMode mode) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + using (Image blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes)) { - foreach (TestFile file in Files) - { - using (Image image = file.CreateImage()) - { - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) - { - image.DrawImage(blend, 75, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4)) - .Save(output); - } - } - } + image.DrawImage(blend, mode, .75f, new Size(image.Width / 2, image.Height / 2), new Point(image.Width / 4, image.Height / 4)) + .DebugSave(provider, new { mode }); } } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs index fc231a89d5..7d54879c36 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs @@ -5,15 +5,13 @@ namespace ImageSharp.Tests.Drawing { - using Drawing; - using ImageSharp.Drawing; using ShapePath = SixLabors.Shapes.Path; using SixLabors.Shapes; - using System; - using System.Diagnostics.CodeAnalysis; using System.IO; using System.Numerics; + using ImageSharp.PixelFormats; + using Xunit; public class DrawPathTests : FileTestBase @@ -22,7 +20,7 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedByPath() { string path = this.CreateOutputDirectory("Drawing", "Path"); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { LinearLineSegment linerSegemnt = new LinearLineSegment( new Vector2(10, 10), @@ -38,18 +36,18 @@ namespace ImageSharp.Tests.Drawing using (FileStream output = File.OpenWrite($"{path}/Simple.png")) { image - .BackgroundColor(Color.Blue) - .Draw(Color.HotPink, 5, p) + .BackgroundColor(Rgba32.Blue) + .Draw(Rgba32.HotPink, 5, p) .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[9, 9]); + Assert.Equal(Rgba32.HotPink, sourcePixels[9, 9]); - Assert.Equal(Color.HotPink, sourcePixels[199, 149]); + Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]); - Assert.Equal(Color.Blue, sourcePixels[50, 50]); + Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); } } } @@ -60,7 +58,7 @@ namespace ImageSharp.Tests.Drawing { string path = this.CreateOutputDirectory("Drawing", "Path"); - Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); LinearLineSegment linerSegemnt = new LinearLineSegment( @@ -76,26 +74,26 @@ namespace ImageSharp.Tests.Drawing ShapePath p = new ShapePath(linerSegemnt, bazierSegment); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) { image - .BackgroundColor(Color.Blue) + .BackgroundColor(Rgba32.Blue) .Draw(color, 10, p) .Save(output); } //shift background color towards forground color by the opacity amount - Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); + Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { Assert.Equal(mergedColor, sourcePixels[9, 9]); Assert.Equal(mergedColor, sourcePixels[199, 149]); - Assert.Equal(Color.Blue, sourcePixels[50, 50]); + Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); } } } diff --git a/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs b/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs index 8162bc5319..7bacebe42e 100644 --- a/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs @@ -10,15 +10,16 @@ namespace ImageSharp.Tests.Drawing using ImageSharp.Drawing; using ImageSharp.Drawing.Brushes; - + using ImageSharp.Memory; + using ImageSharp.PixelFormats; using Xunit; public class FillPatternBrushTests : FileTestBase { - private void Test(string name, Color background, IBrush brush, Color[,] expectedPattern) + private void Test(string name, Rgba32 background, IBrush brush, Rgba32[,] expectedPattern) { string path = this.CreateOutputDirectory("Fill", "PatternBrush"); - using (Image image = new Image(20, 20)) + using (Image image = new Image(20, 20)) { image .Fill(background) @@ -29,11 +30,11 @@ namespace ImageSharp.Tests.Drawing image.Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { // lets pick random spots to start checking Random r = new Random(); - Fast2DArray expectedPatternFast = new Fast2DArray(expectedPattern); + Fast2DArray expectedPatternFast = new Fast2DArray(expectedPattern); int xStride = expectedPatternFast.Width; int yStride = expectedPatternFast.Height; int offsetX = r.Next(image.Width / xStride) * xStride; @@ -44,8 +45,8 @@ namespace ImageSharp.Tests.Drawing { int actualX = x + offsetX; int actualY = y + offsetY; - Color expected = expectedPatternFast[y, x]; // inverted pattern - Color actual = sourcePixels[actualX, actualY]; + Rgba32 expected = expectedPatternFast[y, x]; // inverted pattern + Rgba32 actual = sourcePixels[actualX, actualY]; if (expected != actual) { Assert.True(false, $"Expected {expected} but found {actual} at ({actualX},{actualY})"); @@ -63,73 +64,73 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeFloodFilledWithPercent10() { - this.Test("Percent10", Color.Blue, Brushes.Percent10(Color.HotPink, Color.LimeGreen), + this.Test("Percent10", Rgba32.Blue, Brushes.Percent10(Rgba32.HotPink, Rgba32.LimeGreen), new[,] { - { Color.HotPink , Color.LimeGreen, Color.LimeGreen, Color.LimeGreen}, - { Color.LimeGreen, Color.LimeGreen, Color.LimeGreen, Color.LimeGreen}, - { Color.LimeGreen, Color.LimeGreen, Color.HotPink , Color.LimeGreen}, - { Color.LimeGreen, Color.LimeGreen, Color.LimeGreen, Color.LimeGreen} + { Rgba32.HotPink , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink , Rgba32.LimeGreen}, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen} }); } [Fact] public void ImageShouldBeFloodFilledWithPercent10Transparent() { - Test("Percent10_Transparent", Color.Blue, Brushes.Percent10(Color.HotPink), - new Color[,] { - { Color.HotPink , Color.Blue, Color.Blue, Color.Blue}, - { Color.Blue, Color.Blue, Color.Blue, Color.Blue}, - { Color.Blue, Color.Blue, Color.HotPink , Color.Blue}, - { Color.Blue, Color.Blue, Color.Blue, Color.Blue} + Test("Percent10_Transparent", Rgba32.Blue, Brushes.Percent10(Rgba32.HotPink), + new Rgba32[,] { + { Rgba32.HotPink , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, + { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, + { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink , Rgba32.Blue}, + { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue} }); } [Fact] public void ImageShouldBeFloodFilledWithPercent20() { - Test("Percent20", Color.Blue, Brushes.Percent20(Color.HotPink, Color.LimeGreen), - new Color[,] { - { Color.HotPink , Color.LimeGreen, Color.LimeGreen, Color.LimeGreen}, - { Color.LimeGreen, Color.LimeGreen, Color.HotPink , Color.LimeGreen}, - { Color.HotPink , Color.LimeGreen, Color.LimeGreen, Color.LimeGreen}, - { Color.LimeGreen, Color.LimeGreen, Color.HotPink , Color.LimeGreen} + Test("Percent20", Rgba32.Blue, Brushes.Percent20(Rgba32.HotPink, Rgba32.LimeGreen), + new Rgba32[,] { + { Rgba32.HotPink , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink , Rgba32.LimeGreen}, + { Rgba32.HotPink , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink , Rgba32.LimeGreen} }); } [Fact] public void ImageShouldBeFloodFilledWithPercent20_transparent() { - Test("Percent20_Transparent", Color.Blue, Brushes.Percent20(Color.HotPink), - new Color[,] { - { Color.HotPink , Color.Blue, Color.Blue, Color.Blue}, - { Color.Blue, Color.Blue, Color.HotPink , Color.Blue}, - { Color.HotPink , Color.Blue, Color.Blue, Color.Blue}, - { Color.Blue, Color.Blue, Color.HotPink , Color.Blue} + Test("Percent20_Transparent", Rgba32.Blue, Brushes.Percent20(Rgba32.HotPink), + new Rgba32[,] { + { Rgba32.HotPink , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, + { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink , Rgba32.Blue}, + { Rgba32.HotPink , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, + { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink , Rgba32.Blue} }); } [Fact] public void ImageShouldBeFloodFilledWithHorizontal() { - Test("Horizontal", Color.Blue, Brushes.Horizontal(Color.HotPink, Color.LimeGreen), - new Color[,] { - { Color.LimeGreen , Color.LimeGreen, Color.LimeGreen, Color.LimeGreen}, - { Color.HotPink, Color.HotPink, Color.HotPink , Color.HotPink}, - { Color.LimeGreen , Color.LimeGreen, Color.LimeGreen, Color.LimeGreen}, - { Color.LimeGreen, Color.LimeGreen, Color.LimeGreen , Color.LimeGreen} + Test("Horizontal", Rgba32.Blue, Brushes.Horizontal(Rgba32.HotPink, Rgba32.LimeGreen), + new Rgba32[,] { + { Rgba32.LimeGreen , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, + { Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink , Rgba32.HotPink}, + { Rgba32.LimeGreen , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen , Rgba32.LimeGreen} }); } [Fact] public void ImageShouldBeFloodFilledWithHorizontal_transparent() { - Test("Horizontal_Transparent", Color.Blue, Brushes.Horizontal(Color.HotPink), - new Color[,] { - { Color.Blue , Color.Blue, Color.Blue, Color.Blue}, - { Color.HotPink, Color.HotPink, Color.HotPink , Color.HotPink}, - { Color.Blue , Color.Blue, Color.Blue, Color.Blue}, - { Color.Blue, Color.Blue, Color.Blue , Color.Blue} + Test("Horizontal_Transparent", Rgba32.Blue, Brushes.Horizontal(Rgba32.HotPink), + new Rgba32[,] { + { Rgba32.Blue , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, + { Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink , Rgba32.HotPink}, + { Rgba32.Blue , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, + { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue , Rgba32.Blue} }); } @@ -138,96 +139,96 @@ namespace ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeFloodFilledWithMin() { - Test("Min", Color.Blue, Brushes.Min(Color.HotPink, Color.LimeGreen), - new Color[,] { - { Color.LimeGreen , Color.LimeGreen, Color.LimeGreen, Color.LimeGreen}, - { Color.LimeGreen , Color.LimeGreen, Color.LimeGreen, Color.LimeGreen}, - { Color.LimeGreen, Color.LimeGreen, Color.LimeGreen , Color.LimeGreen}, - { Color.HotPink, Color.HotPink, Color.HotPink , Color.HotPink} + Test("Min", Rgba32.Blue, Brushes.Min(Rgba32.HotPink, Rgba32.LimeGreen), + new Rgba32[,] { + { Rgba32.LimeGreen , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, + { Rgba32.LimeGreen , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen , Rgba32.LimeGreen}, + { Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink , Rgba32.HotPink} }); } [Fact] public void ImageShouldBeFloodFilledWithMin_transparent() { - Test("Min_Transparent", Color.Blue, Brushes.Min(Color.HotPink), - new Color[,] { - { Color.Blue , Color.Blue, Color.Blue, Color.Blue}, - { Color.Blue , Color.Blue, Color.Blue, Color.Blue}, - { Color.Blue, Color.Blue, Color.Blue , Color.Blue}, - { Color.HotPink, Color.HotPink, Color.HotPink , Color.HotPink}, + Test("Min_Transparent", Rgba32.Blue, Brushes.Min(Rgba32.HotPink), + new Rgba32[,] { + { Rgba32.Blue , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, + { Rgba32.Blue , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, + { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue , Rgba32.Blue}, + { Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink , Rgba32.HotPink}, }); } [Fact] public void ImageShouldBeFloodFilledWithVertical() { - Test("Vertical", Color.Blue, Brushes.Vertical(Color.HotPink, Color.LimeGreen), - new Color[,] { - { Color.LimeGreen, Color.HotPink, Color.LimeGreen, Color.LimeGreen}, - { Color.LimeGreen, Color.HotPink, Color.LimeGreen, Color.LimeGreen}, - { Color.LimeGreen, Color.HotPink, Color.LimeGreen, Color.LimeGreen}, - { Color.LimeGreen, Color.HotPink, Color.LimeGreen, Color.LimeGreen} + Test("Vertical", Rgba32.Blue, Brushes.Vertical(Rgba32.HotPink, Rgba32.LimeGreen), + new Rgba32[,] { + { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen}, + { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen}, + { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen}, + { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen} }); } [Fact] public void ImageShouldBeFloodFilledWithVertical_transparent() { - Test("Vertical_Transparent", Color.Blue, Brushes.Vertical(Color.HotPink), - new Color[,] { - { Color.Blue, Color.HotPink, Color.Blue, Color.Blue}, - { Color.Blue, Color.HotPink, Color.Blue, Color.Blue}, - { Color.Blue, Color.HotPink, Color.Blue, Color.Blue}, - { Color.Blue, Color.HotPink, Color.Blue, Color.Blue} + Test("Vertical_Transparent", Rgba32.Blue, Brushes.Vertical(Rgba32.HotPink), + new Rgba32[,] { + { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue}, + { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue}, + { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue}, + { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue} }); } [Fact] public void ImageShouldBeFloodFilledWithForwardDiagonal() { - Test("ForwardDiagonal", Color.Blue, Brushes.ForwardDiagonal(Color.HotPink, Color.LimeGreen), - new Color[,] { - { Color.LimeGreen, Color.LimeGreen, Color.LimeGreen, Color.HotPink}, - { Color.LimeGreen, Color.LimeGreen, Color.HotPink, Color.LimeGreen}, - { Color.LimeGreen, Color.HotPink, Color.LimeGreen, Color.LimeGreen}, - { Color.HotPink, Color.LimeGreen, Color.LimeGreen, Color.LimeGreen} + Test("ForwardDiagonal", Rgba32.Blue, Brushes.ForwardDiagonal(Rgba32.HotPink, Rgba32.LimeGreen), + new Rgba32[,] { + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink}, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen}, + { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen}, + { Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen} }); } [Fact] public void ImageShouldBeFloodFilledWithForwardDiagonal_transparent() { - Test("ForwardDiagonal_Transparent", Color.Blue, Brushes.ForwardDiagonal(Color.HotPink), - new Color[,] { - { Color.Blue, Color.Blue, Color.Blue, Color.HotPink}, - { Color.Blue, Color.Blue, Color.HotPink, Color.Blue}, - { Color.Blue, Color.HotPink, Color.Blue, Color.Blue}, - { Color.HotPink, Color.Blue, Color.Blue, Color.Blue} + Test("ForwardDiagonal_Transparent", Rgba32.Blue, Brushes.ForwardDiagonal(Rgba32.HotPink), + new Rgba32[,] { + { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink}, + { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue}, + { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue}, + { Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue} }); } [Fact] public void ImageShouldBeFloodFilledWithBackwardDiagonal() { - Test("BackwardDiagonal", Color.Blue, Brushes.BackwardDiagonal(Color.HotPink, Color.LimeGreen), - new Color[,] { - { Color.HotPink, Color.LimeGreen, Color.LimeGreen, Color.LimeGreen}, - { Color.LimeGreen, Color.HotPink, Color.LimeGreen, Color.LimeGreen}, - { Color.LimeGreen, Color.LimeGreen, Color.HotPink, Color.LimeGreen}, - { Color.LimeGreen, Color.LimeGreen, Color.LimeGreen, Color.HotPink} + Test("BackwardDiagonal", Rgba32.Blue, Brushes.BackwardDiagonal(Rgba32.HotPink, Rgba32.LimeGreen), + new Rgba32[,] { + { Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, + { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen}, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen}, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink} }); } [Fact] public void ImageShouldBeFloodFilledWithBackwardDiagonal_transparent() { - Test("BackwardDiagonal_Transparent", Color.Blue, Brushes.BackwardDiagonal(Color.HotPink), - new Color[,] { - { Color.HotPink, Color.Blue, Color.Blue, Color.Blue}, - { Color.Blue, Color.HotPink, Color.Blue, Color.Blue}, - { Color.Blue, Color.Blue, Color.HotPink, Color.Blue}, - { Color.Blue, Color.Blue, Color.Blue, Color.HotPink} + Test("BackwardDiagonal_Transparent", Rgba32.Blue, Brushes.BackwardDiagonal(Rgba32.HotPink), + new Rgba32[,] { + { Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, + { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue}, + { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue}, + { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink} }); } } diff --git a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs index 03994bc94d..c7b789da0d 100644 --- a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs @@ -1,20 +1,13 @@  namespace ImageSharp.Tests.Drawing { - using System; - using System.IO; using ImageSharp; - using ImageSharp.Drawing.Brushes; - using Processing; - using System.Collections.Generic; using Xunit; using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; using ImageSharp.Drawing.Processors; - using ImageSharp.Drawing.Pens; using Moq; - using System.Collections.Immutable; + + using ImageSharp.PixelFormats; public class FillRegionProcessorTests { @@ -23,21 +16,21 @@ namespace ImageSharp.Tests.Drawing [InlineData(true, 2, 4)] [InlineData(true, 5, 5)] [InlineData(true, 8, 8)] - [InlineData(false, 8, 4)] + [InlineData(false, 8, 4)] [InlineData(false, 16, 4)] // we always do 4 sub=pixels when antialising is off. public void MinimumAntialiasSubpixelDepth(bool antialias, int antialiasSubpixelDepth, int expectedAntialiasSubpixelDepth) { ImageSharp.Rectangle bounds = new ImageSharp.Rectangle(0, 0, 1, 1); - Mock> brush = new Mock>(); + Mock> brush = new Mock>(); Mock region = new Mock(); region.Setup(x => x.Bounds).Returns(bounds); GraphicsOptions options = new GraphicsOptions(antialias) { AntialiasSubpixelDepth = 1 }; - FillRegionProcessor processor = new FillRegionProcessor(brush.Object, region.Object, options); - Image img = new Image(1, 1); + FillRegionProcessor processor = new FillRegionProcessor(brush.Object, region.Object, options); + Image img = new Image(1, 1); processor.Apply(img, bounds); region.Verify(x => x.Scan(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(4)); diff --git a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs index bafc84b69f..dc0b83615d 100644 --- a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs @@ -11,6 +11,9 @@ namespace ImageSharp.Tests.Drawing using System.Diagnostics.CodeAnalysis; using System.IO; using System.Numerics; + + using ImageSharp.PixelFormats; + using Xunit; public class FillSolidBrushTests: FileTestBase @@ -19,20 +22,20 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeFloodFilledWithColorOnDefaultBackground() { string path = this.CreateOutputDirectory("Fill", "SolidBrush"); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/DefaultBack.png")) { image - .Fill(Color.HotPink) + .Fill(Rgba32.HotPink) .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[9, 9]); + Assert.Equal(Rgba32.HotPink, sourcePixels[9, 9]); - Assert.Equal(Color.HotPink, sourcePixels[199, 149]); + Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]); } } } @@ -41,21 +44,21 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeFloodFilledWithColor() { string path = this.CreateOutputDirectory("Fill", "SolidBrush"); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Simple.png")) { image - .BackgroundColor(Color.Blue) - .Fill(Color.HotPink) + .BackgroundColor(Rgba32.Blue) + .Fill(Rgba32.HotPink) .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[9, 9]); + Assert.Equal(Rgba32.HotPink, sourcePixels[9, 9]); - Assert.Equal(Color.HotPink, sourcePixels[199, 149]); + Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]); } } } @@ -64,22 +67,22 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeFloodFilledWithColorOpacity() { string path = this.CreateOutputDirectory("Fill", "SolidBrush"); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { - Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) { image - .BackgroundColor(Color.Blue) + .BackgroundColor(Rgba32.Blue) .Fill(color) .Save(output); } //shift background color towards forground color by the opacity amount - Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); + Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { Assert.Equal(mergedColor, sourcePixels[9, 9]); Assert.Equal(mergedColor, sourcePixels[199, 149]); diff --git a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs index d7a4bde957..1f35a37884 100644 --- a/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/LineComplexPolygonTests.cs @@ -11,6 +11,7 @@ namespace ImageSharp.Tests.Drawing using ImageSharp.Drawing; using System.Numerics; using ImageSharp.Drawing.Pens; + using ImageSharp.PixelFormats; using SixLabors.Shapes; @@ -31,38 +32,38 @@ namespace ImageSharp.Tests.Drawing new Vector2(93, 85), new Vector2(65, 137))); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Simple.png")) { image - .BackgroundColor(Color.Blue) - .Draw(Color.HotPink, 5, simplePath.Clip(hole1)) + .BackgroundColor(Rgba32.Blue) + .Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)) .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[10, 10]); + Assert.Equal(Rgba32.HotPink, sourcePixels[10, 10]); - Assert.Equal(Color.HotPink, sourcePixels[200, 150]); + Assert.Equal(Rgba32.HotPink, sourcePixels[200, 150]); - Assert.Equal(Color.HotPink, sourcePixels[50, 300]); + Assert.Equal(Rgba32.HotPink, sourcePixels[50, 300]); - Assert.Equal(Color.HotPink, sourcePixels[37, 85]); + Assert.Equal(Rgba32.HotPink, sourcePixels[37, 85]); - Assert.Equal(Color.HotPink, sourcePixels[93, 85]); + Assert.Equal(Rgba32.HotPink, sourcePixels[93, 85]); - Assert.Equal(Color.HotPink, sourcePixels[65, 137]); + Assert.Equal(Rgba32.HotPink, sourcePixels[65, 137]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); //inside hole - Assert.Equal(Color.Blue, sourcePixels[57, 99]); + Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]); //inside shape - Assert.Equal(Color.Blue, sourcePixels[100, 192]); + Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]); } } } @@ -81,23 +82,23 @@ namespace ImageSharp.Tests.Drawing new Vector2(263, 25), new Vector2(235, 57))); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/SimpleVanishHole.png")) { image - .BackgroundColor(Color.Blue) - .Draw(Color.HotPink, 5, simplePath.Clip(hole1)) + .BackgroundColor(Rgba32.Blue) + .Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)) .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[10, 10]); + Assert.Equal(Rgba32.HotPink, sourcePixels[10, 10]); - Assert.Equal(Color.HotPink, sourcePixels[200, 150]); + Assert.Equal(Rgba32.HotPink, sourcePixels[200, 150]); - Assert.Equal(Color.HotPink, sourcePixels[50, 300]); + Assert.Equal(Rgba32.HotPink, sourcePixels[50, 300]); //Assert.Equal(Color.HotPink, sourcePixels[37, 85]); @@ -106,13 +107,13 @@ namespace ImageSharp.Tests.Drawing //Assert.Equal(Color.HotPink, sourcePixels[65, 137]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); //inside hole - Assert.Equal(Color.Blue, sourcePixels[57, 99]); + Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]); //inside shape - Assert.Equal(Color.Blue, sourcePixels[100, 192]); + Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]); } } } @@ -132,33 +133,33 @@ namespace ImageSharp.Tests.Drawing new Vector2(130, 40), new Vector2(65, 137))); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/SimpleOverlapping.png")) { image - .BackgroundColor(Color.Blue) - .Draw(Color.HotPink, 5, simplePath.Clip(hole1)) + .BackgroundColor(Rgba32.Blue) + .Draw(Rgba32.HotPink, 5, simplePath.Clip(hole1)) .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[10, 10]); + Assert.Equal(Rgba32.HotPink, sourcePixels[10, 10]); - Assert.Equal(Color.HotPink, sourcePixels[200, 150]); + Assert.Equal(Rgba32.HotPink, sourcePixels[200, 150]); - Assert.Equal(Color.HotPink, sourcePixels[50, 300]); + Assert.Equal(Rgba32.HotPink, sourcePixels[50, 300]); - Assert.Equal(Color.Blue, sourcePixels[130, 41]); + Assert.Equal(Rgba32.Blue, sourcePixels[130, 41]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); //inside hole - Assert.Equal(Color.Blue, sourcePixels[57, 99]); + Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]); //inside shape - Assert.Equal(Color.Blue, sourcePixels[100, 192]); + Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]); } } } @@ -178,13 +179,13 @@ namespace ImageSharp.Tests.Drawing new Vector2(93, 85), new Vector2(65, 137))); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Dashed.png")) { image - .BackgroundColor(Color.Blue) - .Draw(Pens.Dash(Color.HotPink, 5), simplePath.Clip(hole1)) + .BackgroundColor(Rgba32.Blue) + .Draw(Pens.Dash(Rgba32.HotPink, 5), simplePath.Clip(hole1)) .Save(output); } } @@ -204,22 +205,22 @@ namespace ImageSharp.Tests.Drawing new Vector2(37, 85), new Vector2(93, 85), new Vector2(65, 137))); - Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) { image - .BackgroundColor(Color.Blue) + .BackgroundColor(Rgba32.Blue) .Draw(color, 5, simplePath.Clip(hole1)) .Save(output); } //shift background color towards forground color by the opacity amount - Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); + Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { Assert.Equal(mergedColor, sourcePixels[10, 10]); @@ -234,14 +235,14 @@ namespace ImageSharp.Tests.Drawing Assert.Equal(mergedColor, sourcePixels[65, 137]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); //inside hole - Assert.Equal(Color.Blue, sourcePixels[57, 99]); + Assert.Equal(Rgba32.Blue, sourcePixels[57, 99]); //inside shape - Assert.Equal(Color.Blue, sourcePixels[100, 192]); + Assert.Equal(Rgba32.Blue, sourcePixels[100, 192]); } } } diff --git a/tests/ImageSharp.Tests/Drawing/LineTests.cs b/tests/ImageSharp.Tests/Drawing/LineTests.cs index 81efd933ba..3396d89c57 100644 --- a/tests/ImageSharp.Tests/Drawing/LineTests.cs +++ b/tests/ImageSharp.Tests/Drawing/LineTests.cs @@ -10,6 +10,9 @@ namespace ImageSharp.Tests.Drawing using System.IO; using System.Numerics; + + using ImageSharp.PixelFormats; + using Xunit; public class LineTests : FileTestBase @@ -18,13 +21,13 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedByPath() { string path = this.CreateOutputDirectory("Drawing", "Lines"); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Simple.png")) { image - .BackgroundColor(Color.Blue) - .DrawLines(Color.HotPink, 5, + .BackgroundColor(Rgba32.Blue) + .DrawLines(Rgba32.HotPink, 5, new[] { new Vector2(10, 10), new Vector2(200, 150), @@ -33,13 +36,13 @@ namespace ImageSharp.Tests.Drawing .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[9, 9]); + Assert.Equal(Rgba32.HotPink, sourcePixels[9, 9]); - Assert.Equal(Color.HotPink, sourcePixels[199, 149]); + Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]); - Assert.Equal(Color.Blue, sourcePixels[50, 50]); + Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); } } } @@ -48,13 +51,13 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedByPath_NoAntialias() { string path = this.CreateOutputDirectory("Drawing", "Lines"); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Simple_noantialias.png")) { image - .BackgroundColor(Color.Blue) - .DrawLines(Color.HotPink, 5, + .BackgroundColor(Rgba32.Blue) + .DrawLines(Rgba32.HotPink, 5, new[] { new Vector2(10, 10), new Vector2(200, 150), @@ -64,13 +67,13 @@ namespace ImageSharp.Tests.Drawing .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[9, 9]); + Assert.Equal(Rgba32.HotPink, sourcePixels[9, 9]); - Assert.Equal(Color.HotPink, sourcePixels[199, 149]); + Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]); - Assert.Equal(Color.Blue, sourcePixels[50, 50]); + Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); } } } @@ -79,13 +82,13 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedByPathDashed() { string path = this.CreateOutputDirectory("Drawing", "Lines"); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Dashed.png")) { image - .BackgroundColor(Color.Blue) - .DrawLines(Pens.Dash(Color.HotPink, 5), + .BackgroundColor(Rgba32.Blue) + .DrawLines(Pens.Dash(Rgba32.HotPink, 5), new[] { new Vector2(10, 10), new Vector2(200, 150), @@ -100,13 +103,13 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedByPathDotted() { string path = this.CreateOutputDirectory("Drawing", "Lines"); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Dot.png")) { image - .BackgroundColor(Color.Blue) - .DrawLines(Pens.Dot(Color.HotPink, 5), + .BackgroundColor(Rgba32.Blue) + .DrawLines(Pens.Dot(Rgba32.HotPink, 5), new[] { new Vector2(10, 10), new Vector2(200, 150), @@ -121,13 +124,13 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedByPathDashDot() { string path = this.CreateOutputDirectory("Drawing", "Lines"); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/DashDot.png")) { image - .BackgroundColor(Color.Blue) - .DrawLines(Pens.DashDot(Color.HotPink, 5), + .BackgroundColor(Rgba32.Blue) + .DrawLines(Pens.DashDot(Rgba32.HotPink, 5), new[] { new Vector2(10, 10), new Vector2(200, 150), @@ -142,13 +145,13 @@ namespace ImageSharp.Tests.Drawing public void ImageShouldBeOverlayedByPathDashDotDot() { string path = this.CreateOutputDirectory("Drawing", "Lines"); - Image image = new Image(500, 500); + Image image = new Image(500, 500); using (FileStream output = File.OpenWrite($"{path}/DashDotDot.png")) { image - .BackgroundColor(Color.Blue) - .DrawLines(Pens.DashDotDot(Color.HotPink, 5), new[] { + .BackgroundColor(Rgba32.Blue) + .DrawLines(Pens.DashDotDot(Rgba32.HotPink, 5), new[] { new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300) @@ -162,15 +165,15 @@ namespace ImageSharp.Tests.Drawing { string path = this.CreateOutputDirectory("Drawing", "Lines"); - Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - Image image = new Image(500, 500); + Image image = new Image(500, 500); using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) { image - .BackgroundColor(Color.Blue) + .BackgroundColor(Rgba32.Blue) .DrawLines(color, 10, new[] { new Vector2(10, 10), new Vector2(200, 150), @@ -180,15 +183,15 @@ namespace ImageSharp.Tests.Drawing } //shift background color towards forground color by the opacity amount - Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f/255f)); + Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f/255f)); - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { Assert.Equal(mergedColor, sourcePixels[9, 9]); Assert.Equal(mergedColor, sourcePixels[199, 149]); - Assert.Equal(Color.Blue, sourcePixels[50, 50]); + Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); } } @@ -197,13 +200,13 @@ namespace ImageSharp.Tests.Drawing { string path = this.CreateOutputDirectory("Drawing", "Lines"); - Image image = new Image(500, 500); + Image image = new Image(500, 500); using (FileStream output = File.OpenWrite($"{path}/Rectangle.png")) { image - .BackgroundColor(Color.Blue) - .DrawLines(Color.HotPink, 10, new[] { + .BackgroundColor(Rgba32.Blue) + .DrawLines(Rgba32.HotPink, 10, new[] { new Vector2(10, 10), new Vector2(200, 10), new Vector2(200, 150), @@ -212,15 +215,15 @@ namespace ImageSharp.Tests.Drawing .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[8, 8]); + Assert.Equal(Rgba32.HotPink, sourcePixels[8, 8]); - Assert.Equal(Color.HotPink, sourcePixels[198, 10]); + Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]); - Assert.Equal(Color.Blue, sourcePixels[10, 50]); + Assert.Equal(Rgba32.Blue, sourcePixels[10, 50]); - Assert.Equal(Color.Blue, sourcePixels[50, 50]); + Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); } } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs index 82e2f72a2f..a9b2284e8a 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs @@ -2,25 +2,24 @@ namespace ImageSharp.Tests.Drawing.Paths { using System; - using System.IO; - using ImageSharp; + using ImageSharp.Drawing.Brushes; - using Processing; - using System.Collections.Generic; + using Xunit; using ImageSharp.Drawing; using System.Numerics; using SixLabors.Shapes; using ImageSharp.Drawing.Processors; using ImageSharp.Drawing.Pens; + using ImageSharp.PixelFormats; public class DrawBeziersTests : IDisposable { float thickness = 7.2f; GraphicsOptions noneDefault = new GraphicsOptions(); - Color color = Color.HotPink; - SolidBrush brush = Brushes.Solid(Color.HotPink); - Pen pen = new Pen(Color.Firebrick, 99.9f); + Rgba32 color = Rgba32.HotPink; + SolidBrush brush = Brushes.Solid(Rgba32.HotPink); + Pen pen = new Pen(Rgba32.Firebrick, 99.9f); Vector2[] points = new Vector2[] { new Vector2(10,10), new Vector2(20,10), @@ -45,7 +44,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawBeziers(brush, thickness, points); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -56,7 +55,7 @@ namespace ImageSharp.Tests.Drawing.Paths BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } @@ -67,7 +66,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawBeziers(brush, thickness, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); @@ -76,7 +75,7 @@ namespace ImageSharp.Tests.Drawing.Paths SixLabors.Shapes.Path vector = Assert.IsType(path.Path); BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } @@ -87,7 +86,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawBeziers(color, thickness, points); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -96,10 +95,10 @@ namespace ImageSharp.Tests.Drawing.Paths SixLabors.Shapes.Path vector = Assert.IsType(path.Path); BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - SolidBrush brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } @@ -109,7 +108,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawBeziers(color, thickness, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); @@ -118,10 +117,10 @@ namespace ImageSharp.Tests.Drawing.Paths SixLabors.Shapes.Path vector = Assert.IsType(path.Path); BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - SolidBrush brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } @@ -131,7 +130,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawBeziers(pen, points); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -149,7 +148,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawBeziers(pen, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs index cc126614f6..3b7ba303dc 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs @@ -2,25 +2,23 @@ namespace ImageSharp.Tests.Drawing.Paths { using System; - using System.IO; - using ImageSharp; + using ImageSharp.Drawing.Brushes; - using Processing; - using System.Collections.Generic; using Xunit; using ImageSharp.Drawing; using System.Numerics; using SixLabors.Shapes; using ImageSharp.Drawing.Processors; using ImageSharp.Drawing.Pens; + using ImageSharp.PixelFormats; public class DrawLinesTests : IDisposable { float thickness = 7.2f; GraphicsOptions noneDefault = new GraphicsOptions(); - Color color = Color.HotPink; - SolidBrush brush = Brushes.Solid(Color.HotPink); - Pen pen = new Pen(Color.Gray, 99.9f); + Rgba32 color = Rgba32.HotPink; + SolidBrush brush = Brushes.Solid(Rgba32.HotPink); + Pen pen = new Pen(Rgba32.Gray, 99.9f); Vector2[] points = new Vector2[] { new Vector2(10,10), new Vector2(20,10), @@ -45,7 +43,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawLines(brush, thickness, points); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -54,7 +52,7 @@ namespace ImageSharp.Tests.Drawing.Paths SixLabors.Shapes.Path vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } @@ -65,7 +63,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawLines(brush, thickness, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); @@ -74,7 +72,7 @@ namespace ImageSharp.Tests.Drawing.Paths SixLabors.Shapes.Path vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } @@ -85,7 +83,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawLines(color, thickness, points); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -94,10 +92,10 @@ namespace ImageSharp.Tests.Drawing.Paths SixLabors.Shapes.Path vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - SolidBrush brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } @@ -107,7 +105,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawLines(color, thickness, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); @@ -116,10 +114,10 @@ namespace ImageSharp.Tests.Drawing.Paths SixLabors.Shapes.Path vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - SolidBrush brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } @@ -129,7 +127,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawLines(pen, points); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -147,7 +145,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawLines(pen, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs index 6c1c068135..0bdcbdc082 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs @@ -2,25 +2,24 @@ namespace ImageSharp.Tests.Drawing.Paths { using System; - using System.IO; - using ImageSharp; + using ImageSharp.Drawing.Brushes; - using Processing; - using System.Collections.Generic; + using Xunit; using ImageSharp.Drawing; using System.Numerics; using SixLabors.Shapes; using ImageSharp.Drawing.Processors; using ImageSharp.Drawing.Pens; + using ImageSharp.PixelFormats; public class DrawPath : IDisposable { float thickness = 7.2f; GraphicsOptions noneDefault = new GraphicsOptions(); - Color color = Color.HotPink; - SolidBrush brush = Brushes.Solid(Color.HotPink); - Pen pen = new Pen(Color.Gray, 99.9f); + Rgba32 color = Rgba32.HotPink; + SolidBrush brush = Brushes.Solid(Rgba32.HotPink); + Pen pen = new Pen(Rgba32.Gray, 99.9f); IPath path = new SixLabors.Shapes.Path(new LinearLineSegment(new Vector2[] { new Vector2(10,10), new Vector2(20,10), @@ -45,14 +44,14 @@ namespace ImageSharp.Tests.Drawing.Paths img.Draw(brush, thickness, path); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath shapepath = Assert.IsType(processor.Path); Assert.Equal(path, shapepath.Path); - - Pen pen = Assert.IsType>(processor.Pen); + + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } @@ -63,14 +62,14 @@ namespace ImageSharp.Tests.Drawing.Paths img.Draw(brush, thickness, path, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); ShapePath shapepath = Assert.IsType(processor.Path); Assert.Equal(path, shapepath.Path); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } @@ -81,17 +80,17 @@ namespace ImageSharp.Tests.Drawing.Paths img.Draw(color, thickness, path); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); ShapePath shapepath = Assert.IsType(processor.Path); Assert.Equal(path, shapepath.Path); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - SolidBrush brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } @@ -101,17 +100,17 @@ namespace ImageSharp.Tests.Drawing.Paths img.Draw(color, thickness, path, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); ShapePath shapepath = Assert.IsType(processor.Path); Assert.Equal(path, shapepath.Path); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - SolidBrush brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } @@ -121,7 +120,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.Draw(pen, path); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -137,7 +136,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.Draw(pen, path, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs index 9de0523313..3474e6f62f 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs @@ -2,25 +2,24 @@ namespace ImageSharp.Tests.Drawing.Paths { using System; - using System.IO; - using ImageSharp; + using ImageSharp.Drawing.Brushes; - using Processing; - using System.Collections.Generic; + using Xunit; using ImageSharp.Drawing; using System.Numerics; using SixLabors.Shapes; using ImageSharp.Drawing.Processors; using ImageSharp.Drawing.Pens; + using ImageSharp.PixelFormats; public class DrawPolygon : IDisposable { float thickness = 7.2f; GraphicsOptions noneDefault = new GraphicsOptions(); - Color color = Color.HotPink; - SolidBrush brush = Brushes.Solid(Color.HotPink); - Pen pen = new Pen(Color.Gray, 99.9f); + Rgba32 color = Rgba32.HotPink; + SolidBrush brush = Brushes.Solid(Rgba32.HotPink); + Pen pen = new Pen(Rgba32.Gray, 99.9f); Vector2[] points = new Vector2[] { new Vector2(10,10), new Vector2(20,10), @@ -45,7 +44,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawPolygon(brush, thickness, points); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -54,7 +53,7 @@ namespace ImageSharp.Tests.Drawing.Paths Polygon vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } @@ -65,7 +64,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawPolygon(brush, thickness, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); @@ -74,7 +73,7 @@ namespace ImageSharp.Tests.Drawing.Paths Polygon vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } @@ -85,7 +84,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawPolygon(color, thickness, points); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -94,10 +93,10 @@ namespace ImageSharp.Tests.Drawing.Paths Polygon vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - SolidBrush brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } @@ -107,7 +106,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawPolygon(color, thickness, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); @@ -116,10 +115,10 @@ namespace ImageSharp.Tests.Drawing.Paths Polygon vector = Assert.IsType(path.Path); LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - SolidBrush brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } @@ -129,7 +128,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawPolygon(pen, points); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -147,7 +146,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.DrawPolygon(pen, points, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs index 215d5a7c70..e08e702c1d 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs @@ -2,25 +2,21 @@ namespace ImageSharp.Tests.Drawing.Paths { using System; - using System.IO; using ImageSharp; using ImageSharp.Drawing.Brushes; - using Processing; - using System.Collections.Generic; using Xunit; using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; using ImageSharp.Drawing.Processors; using ImageSharp.Drawing.Pens; + using ImageSharp.PixelFormats; public class DrawRectangle : IDisposable { float thickness = 7.2f; GraphicsOptions noneDefault = new GraphicsOptions(); - Color color = Color.HotPink; - SolidBrush brush = Brushes.Solid(Color.HotPink); - Pen pen = new Pen(Color.Gray, 99.9f); + Rgba32 color = Rgba32.HotPink; + SolidBrush brush = Brushes.Solid(Rgba32.HotPink); + Pen pen = new Pen(Rgba32.Gray, 99.9f); ImageSharp.Rectangle rectangle = new ImageSharp.Rectangle(10, 10, 98, 324); private ProcessorWatchingImage img; @@ -41,7 +37,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.Draw(brush, thickness, rectangle); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -53,7 +49,7 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(rect.Size.Width, rectangle.Width); Assert.Equal(rect.Size.Height, rectangle.Height); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } @@ -64,7 +60,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.Draw(brush, thickness, rectangle, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); @@ -77,7 +73,7 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(rect.Size.Width, rectangle.Width); Assert.Equal(rect.Size.Height, rectangle.Height); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(brush, pen.Brush); Assert.Equal(thickness, pen.Width); } @@ -88,7 +84,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.Draw(color, thickness, rectangle); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -101,10 +97,10 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(rect.Size.Width, rectangle.Width); Assert.Equal(rect.Size.Height, rectangle.Height); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - SolidBrush brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } @@ -114,7 +110,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.Draw(color, thickness, rectangle, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); @@ -127,10 +123,10 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(rect.Size.Width, rectangle.Width); Assert.Equal(rect.Size.Height, rectangle.Height); - Pen pen = Assert.IsType>(processor.Pen); + Pen pen = Assert.IsType>(processor.Pen); Assert.Equal(thickness, pen.Width); - SolidBrush brush = Assert.IsType>(pen.Brush); + SolidBrush brush = Assert.IsType>(pen.Brush); Assert.Equal(color, brush.Color); } @@ -140,7 +136,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.Draw(pen, rectangle); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -162,7 +158,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.Draw(pen, rectangle, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs index 5ba6580bd7..eb0127cb10 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPath.cs @@ -2,23 +2,20 @@ namespace ImageSharp.Tests.Drawing.Paths { using System; - using System.IO; using ImageSharp; using ImageSharp.Drawing.Brushes; - using Processing; - using System.Collections.Generic; using Xunit; using ImageSharp.Drawing; using System.Numerics; using SixLabors.Shapes; using ImageSharp.Drawing.Processors; - using ImageSharp.Drawing.Pens; + using ImageSharp.PixelFormats; public class FillPath : IDisposable { GraphicsOptions noneDefault = new GraphicsOptions(); - Color color = Color.HotPink; - SolidBrush brush = Brushes.Solid(Color.HotPink); + Rgba32 color = Rgba32.HotPink; + SolidBrush brush = Brushes.Solid(Rgba32.HotPink); IPath path = new SixLabors.Shapes.Path(new LinearLineSegment(new Vector2[] { new Vector2(10,10), new Vector2(20,10), @@ -43,12 +40,12 @@ namespace ImageSharp.Tests.Drawing.Paths img.Fill(brush, path); Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); ShapeRegion region = Assert.IsType(processor.Region); - + // path is converted to a polygon before filling Polygon polygon = Assert.IsType(region.Shape); LinearLineSegment segments = Assert.IsType(polygon.LineSegments[0]); @@ -62,7 +59,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.Fill(brush, path, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); @@ -79,7 +76,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.Fill(color, path); Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -87,7 +84,7 @@ namespace ImageSharp.Tests.Drawing.Paths Polygon polygon = Assert.IsType(region.Shape); LinearLineSegment segments = Assert.IsType(polygon.LineSegments[0]); - SolidBrush brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(color, brush.Color); } @@ -97,7 +94,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.Fill(color, path, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); @@ -105,7 +102,7 @@ namespace ImageSharp.Tests.Drawing.Paths Polygon polygon = Assert.IsType(region.Shape); LinearLineSegment segments = Assert.IsType(polygon.LineSegments[0]); - SolidBrush brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(color, brush.Color); } } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs index ad72d4c4ee..3f912fe79f 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPolygon.cs @@ -2,23 +2,20 @@ namespace ImageSharp.Tests.Drawing.Paths { using System; - using System.IO; using ImageSharp; using ImageSharp.Drawing.Brushes; - using Processing; - using System.Collections.Generic; using Xunit; using ImageSharp.Drawing; using System.Numerics; using SixLabors.Shapes; using ImageSharp.Drawing.Processors; - using ImageSharp.Drawing.Pens; + using ImageSharp.PixelFormats; public class FillPolygon : IDisposable { GraphicsOptions noneDefault = new GraphicsOptions(); - Color color = Color.HotPink; - SolidBrush brush = Brushes.Solid(Color.HotPink); + Rgba32 color = Rgba32.HotPink; + SolidBrush brush = Brushes.Solid(Rgba32.HotPink); Vector2[] path = new Vector2[] { new Vector2(10,10), new Vector2(20,10), @@ -43,14 +40,14 @@ namespace ImageSharp.Tests.Drawing.Paths img.FillPolygon(brush, path); Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); ShapeRegion region = Assert.IsType(processor.Region); Polygon polygon = Assert.IsType(region.Shape); LinearLineSegment segemnt = Assert.IsType(polygon.LineSegments[0]); - + Assert.Equal(brush, processor.Brush); } @@ -60,7 +57,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.FillPolygon(brush, path, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); @@ -75,9 +72,9 @@ namespace ImageSharp.Tests.Drawing.Paths public void CorrectlySetsColorAndPath() { img.FillPolygon(color, path); - + Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -85,7 +82,7 @@ namespace ImageSharp.Tests.Drawing.Paths Polygon polygon = Assert.IsType(region.Shape); LinearLineSegment segemnt = Assert.IsType(polygon.LineSegments[0]); - SolidBrush brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(color, brush.Color); } @@ -95,7 +92,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.FillPolygon(color, path, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); @@ -103,7 +100,7 @@ namespace ImageSharp.Tests.Drawing.Paths Polygon polygon = Assert.IsType(region.Shape); LinearLineSegment segemnt = Assert.IsType(polygon.LineSegments[0]); - SolidBrush brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(color, brush.Color); } } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs index f6b1c4adef..1f4774550e 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillRectangle.cs @@ -2,23 +2,19 @@ namespace ImageSharp.Tests.Drawing.Paths { using System; - using System.IO; - using ImageSharp; + using ImageSharp.Drawing.Brushes; - using Processing; - using System.Collections.Generic; + using Xunit; using ImageSharp.Drawing; - using System.Numerics; - using SixLabors.Shapes; using ImageSharp.Drawing.Processors; - using ImageSharp.Drawing.Pens; + using ImageSharp.PixelFormats; public class FillRectangle : IDisposable { GraphicsOptions noneDefault = new GraphicsOptions(); - Color color = Color.HotPink; - SolidBrush brush = Brushes.Solid(Color.HotPink); + Rgba32 color = Rgba32.HotPink; + SolidBrush brush = Brushes.Solid(Rgba32.HotPink); ImageSharp.Rectangle rectangle = new ImageSharp.Rectangle(10, 10, 77, 76); private ProcessorWatchingImage img; @@ -39,7 +35,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.Fill(brush, rectangle); Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -49,7 +45,7 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(rect.Location.Y, rectangle.Y); Assert.Equal(rect.Size.Width, rectangle.Width); Assert.Equal(rect.Size.Height, rectangle.Height); - + Assert.Equal(brush, processor.Brush); } @@ -59,7 +55,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.Fill(brush, rectangle, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); @@ -77,9 +73,9 @@ namespace ImageSharp.Tests.Drawing.Paths public void CorrectlySetsColorAndRectangle() { img.Fill(color, rectangle); - + Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(GraphicsOptions.Default, processor.Options); @@ -90,7 +86,7 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(rect.Size.Width, rectangle.Width); Assert.Equal(rect.Size.Height, rectangle.Height); - SolidBrush brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(color, brush.Color); } @@ -100,7 +96,7 @@ namespace ImageSharp.Tests.Drawing.Paths img.Fill(color, rectangle, noneDefault); Assert.NotEmpty(img.ProcessorApplications); - FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); Assert.Equal(noneDefault, processor.Options); @@ -111,7 +107,7 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.Equal(rect.Size.Width, rectangle.Width); Assert.Equal(rect.Size.Height, rectangle.Height); - SolidBrush brush = Assert.IsType>(processor.Brush); + SolidBrush brush = Assert.IsType>(processor.Brush); Assert.Equal(color, brush.Color); } } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs b/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs index 2d3d2cc2b8..c1d34a112a 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs @@ -6,22 +6,22 @@ namespace ImageSharp.Tests.Drawing.Paths using ImageSharp; using Processing; using System.Collections.Generic; - using ImageSharp.Formats; + using ImageSharp.PixelFormats; /// /// Watches but does not actually run the processors against the image. /// - /// - public class ProcessorWatchingImage : Image + /// + public class ProcessorWatchingImage : Image { public List ProcessorApplications { get; } = new List(); public ProcessorWatchingImage(int width, int height) - : base(width, height, Configuration.CreateDefaultInstance()) + : base(Configuration.CreateDefaultInstance(), width, height) { } - public override void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + public override void ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { this.ProcessorApplications.Add(new ProcessorDetails { @@ -32,7 +32,7 @@ namespace ImageSharp.Tests.Drawing.Paths public struct ProcessorDetails { - public IImageProcessor processor; + public IImageProcessor processor; public Rectangle rectangle; } } diff --git a/tests/ImageSharp.Tests/Drawing/PolygonTests.cs b/tests/ImageSharp.Tests/Drawing/PolygonTests.cs index 3e06ca918e..9bc918d37a 100644 --- a/tests/ImageSharp.Tests/Drawing/PolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/PolygonTests.cs @@ -13,6 +13,8 @@ namespace ImageSharp.Tests.Drawing using ImageSharp.Drawing; using System.Numerics; + using ImageSharp.PixelFormats; + public class PolygonTests : FileTestBase { [Fact] @@ -20,13 +22,13 @@ namespace ImageSharp.Tests.Drawing { string path = this.CreateOutputDirectory("Drawing", "Polygons"); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Simple.png")) { image - .BackgroundColor(Color.Blue) - .DrawPolygon(Color.HotPink, 5, + .BackgroundColor(Rgba32.Blue) + .DrawPolygon(Rgba32.HotPink, 5, new[] { new Vector2(10, 10), new Vector2(200, 150), @@ -35,15 +37,15 @@ namespace ImageSharp.Tests.Drawing .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[9, 9]); + Assert.Equal(Rgba32.HotPink, sourcePixels[9, 9]); - Assert.Equal(Color.HotPink, sourcePixels[199, 149]); + Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]); - Assert.Equal(Color.Blue, sourcePixels[50, 50]); + Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); } } } @@ -58,30 +60,30 @@ namespace ImageSharp.Tests.Drawing new Vector2(50, 300) }; - Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) { image - .BackgroundColor(Color.Blue) + .BackgroundColor(Rgba32.Blue) .DrawPolygon(color, 10, simplePath) .Save(output); } //shift background color towards forground color by the opacity amount - Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); + Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { Assert.Equal(mergedColor, sourcePixels[9, 9]); Assert.Equal(mergedColor, sourcePixels[199, 149]); - Assert.Equal(Color.Blue, sourcePixels[50, 50]); + Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); } } } @@ -91,27 +93,27 @@ namespace ImageSharp.Tests.Drawing { string path = this.CreateOutputDirectory("Drawing", "Polygons"); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Rectangle.png")) { image - .BackgroundColor(Color.Blue) - .Draw(Color.HotPink, 10, new Rectangle(10, 10, 190, 140)) + .BackgroundColor(Rgba32.Blue) + .Draw(Rgba32.HotPink, 10, new Rectangle(10, 10, 190, 140)) .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[8, 8]); + Assert.Equal(Rgba32.HotPink, sourcePixels[8, 8]); - Assert.Equal(Color.HotPink, sourcePixels[198, 10]); + Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]); - Assert.Equal(Color.HotPink, sourcePixels[10, 50]); + Assert.Equal(Rgba32.HotPink, sourcePixels[10, 50]); - Assert.Equal(Color.Blue, sourcePixels[50, 50]); + Assert.Equal(Rgba32.Blue, sourcePixels[50, 50]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); } } } diff --git a/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs b/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs index 0b450d166e..83419caaf4 100644 --- a/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs @@ -9,6 +9,8 @@ namespace ImageSharp.Tests using System.IO; using System.Linq; + using ImageSharp.PixelFormats; + using Xunit; public class RecolorImageTest : FileTestBase @@ -18,11 +20,11 @@ namespace ImageSharp.Tests { string path = this.CreateOutputDirectory("Drawing", "RecolorImage"); - RecolorBrush brush = new RecolorBrush(Color.Yellow, Color.HotPink, 0.2f); + RecolorBrush brush = new RecolorBrush(Rgba32.Yellow, Rgba32.HotPink, 0.2f); foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) { using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { @@ -38,11 +40,11 @@ namespace ImageSharp.Tests { string path = this.CreateOutputDirectory("Drawing", "RecolorImage"); - RecolorBrush brush = new RecolorBrush(Color.Yellow, Color.HotPink, 0.2f); + RecolorBrush brush = new RecolorBrush(Rgba32.Yellow, Rgba32.HotPink, 0.2f); foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) { using (FileStream output = File.OpenWrite($"{path}/Shaped_{file.FileName}")) { diff --git a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs index 1a7e98a12f..6cab7778e0 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidBezierTests.cs @@ -8,6 +8,8 @@ namespace ImageSharp.Tests.Drawing using System.IO; using System.Numerics; + using ImageSharp.PixelFormats; + using SixLabors.Shapes; using Xunit; @@ -24,25 +26,25 @@ namespace ImageSharp.Tests.Drawing new Vector2(240, 30), new Vector2(300, 400) }; - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Simple.png")) { image - .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, new Polygon(new BezierLineSegment(simplePath))) + .BackgroundColor(Rgba32.Blue) + .Fill(Rgba32.HotPink, new Polygon(new BezierLineSegment(simplePath))) .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[150, 300]); + Assert.Equal(Rgba32.HotPink, sourcePixels[150, 300]); //curve points should not be never be set - Assert.Equal(Color.Blue, sourcePixels[240, 30]); + Assert.Equal(Rgba32.Blue, sourcePixels[240, 30]); // inside shape should not be empty - Assert.Equal(Color.HotPink, sourcePixels[200, 250]); + Assert.Equal(Rgba32.HotPink, sourcePixels[200, 250]); } } } @@ -57,28 +59,28 @@ namespace ImageSharp.Tests.Drawing new Vector2(240, 30), new Vector2(300, 400) }; - Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) { image - .BackgroundColor(Color.Blue) + .BackgroundColor(Rgba32.Blue) .Fill(color, new Polygon(new BezierLineSegment(simplePath))) .Save(output); } //shift background color towards forground color by the opacity amount - Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); + Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { //top of curve Assert.Equal(mergedColor, sourcePixels[138, 116]); //curve points should not be never be set - Assert.Equal(Color.Blue, sourcePixels[240, 30]); + Assert.Equal(Rgba32.Blue, sourcePixels[240, 30]); // inside shape should not be empty Assert.Equal(mergedColor, sourcePixels[200, 250]); diff --git a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs index 4ff250a934..5e0244d02f 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidComplexPolygonTests.cs @@ -11,6 +11,8 @@ namespace ImageSharp.Tests.Drawing using ImageSharp.Drawing; using System.Numerics; + using ImageSharp.PixelFormats; + using SixLabors.Shapes; public class SolidComplexPolygonTests : FileTestBase @@ -30,22 +32,22 @@ namespace ImageSharp.Tests.Drawing new Vector2(65, 137))); IPath clipped = simplePath.Clip(hole1); // var clipped = new Rectangle(10, 10, 100, 100).Clip(new Rectangle(20, 0, 20, 20)); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Simple.png")) { image - .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, clipped) + .BackgroundColor(Rgba32.Blue) + .Fill(Rgba32.HotPink, clipped) .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[20, 35]); + Assert.Equal(Rgba32.HotPink, sourcePixels[20, 35]); //inside hole - Assert.Equal(Color.Blue, sourcePixels[60, 100]); + Assert.Equal(Rgba32.Blue, sourcePixels[60, 100]); } } } @@ -65,22 +67,22 @@ namespace ImageSharp.Tests.Drawing new Vector2(130, 40), new Vector2(65, 137))); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/SimpleOverlapping.png")) { image - .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, simplePath.Clip(hole1)) + .BackgroundColor(Rgba32.Blue) + .Fill(Rgba32.HotPink, simplePath.Clip(hole1)) .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[20, 35]); + Assert.Equal(Rgba32.HotPink, sourcePixels[20, 35]); //inside hole - Assert.Equal(Color.Blue, sourcePixels[60, 100]); + Assert.Equal(Rgba32.Blue, sourcePixels[60, 100]); } } } @@ -98,27 +100,27 @@ namespace ImageSharp.Tests.Drawing new Vector2(37, 85), new Vector2(93, 85), new Vector2(65, 137))); - Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) { image - .BackgroundColor(Color.Blue) + .BackgroundColor(Rgba32.Blue) .Fill(color, simplePath.Clip(hole1)) .Save(output); } //shift background color towards forground color by the opacity amount - Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); + Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { Assert.Equal(mergedColor, sourcePixels[20, 35]); //inside hole - Assert.Equal(Color.Blue, sourcePixels[60, 100]); + Assert.Equal(Rgba32.Blue, sourcePixels[60, 100]); } } } diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs index 79363480fc..8ffa62d815 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs @@ -13,6 +13,8 @@ namespace ImageSharp.Tests.Drawing using System.Numerics; using Xunit; using ImageSharp.Drawing.Brushes; + using ImageSharp.PixelFormats; + using SixLabors.Shapes; public class SolidPolygonTests : FileTestBase @@ -27,18 +29,18 @@ namespace ImageSharp.Tests.Drawing new Vector2(50, 300) }; - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Simple.png")) { image - .FillPolygon(Color.HotPink, simplePath, new GraphicsOptions(true)) + .FillPolygon(Rgba32.HotPink, simplePath, new GraphicsOptions(true)) .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[81, 145]); + Assert.Equal(Rgba32.HotPink, sourcePixels[81, 145]); } } } @@ -53,18 +55,18 @@ namespace ImageSharp.Tests.Drawing new Vector2(50, 300) }; - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Pattern.png")) { image - .FillPolygon(Brushes.Horizontal(Color.HotPink), simplePath, new GraphicsOptions(true)) + .FillPolygon(Brushes.Horizontal(Rgba32.HotPink), simplePath, new GraphicsOptions(true)) .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[81, 145]); + Assert.Equal(Rgba32.HotPink, sourcePixels[81, 145]); } } } @@ -79,23 +81,23 @@ namespace ImageSharp.Tests.Drawing new Vector2(50, 300) }; - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) using (FileStream output = File.OpenWrite($"{path}/Simple_NoAntialias.png")) { image - .BackgroundColor(Color.Blue) - .FillPolygon(Color.HotPink, simplePath, new GraphicsOptions(false)) + .BackgroundColor(Rgba32.Blue) + .FillPolygon(Rgba32.HotPink, simplePath, new GraphicsOptions(false)) .Save(output); - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[11, 11]); + Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); - Assert.Equal(Color.HotPink, sourcePixels[199, 150]); + Assert.Equal(Rgba32.HotPink, sourcePixels[199, 150]); - Assert.Equal(Color.HotPink, sourcePixels[50, 50]); + Assert.Equal(Rgba32.HotPink, sourcePixels[50, 50]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); } } } @@ -110,14 +112,14 @@ namespace ImageSharp.Tests.Drawing new Vector2(50, 300) }; - using (Image brushImage = TestFile.Create(TestImages.Bmp.Car).CreateImage()) - using (Image image = new Image(500, 500)) + using (Image brushImage = TestFile.Create(TestImages.Bmp.Car).CreateImage()) + using (Image image = new Image(500, 500)) using (FileStream output = File.OpenWrite($"{path}/Image.png")) { - ImageBrush brush = new ImageBrush(brushImage); + ImageBrush brush = new ImageBrush(brushImage); image - .BackgroundColor(Color.Blue) + .BackgroundColor(Rgba32.Blue) .FillPolygon(brush, simplePath) .Save(output); } @@ -132,24 +134,24 @@ namespace ImageSharp.Tests.Drawing new Vector2(200, 150), new Vector2(50, 300) }; - Color color = new Color(Color.HotPink.R, Color.HotPink.G, Color.HotPink.B, 150); + Rgba32 color = new Rgba32(Rgba32.HotPink.R, Rgba32.HotPink.G, Rgba32.HotPink.B, 150); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Opacity.png")) { image - .BackgroundColor(Color.Blue) + .BackgroundColor(Rgba32.Blue) .FillPolygon(color, simplePath) .Save(output); } //shift background color towards forground color by the opacity amount - Color mergedColor = new Color(Vector4.Lerp(Color.Blue.ToVector4(), Color.HotPink.ToVector4(), 150f / 255f)); + Rgba32 mergedColor = new Rgba32(Vector4.Lerp(Rgba32.Blue.ToVector4(), Rgba32.HotPink.ToVector4(), 150f / 255f)); - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); } } } @@ -159,27 +161,27 @@ namespace ImageSharp.Tests.Drawing { string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); - using (Image image = new Image(500, 500)) + using (Image image = new Image(500, 500)) { using (FileStream output = File.OpenWrite($"{path}/Rectangle.png")) { image - .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, new SixLabors.Shapes.Rectangle(10, 10, 190, 140)) + .BackgroundColor(Rgba32.Blue) + .Fill(Rgba32.HotPink, new SixLabors.Shapes.Rectangle(10, 10, 190, 140)) .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.HotPink, sourcePixels[11, 11]); + Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); - Assert.Equal(Color.HotPink, sourcePixels[198, 10]); + Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]); - Assert.Equal(Color.HotPink, sourcePixels[10, 50]); + Assert.Equal(Rgba32.HotPink, sourcePixels[10, 50]); - Assert.Equal(Color.HotPink, sourcePixels[50, 50]); + Assert.Equal(Rgba32.HotPink, sourcePixels[50, 50]); - Assert.Equal(Color.Blue, sourcePixels[2, 2]); + Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); } } } @@ -189,21 +191,21 @@ namespace ImageSharp.Tests.Drawing { string path = this.CreateOutputDirectory("Drawing", "FilledPolygons"); - using (Image image = new Image(100, 100)) + using (Image image = new Image(100, 100)) { using (FileStream output = File.OpenWrite($"{path}/Triangle.png")) { image - .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, new RegularPolygon(50, 50, 3, 30)) + .BackgroundColor(Rgba32.Blue) + .Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 3, 30)) .Save(output); } - using (PixelAccessor sourcePixels = image.Lock()) + using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Color.Blue, sourcePixels[30, 65]); + Assert.Equal(Rgba32.Blue, sourcePixels[30, 65]); - Assert.Equal(Color.HotPink, sourcePixels[50, 50]); + Assert.Equal(Rgba32.HotPink, sourcePixels[50, 50]); } } } @@ -215,13 +217,13 @@ namespace ImageSharp.Tests.Drawing Configuration config = Configuration.CreateDefaultInstance(); config.ParallelOptions.MaxDegreeOfParallelism = 1; - using (Image image = new Image(100, 100, config)) + using (Image image = new Image(config, 100, 100)) { using (FileStream output = File.OpenWrite($"{path}/Septagon.png")) { image - .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, new RegularPolygon(50, 50, 7, 30, -(float)Math.PI)) + .BackgroundColor(Rgba32.Blue) + .Fill(Rgba32.HotPink, new RegularPolygon(50, 50, 7, 30, -(float)Math.PI)) .Save(output); } } @@ -234,13 +236,13 @@ namespace ImageSharp.Tests.Drawing Configuration config = Configuration.CreateDefaultInstance(); config.ParallelOptions.MaxDegreeOfParallelism = 1; - using (Image image = new Image(100, 100, config)) + using (Image image = new Image(config, 100, 100)) { using (FileStream output = File.OpenWrite($"{path}/ellipse.png")) { image - .BackgroundColor(Color.Blue) - .Fill(Color.HotPink, new Ellipse(50, 50, 30, 50) + .BackgroundColor(Rgba32.Blue) + .Fill(Rgba32.HotPink, new Ellipse(50, 50, 30, 50) .Rotate((float)(Math.PI / 3))) .Save(output); } @@ -254,13 +256,13 @@ namespace ImageSharp.Tests.Drawing Configuration config = Configuration.CreateDefaultInstance(); config.ParallelOptions.MaxDegreeOfParallelism = 1; - using (Image image = new Image(200, 200, config)) + using (Image image = new Image(config, 200, 200)) { using (FileStream output = File.OpenWrite($"{path}/clipped-corner.png")) { image - .Fill(Color.Blue) - .FillPolygon(Color.HotPink, new[] + .Fill(Rgba32.Blue) + .FillPolygon(Rgba32.HotPink, new[] { new Vector2( 8, 8 ), new Vector2( 64, 8 ), diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs index 52b7fcbb65..1516b33d43 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs @@ -12,6 +12,7 @@ namespace ImageSharp.Tests.Drawing.Text using ImageSharp.Drawing.Brushes; using ImageSharp.Drawing.Pens; using ImageSharp.Drawing.Processors; + using ImageSharp.PixelFormats; using ImageSharp.Tests.Drawing.Paths; using SixLabors.Fonts; @@ -21,9 +22,9 @@ namespace ImageSharp.Tests.Drawing.Text public class DrawText : IDisposable { - Color color = Color.HotPink; + Rgba32 color = Rgba32.HotPink; - SolidBrush brush = Brushes.Solid(Color.HotPink); + SolidBrush brush = Brushes.Solid(Rgba32.HotPink); IPath path = new SixLabors.Shapes.Path( new LinearLineSegment( @@ -53,73 +54,73 @@ namespace ImageSharp.Tests.Drawing.Text this.img.DrawText( "123", this.Font, - Brushes.Solid(Color.Red), + Brushes.Solid(Rgba32.Red), null, Vector2.Zero, new TextGraphicsOptions(true)); Assert.NotEmpty(this.img.ProcessorApplications); Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + Assert.IsType>(this.img.ProcessorApplications[0].processor); } [Fact] public void FillsForEachACharachterWhenBrushSetAndNotPenDefaultOptions() { - this.img.DrawText("123", this.Font, Brushes.Solid(Color.Red), null, Vector2.Zero); + this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, Vector2.Zero); Assert.NotEmpty(this.img.ProcessorApplications); Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + Assert.IsType>(this.img.ProcessorApplications[0].processor); } [Fact] public void FillsForEachACharachterWhenBrushSet() { - this.img.DrawText("123", this.Font, Brushes.Solid(Color.Red), Vector2.Zero, new TextGraphicsOptions(true)); + this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero, new TextGraphicsOptions(true)); Assert.NotEmpty(this.img.ProcessorApplications); Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + Assert.IsType>(this.img.ProcessorApplications[0].processor); } [Fact] public void FillsForEachACharachterWhenBrushSetDefaultOptions() { - this.img.DrawText("123", this.Font, Brushes.Solid(Color.Red), Vector2.Zero); + this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero); Assert.NotEmpty(this.img.ProcessorApplications); Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + Assert.IsType>(this.img.ProcessorApplications[0].processor); } [Fact] public void FillsForEachACharachterWhenColorSet() { - this.img.DrawText("123", this.Font, Color.Red, Vector2.Zero, new TextGraphicsOptions(true)); + this.img.DrawText("123", this.Font, Rgba32.Red, Vector2.Zero, new TextGraphicsOptions(true)); Assert.NotEmpty(this.img.ProcessorApplications); Assert.Equal(3, this.img.ProcessorApplications.Count); - FillRegionProcessor processor = - Assert.IsType>(this.img.ProcessorApplications[0].processor); + FillRegionProcessor processor = + Assert.IsType>(this.img.ProcessorApplications[0].processor); - SolidBrush brush = Assert.IsType>(processor.Brush); - Assert.Equal(Color.Red, brush.Color); + SolidBrush brush = Assert.IsType>(processor.Brush); + Assert.Equal(Rgba32.Red, brush.Color); } [Fact] public void FillsForEachACharachterWhenColorSetDefaultOptions() { - this.img.DrawText("123", this.Font, Color.Red, Vector2.Zero); + this.img.DrawText("123", this.Font, Rgba32.Red, Vector2.Zero); Assert.NotEmpty(this.img.ProcessorApplications); Assert.Equal(3, this.img.ProcessorApplications.Count); - Assert.IsType>(this.img.ProcessorApplications[0].processor); - FillRegionProcessor processor = - Assert.IsType>(this.img.ProcessorApplications[0].processor); + Assert.IsType>(this.img.ProcessorApplications[0].processor); + FillRegionProcessor processor = + Assert.IsType>(this.img.ProcessorApplications[0].processor); - SolidBrush brush = Assert.IsType>(processor.Brush); - Assert.Equal(Color.Red, brush.Color); + SolidBrush brush = Assert.IsType>(processor.Brush); + Assert.Equal(Rgba32.Red, brush.Color); } [Fact] @@ -129,43 +130,43 @@ namespace ImageSharp.Tests.Drawing.Text "123", this.Font, null, - Pens.Dash(Color.Red, 1), + Pens.Dash(Rgba32.Red, 1), Vector2.Zero, new TextGraphicsOptions(true)); Assert.NotEmpty(this.img.ProcessorApplications); Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + Assert.IsType>(this.img.ProcessorApplications[0].processor); } [Fact] public void DrawForEachACharachterWhenPenSetAndNotBrushDefaultOptions() { - this.img.DrawText("123", this.Font, null, Pens.Dash(Color.Red, 1), Vector2.Zero); + this.img.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), Vector2.Zero); Assert.NotEmpty(this.img.ProcessorApplications); Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + Assert.IsType>(this.img.ProcessorApplications[0].processor); } [Fact] public void DrawForEachACharachterWhenPenSet() { - this.img.DrawText("123", this.Font, Pens.Dash(Color.Red, 1), Vector2.Zero, new TextGraphicsOptions(true)); + this.img.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero, new TextGraphicsOptions(true)); Assert.NotEmpty(this.img.ProcessorApplications); Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + Assert.IsType>(this.img.ProcessorApplications[0].processor); } [Fact] public void DrawForEachACharachterWhenPenSetDefaultOptions() { - this.img.DrawText("123", this.Font, Pens.Dash(Color.Red, 1), Vector2.Zero); + this.img.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero); Assert.NotEmpty(this.img.ProcessorApplications); Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied - Assert.IsType>(this.img.ProcessorApplications[0].processor); + Assert.IsType>(this.img.ProcessorApplications[0].processor); } [Fact] @@ -174,8 +175,8 @@ namespace ImageSharp.Tests.Drawing.Text this.img.DrawText( "123", this.Font, - Brushes.Solid(Color.Red), - Pens.Dash(Color.Red, 1), + Brushes.Solid(Rgba32.Red), + Pens.Dash(Rgba32.Red, 1), Vector2.Zero, new TextGraphicsOptions(true)); @@ -186,7 +187,7 @@ namespace ImageSharp.Tests.Drawing.Text [Fact] public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSetDefaultOptions() { - this.img.DrawText("123", this.Font, Brushes.Solid(Color.Red), Pens.Dash(Color.Red, 1), Vector2.Zero); + this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), Vector2.Zero); Assert.NotEmpty(this.img.ProcessorApplications); Assert.Equal(6, this.img.ProcessorApplications.Count); @@ -198,26 +199,26 @@ namespace ImageSharp.Tests.Drawing.Text this.img.DrawText( "1", this.Font, - Brushes.Solid(Color.Red), - Pens.Dash(Color.Red, 1), + Brushes.Solid(Rgba32.Red), + Pens.Dash(Rgba32.Red, 1), Vector2.Zero, new TextGraphicsOptions(true)); Assert.NotEmpty(this.img.ProcessorApplications); Assert.Equal(2, this.img.ProcessorApplications.Count); - Assert.IsType>(this.img.ProcessorApplications[0].processor); - Assert.IsType>(this.img.ProcessorApplications[1].processor); + Assert.IsType>(this.img.ProcessorApplications[0].processor); + Assert.IsType>(this.img.ProcessorApplications[1].processor); } [Fact] public void BrushAppliesBeforPenDefaultOptions() { - this.img.DrawText("1", this.Font, Brushes.Solid(Color.Red), Pens.Dash(Color.Red, 1), Vector2.Zero); + this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), Vector2.Zero); Assert.NotEmpty(this.img.ProcessorApplications); Assert.Equal(2, this.img.ProcessorApplications.Count); - Assert.IsType>(this.img.ProcessorApplications[0].processor); - Assert.IsType>(this.img.ProcessorApplications[1].processor); + Assert.IsType>(this.img.ProcessorApplications[0].processor); + Assert.IsType>(this.img.ProcessorApplications[1].processor); } [Fact] @@ -225,19 +226,19 @@ namespace ImageSharp.Tests.Drawing.Text { this.img.MetaData.VerticalResolution = 1; this.img.MetaData.HorizontalResolution = 1; - this.img.DrawText("1", this.Font, Brushes.Solid(Color.Red), Vector2.Zero, new TextGraphicsOptions(true) { + this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero, new TextGraphicsOptions(true) { UseImageResolution = false }); - this.img.DrawText("1", this.Font, Brushes.Solid(Color.Red), Vector2.Zero, new TextGraphicsOptions(true) + this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero, new TextGraphicsOptions(true) { UseImageResolution = true }); Assert.NotEmpty(this.img.ProcessorApplications); Assert.Equal(2, this.img.ProcessorApplications.Count); - FillRegionProcessor ownResolution = Assert.IsType>(this.img.ProcessorApplications[0].processor); - FillRegionProcessor imgResolution = Assert.IsType>(this.img.ProcessorApplications[1].processor); + FillRegionProcessor ownResolution = Assert.IsType>(this.img.ProcessorApplications[0].processor); + FillRegionProcessor imgResolution = Assert.IsType>(this.img.ProcessorApplications[1].processor); ShapeRegion ownRegion = Assert.IsType(ownResolution.Region); ShapeRegion imgRegion = Assert.IsType(imgResolution.Region); diff --git a/tests/ImageSharp.Tests/Drawing/Text/GlyphBuilder.cs b/tests/ImageSharp.Tests/Drawing/Text/GlyphBuilder.cs index c7121695e2..7f16f30bb8 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/GlyphBuilder.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/GlyphBuilder.cs @@ -19,7 +19,7 @@ namespace ImageSharp.Tests.Drawing.Text GlyphBuilder fullBuilder = new GlyphBuilder(new System.Numerics.Vector2(10, 99)); IGlyphRenderer builder = fullBuilder; - builder.BeginGlyph(); + builder.BeginGlyph(Vector2.Zero); builder.BeginFigure(); builder.MoveTo(new Vector2(0, 0)); builder.LineTo(new Vector2(0, 10)); // becomes 0, -10 @@ -52,7 +52,7 @@ namespace ImageSharp.Tests.Drawing.Text IGlyphRenderer builder = fullBuilder; for (int i = 0; i < 10; i++) { - builder.BeginGlyph(); + builder.BeginGlyph(Vector2.Zero); builder.BeginFigure(); builder.MoveTo(new Vector2(0, 0)); builder.LineTo(new Vector2(0, 10)); // becomes 0, -10 diff --git a/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs b/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs index 0bb3afccd7..bb9cd264e7 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs @@ -13,6 +13,8 @@ namespace ImageSharp.Tests.Drawing.Text using SixLabors.Shapes; using ImageSharp.Drawing.Processors; using ImageSharp.Drawing.Pens; + using ImageSharp.PixelFormats; + using SixLabors.Fonts; public class OutputText : FileTestBase @@ -30,10 +32,10 @@ namespace ImageSharp.Tests.Drawing.Text public void DrawAB() { //draws 2 overlapping triangle glyphs twice 1 set on each line - using (Image img = new Image(100, 200)) + using (Image img = new Image(100, 200)) { - img.Fill(Color.DarkBlue) - .DrawText("AB\nAB", new Font(this.Font, 50), Color.Red, new Vector2(0, 0)); + img.Fill(Rgba32.DarkBlue) + .DrawText("AB\nAB", new Font(this.Font, 50), Rgba32.Red, new Vector2(0, 0)); img.Save($"{this.CreateOutputDirectory("Drawing", "Text")}/AB.png"); } } diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index 765ff3a423..12c7d51541 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/tests/ImageSharp.Tests/FileTestBase.cs @@ -28,8 +28,9 @@ namespace ImageSharp.Tests // TestFile.Create(TestImages.Jpeg.Baseline.GammaDalaiLamaGray), // Perf: Enable for local testing only // TestFile.Create(TestImages.Jpeg.Progressive.Bad.BadEOF), // Perf: Enable for local testing only TestFile.Create(TestImages.Bmp.Car), - // TestFile.Create(TestImages.Bmp.Neg_height), // Perf: Enable for local testing only + // TestFile.Create(TestImages.Bmp.NegHeight), // Perf: Enable for local testing only TestFile.Create(TestImages.Png.Splash), + // TestFile.Create(TestImages.Png.Cross), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.ChunkLength1), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.ChunkLength2), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Powerpoint), // Perf: Enable for local testing only @@ -46,6 +47,7 @@ namespace ImageSharp.Tests // TestFile.Create(TestImages.Png.P1), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Pd), // Perf: Enable for local testing only TestFile.Create(TestImages.Gif.Rings), + // TestFile.Create(TestImages.Gif.Trans), // Perf: Enable for local testing only // TestFile.Create(TestImages.Gif.Cheers), // Perf: Enable for local testing only // TestFile.Create(TestImages.Gif.Giphy) // Perf: Enable for local testing only }; diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index 497abb7d56..cf073d3d03 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -7,6 +7,8 @@ using ImageSharp.Formats; namespace ImageSharp.Tests { + using ImageSharp.PixelFormats; + using Xunit; public class BmpEncoderTests : FileTestBase @@ -27,7 +29,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileNameWithoutExtension(bitsPerPixel); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) { image.Save($"{path}/{filename}.bmp", new BmpEncoderOptions { BitsPerPixel = bitsPerPixel }); } diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index 1ecd04690a..b47df8395b 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class GeneralFormatTests : FileTestBase @@ -18,7 +20,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) { using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { @@ -37,7 +39,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) { string filename = path + "/" + file.FileNameWithoutExtension + ".txt"; File.WriteAllText(filename, image.ToBase64String()); @@ -52,7 +54,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) { using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { @@ -69,9 +71,9 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image srcImage = file.CreateImage()) + using (Image srcImage = file.CreateImage()) { - using (Image image = new Image(srcImage)) + using (Image image = new Image(srcImage)) { using (FileStream output = File.OpenWrite($"{path}/Octree-{file.FileName}")) { @@ -81,7 +83,7 @@ namespace ImageSharp.Tests } } - using (Image image = new Image(srcImage)) + using (Image image = new Image(srcImage)) { using (FileStream output = File.OpenWrite($"{path}/Wu-{file.FileName}")) { @@ -90,7 +92,7 @@ namespace ImageSharp.Tests } } - using (Image image = new Image(srcImage)) + using (Image image = new Image(srcImage)) { using (FileStream output = File.OpenWrite($"{path}/Palette-{file.FileName}")) { @@ -109,7 +111,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) { using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.bmp")) { @@ -142,7 +144,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { byte[] serialized; - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (MemoryStream memoryStream = new MemoryStream()) { image.Save(memoryStream); @@ -150,7 +152,7 @@ namespace ImageSharp.Tests serialized = memoryStream.ToArray(); } - using (Image image2 = Image.Load(serialized)) + using (Image image2 = Image.Load(serialized)) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { image2.Save(output); diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 5dac59d696..a5fc92901e 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -3,15 +3,33 @@ // Licensed under the Apache License, Version 2.0. // +// ReSharper disable InconsistentNaming namespace ImageSharp.Tests { using System.Text; using Xunit; using ImageSharp.Formats; + using ImageSharp.PixelFormats; public class GifDecoderTests { + private const PixelTypes PixelTypes = Tests.PixelTypes.StandardImageClass | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; + + public static readonly string[] TestFiles = { TestImages.Gif.Giphy, TestImages.Gif.Rings, TestImages.Gif.Trans }; + + [Theory] + [WithFileCollection(nameof(TestFiles), PixelTypes)] + public void DecodeAndReSave(TestImageProvider imageProvider) + where TPixel : struct, IPixel + { + using (Image image = imageProvider.GetImage()) + { + imageProvider.Utility.SaveTestOutputFile(image, "bmp"); + imageProvider.Utility.SaveTestOutputFile(image, "gif"); + } + } + [Fact] public void Decode_IgnoreMetadataIsFalse_CommentsAreRead() { @@ -22,7 +40,7 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateImage(options)) { Assert.Equal(1, image.MetaData.Properties.Count); Assert.Equal("Comments", image.MetaData.Properties[0].Name); @@ -40,7 +58,7 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateImage(options)) { Assert.Equal(0, image.MetaData.Properties.Count); } @@ -56,7 +74,7 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateImage(options)) { Assert.Equal(1, image.MetaData.Properties.Count); Assert.Equal("浉条卥慨灲", image.MetaData.Properties[0].Value); diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index 897778bc3a..b0ffaaf859 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -9,9 +9,23 @@ namespace ImageSharp.Tests using Xunit; using ImageSharp.Formats; + using ImageSharp.PixelFormats; public class GifEncoderTests { + private const PixelTypes PixelTypes = Tests.PixelTypes.StandardImageClass | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes)] + public void EncodeGeneratedPatterns(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + provider.Utility.SaveTestOutputFile(image, "gif", new GifEncoder()); + } + } + [Fact] public void Encode_IgnoreMetadataIsFalse_CommentsAreWritten() { @@ -22,14 +36,14 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateImage()) { using (MemoryStream memStream = new MemoryStream()) { input.Save(memStream, new GifFormat(), options); memStream.Position = 0; - using (Image output = Image.Load(memStream)) + using (Image output = Image.Load(memStream)) { Assert.Equal(1, output.MetaData.Properties.Count); Assert.Equal("Comments", output.MetaData.Properties[0].Name); @@ -49,14 +63,14 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateImage()) { using (MemoryStream memStream = new MemoryStream()) { input.SaveAsGif(memStream, options); memStream.Position = 0; - using (Image output = Image.Load(memStream)) + using (Image output = Image.Load(memStream)) { Assert.Equal(0, output.MetaData.Properties.Count); } @@ -67,7 +81,7 @@ namespace ImageSharp.Tests [Fact] public void Encode_CommentIsToLong_CommentIsTrimmed() { - using (Image input = new Image(1, 1)) + using (Image input = new Image(1, 1)) { string comments = new string('c', 256); input.MetaData.Properties.Add(new ImageProperty("Comments", comments)); @@ -77,7 +91,7 @@ namespace ImageSharp.Tests input.Save(memStream, new GifFormat()); memStream.Position = 0; - using (Image output = Image.Load(memStream)) + using (Image output = Image.Load(memStream)) { Assert.Equal(1, output.MetaData.Properties.Count); Assert.Equal("Comments", output.MetaData.Properties[0].Name); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs index 8dbdb998c5..dc6985dd34 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/BadEofJpegTests.cs @@ -17,6 +17,7 @@ namespace ImageSharp.Tests using System.Numerics; using ImageSharp.Formats.Jpg; + using ImageSharp.PixelFormats; using ImageSharp.Processing; public class BadEOFJpegTests : MeasureFixture @@ -27,11 +28,11 @@ namespace ImageSharp.Tests } [Theory] - [WithFile(TestImages.Jpeg.Baseline.Bad.MissingEOF, PixelTypes.Color)] - public void LoadBaselineImage(TestImageProvider provider) - where TColor : struct, IPixel + [WithFile(TestImages.Jpeg.Baseline.Bad.MissingEOF, PixelTypes.Rgba32)] + public void LoadBaselineImage(TestImageProvider provider) + where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + using (Image image = provider.GetImage()) { Assert.NotNull(image); provider.Utility.SaveTestOutputFile(image, "bmp"); @@ -39,11 +40,11 @@ namespace ImageSharp.Tests } [Theory] // TODO: #18 - [WithFile(TestImages.Jpeg.Progressive.Bad.BadEOF, PixelTypes.Color)] - public void LoadProgressiveImage(TestImageProvider provider) - where TColor : struct, IPixel + [WithFile(TestImages.Jpeg.Progressive.Bad.BadEOF, PixelTypes.Rgba32)] + public void LoadProgressiveImage(TestImageProvider provider) + where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + using (Image image = provider.GetImage()) { Assert.NotNull(image); provider.Utility.SaveTestOutputFile(image, "bmp"); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index 63ddbc884c..01501a33d4 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -309,7 +309,7 @@ namespace ImageSharp.Tests float[] data = Create8x8FloatData(); Block8x8F block = new Block8x8F(); block.LoadFrom(data); - block.MultiplyAllInplace(new Vector4(5, 5, 5, 5)); + block.MultiplyAllInplace(5); int stride = 256; int height = 42; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index cdd892dcee..a6d6d31f3b 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -10,6 +10,7 @@ namespace ImageSharp.Tests using System.IO; using ImageSharp.Formats; + using ImageSharp.PixelFormats; using Xunit; @@ -25,22 +26,22 @@ namespace ImageSharp.Tests public static string[] ProgressiveTestJpegs = TestImages.Jpeg.Progressive.All; [Theory] - [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)] - public void OpenBaselineJpeg_SaveBmp(TestImageProvider provider) - where TColor : struct, IPixel + [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32 | PixelTypes.StandardImageClass | PixelTypes.Argb32)] + public void OpenBaselineJpeg_SaveBmp(TestImageProvider provider) + where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + using (Image image = provider.GetImage()) { provider.Utility.SaveTestOutputFile(image, "bmp"); } } [Theory] - [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)] - public void OpenProgressiveJpeg_SaveBmp(TestImageProvider provider) - where TColor : struct, IPixel + [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32 | PixelTypes.StandardImageClass | PixelTypes.Argb32)] + public void OpenProgressiveJpeg_SaveBmp(TestImageProvider provider) + where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + using (Image image = provider.GetImage()) { provider.Utility.SaveTestOutputFile(image, "bmp"); } @@ -52,14 +53,14 @@ namespace ImageSharp.Tests [WithSolidFilledImages(16, 16, 255, 0, 0, PixelTypes.StandardImageClass, JpegSubsample.Ratio444, 75)] [WithSolidFilledImages(16, 16, 255, 0, 0, PixelTypes.StandardImageClass, JpegSubsample.Ratio444, 100)] [WithSolidFilledImages(8, 8, 255, 0, 0, PixelTypes.StandardImageClass, JpegSubsample.Ratio444, 100)] - public void DecodeGenerated_SaveBmp( - TestImageProvider provider, + public void DecodeGenerated_SaveBmp( + TestImageProvider provider, JpegSubsample subsample, int quality) - where TColor : struct, IPixel + where TPixel : struct, IPixel { byte[] data; - using (Image image = provider.GetImage()) + using (Image image = provider.GetImage()) { JpegEncoder encoder = new JpegEncoder(); JpegEncoderOptions options = new JpegEncoderOptions { Subsample = subsample, Quality = quality }; @@ -72,18 +73,18 @@ namespace ImageSharp.Tests } // TODO: Automatic image comparers could help here a lot :P - Image mirror = provider.Factory.CreateImage(data); + Image mirror = provider.Factory.CreateImage(data); provider.Utility.TestName += $"_{subsample}_Q{quality}"; provider.Utility.SaveTestOutputFile(mirror, "bmp"); } [Theory] [WithSolidFilledImages(42, 88, 255, 0, 0, PixelTypes.StandardImageClass)] - public void DecodeGenerated_MetadataOnly( - TestImageProvider provider) - where TColor : struct, IPixel + public void DecodeGenerated_MetadataOnly( + TestImageProvider provider) + where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + using (Image image = provider.GetImage()) { using (MemoryStream ms = new MemoryStream()) { @@ -92,7 +93,7 @@ namespace ImageSharp.Tests using (JpegDecoderCore decoder = new JpegDecoderCore(null, null)) { - Image mirror = decoder.Decode(ms); + Image mirror = decoder.Decode(ms); Assert.Equal(decoder.ImageWidth, image.Width); Assert.Equal(decoder.ImageHeight, image.Height); @@ -104,7 +105,7 @@ namespace ImageSharp.Tests [Fact] public void Decoder_Reads_Correct_Resolution_From_Jfif() { - using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage()) + using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage()) { Assert.Equal(300, image.MetaData.HorizontalResolution); Assert.Equal(300, image.MetaData.VerticalResolution); @@ -114,7 +115,7 @@ namespace ImageSharp.Tests [Fact] public void Decoder_Reads_Correct_Resolution_From_Exif() { - using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Jpeg420).CreateImage()) + using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Jpeg420).CreateImage()) { Assert.Equal(72, image.MetaData.HorizontalResolution); Assert.Equal(72, image.MetaData.VerticalResolution); @@ -131,7 +132,7 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateImage(options)) { Assert.NotNull(image.MetaData.ExifProfile); } @@ -147,7 +148,7 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateImage(options)) { Assert.Null(image.MetaData.ExifProfile); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 0833cb8680..1b4f3ea785 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -15,6 +15,7 @@ using Xunit.Abstractions; namespace ImageSharp.Tests { using ImageSharp.Formats.Jpg; + using ImageSharp.PixelFormats; using ImageSharp.Processing; public class JpegEncoderTests : MeasureFixture @@ -31,10 +32,10 @@ namespace ImageSharp.Tests [WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.StandardImageClass, 75, JpegSubsample.Ratio420)] [WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.StandardImageClass, 75, JpegSubsample.Ratio444)] [WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.StandardImageClass, 75, JpegSubsample.Ratio444)] - public void LoadResizeSave(TestImageProvider provider, int quality, JpegSubsample subsample) - where TColor : struct, IPixel + public void LoadResizeSave(TestImageProvider provider, int quality, JpegSubsample subsample) + where TPixel : struct, IPixel { - using (Image image = provider.GetImage().Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max })) + using (Image image = provider.GetImage().Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max })) { image.MetaData.Quality = quality; image.MetaData.ExifProfile = null; // Reduce the size of the file @@ -48,12 +49,12 @@ namespace ImageSharp.Tests } [Theory] - [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb, JpegSubsample.Ratio420, 75)] - [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb, JpegSubsample.Ratio444, 75)] - public void OpenBmp_SaveJpeg(TestImageProvider provider, JpegSubsample subSample, int quality) - where TColor : struct, IPixel + [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32 | PixelTypes.StandardImageClass | PixelTypes.Argb32, JpegSubsample.Ratio420, 75)] + [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32 | PixelTypes.StandardImageClass | PixelTypes.Argb32, JpegSubsample.Ratio444, 75)] + public void OpenBmp_SaveJpeg(TestImageProvider provider, JpegSubsample subSample, int quality) + where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + using (Image image = provider.GetImage()) { ImagingTestCaseUtility utility = provider.Utility; utility.TestName += "_" + subSample + "_Q" + quality; @@ -81,14 +82,14 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateImage()) { using (MemoryStream memStream = new MemoryStream()) { input.Save(memStream, new JpegFormat(), options); memStream.Position = 0; - using (Image output = Image.Load(memStream)) + using (Image output = Image.Load(memStream)) { Assert.NotNull(output.MetaData.ExifProfile); } @@ -106,14 +107,14 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateImage()) { using (MemoryStream memStream = new MemoryStream()) { input.SaveAsJpeg(memStream, options); memStream.Position = 0; - using (Image output = Image.Load(memStream)) + using (Image output = Image.Load(memStream)) { Assert.Null(output.MetaData.ExifProfile); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs index 28a64a765c..b41826e2f6 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs @@ -11,6 +11,7 @@ namespace ImageSharp.Tests using System.Numerics; using ImageSharp.Formats; + using ImageSharp.PixelFormats; using Xunit; using Xunit.Abstractions; @@ -50,7 +51,7 @@ namespace ImageSharp.Tests ExecutionCount, () => { - Image img = Image.Load(bytes); + Image img = Image.Load(bytes); }, // ReSharper disable once ExplicitCallerInfoArgument $"Decode {fileName}"); @@ -69,9 +70,9 @@ namespace ImageSharp.Tests .Concat(new[] { TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk }) .ToArray(); - Image[] testImages = + Image[] testImages = testFiles.Select( - tf => TestImageProvider.File(tf, pixelTypeOverride: PixelTypes.StandardImageClass).GetImage()) + tf => TestImageProvider.File(tf, pixelTypeOverride: PixelTypes.StandardImageClass).GetImage()) .ToArray(); using (MemoryStream ms = new MemoryStream()) @@ -79,7 +80,7 @@ namespace ImageSharp.Tests this.Measure(executionCount, () => { - foreach (Image img in testImages) + foreach (Image img in testImages) { JpegEncoder encoder = new JpegEncoder(); JpegEncoderOptions options = new JpegEncoderOptions { Quality = quality, Subsample = subsample }; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs index d0a7fae333..f242faf12c 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs @@ -10,16 +10,17 @@ namespace ImageSharp.Tests using System.Numerics; using ImageSharp.Formats.Jpg; + using ImageSharp.PixelFormats; using Xunit; public class JpegUtilsTests : TestBase { - public static Image CreateTestImage(GenericFactory factory) - where TColor : struct, IPixel + public static Image CreateTestImage(GenericFactory factory) + where TPixel : struct, IPixel { - Image image = factory.CreateImage(10, 10); - using (PixelAccessor pixels = image.Lock()) + Image image = factory.CreateImage(10, 10); + using (PixelAccessor pixels = image.Lock()) { for (int i = 0; i < 10; i++) { @@ -27,7 +28,7 @@ namespace ImageSharp.Tests { Vector4 v = new Vector4(i / 10f, j / 10f, 0, 1); - TColor color = default(TColor); + TPixel color = default(TPixel); color.PackFromVector4(v); pixels[i, j] = color; @@ -39,15 +40,15 @@ namespace ImageSharp.Tests } [Theory] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)] - public void CopyStretchedRGBTo_FromOrigo(TestImageProvider provider) - where TColor : struct, IPixel + [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Rgba32| PixelTypes.StandardImageClass | PixelTypes.Argb32)] + public void CopyStretchedRGBTo_FromOrigo(TestImageProvider provider) + where TPixel : struct, IPixel { - using (Image src = provider.GetImage()) - using (Image dest = provider.Factory.CreateImage(8, 8)) - using (PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz)) - using (PixelAccessor s = src.Lock()) - using (PixelAccessor d = dest.Lock()) + using (Image src = provider.GetImage()) + using (Image dest = provider.Factory.CreateImage(8, 8)) + using (PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz)) + using (PixelAccessor s = src.Lock()) + using (PixelAccessor d = dest.Lock()) { s.CopyRGBBytesStretchedTo(area, 0, 0); d.CopyFrom(area, 0, 0); @@ -61,15 +62,15 @@ namespace ImageSharp.Tests } [Theory] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Color | PixelTypes.StandardImageClass | PixelTypes.Argb)] - public void CopyStretchedRGBTo_WithOffset(TestImageProvider provider) - where TColor : struct, IPixel + [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Rgba32| PixelTypes.StandardImageClass | PixelTypes.Argb32)] + public void CopyStretchedRGBTo_WithOffset(TestImageProvider provider) + where TPixel : struct, IPixel { - using (Image src = provider.GetImage()) - using (PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz)) - using (Image dest = provider.Factory.CreateImage(8, 8)) - using (PixelAccessor s = src.Lock()) - using (PixelAccessor d = dest.Lock()) + using (Image src = provider.GetImage()) + using (PixelArea area = new PixelArea(8, 8, ComponentOrder.Xyz)) + using (Image dest = provider.Factory.CreateImage(8, 8)) + using (PixelAccessor s = src.Lock()) + using (PixelAccessor d = dest.Lock()) { s.CopyRGBBytesStretchedTo(area, 7, 6); d.CopyFrom(area, 0, 0); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/YCbCrImageTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/YCbCrImageTests.cs index ee38f500b0..ba55665ca2 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/YCbCrImageTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/YCbCrImageTests.cs @@ -61,9 +61,9 @@ namespace ImageSharp.Tests //this.PrintChannel("Cb", img.CbChannel); //this.PrintChannel("Cr", img.CrChannel); - Assert.Equal(img.YChannel.Stride, 400); - Assert.Equal(img.CbChannel.Stride, 400 / expectedCStrideDiv); - Assert.Equal(img.CrChannel.Stride, 400 / expectedCStrideDiv); + Assert.Equal(img.YChannel.Width, 400); + Assert.Equal(img.CbChannel.Width, 400 / expectedCStrideDiv); + Assert.Equal(img.CrChannel.Width, 400 / expectedCStrideDiv); } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index e03d42c9af..cf5c1cfa9b 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -9,9 +9,29 @@ namespace ImageSharp.Tests using Xunit; using ImageSharp.Formats; + using ImageSharp.PixelFormats; public class PngDecoderTests { + private const PixelTypes PixelTypes = Tests.PixelTypes.StandardImageClass | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; + + public static readonly string[] TestFiles = + { + TestImages.Png.Splash, TestImages.Png.Indexed, TestImages.Png.Interlaced, TestImages.Png.FilterVar, + TestImages.Png.ChunkLength1, TestImages.Png.ChunkLength2 + }; + + [Theory] + [WithFileCollection(nameof(TestFiles), PixelTypes)] + public void DecodeAndReSave(TestImageProvider imageProvider) + where TPixel : struct, IPixel + { + using (Image image = imageProvider.GetImage()) + { + imageProvider.Utility.SaveTestOutputFile(image, "bmp"); + } + } + [Fact] public void Decode_IgnoreMetadataIsFalse_TextChunckIsRead() { @@ -22,7 +42,7 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Png.Blur); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateImage(options)) { Assert.Equal(1, image.MetaData.Properties.Count); Assert.Equal("Software", image.MetaData.Properties[0].Name); @@ -40,7 +60,7 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Png.Blur); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateImage(options)) { Assert.Equal(0, image.MetaData.Properties.Count); } @@ -56,7 +76,7 @@ namespace ImageSharp.Tests TestFile testFile = TestFile.Create(TestImages.Png.Blur); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateImage(options)) { Assert.Equal(1, image.MetaData.Properties.Count); Assert.Equal("潓瑦慷敲", image.MetaData.Properties[0].Name); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 51cb0cdc00..195eaba105 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -7,20 +7,46 @@ using ImageSharp.Formats; namespace ImageSharp.Tests { + using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using ImageSharp.IO; + using ImageSharp.PixelFormats; + using Xunit; public class PngEncoderTests : FileTestBase { + private const PixelTypes PixelTypes = Tests.PixelTypes.StandardImageClass | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes, PngColorType.RgbWithAlpha)] + [WithTestPatternImages(100, 100, PixelTypes, PngColorType.Rgb)] + [WithTestPatternImages(100, 100, PixelTypes, PngColorType.Palette)] + [WithTestPatternImages(100, 100, PixelTypes, PngColorType.Grayscale)] + [WithTestPatternImages(100, 100, PixelTypes, PngColorType.GrayscaleWithAlpha)] + public void EncodeGeneratedPatterns(TestImageProvider provider, PngColorType pngColorType) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + PngEncoderOptions options = new PngEncoderOptions() + { + PngColorType = pngColorType + }; + provider.Utility.TestName += "_" + pngColorType; + + provider.Utility.SaveTestOutputFile(image, "png", new PngEncoder(), options); + } + } + [Theory] [WithBlankImages(1, 1, PixelTypes.All)] - public void WritesFileMarker(TestImageProvider provider) - where TColor : struct, IPixel + public void WritesFileMarker(TestImageProvider provider) + where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + using (Image image = provider.GetImage()) using (MemoryStream ms = new MemoryStream()) { image.Save(ms, new PngEncoder()); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index 882f903d68..22bb0b2447 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -13,23 +13,26 @@ namespace ImageSharp.Tests.Formats.Png using ImageSharp.Formats; using System.Linq; using ImageSharp.IO; + using System.Numerics; + + using ImageSharp.PixelFormats; public class PngSmokeTests { [Theory] - [WithTestPatternImages(300, 300, PixelTypes.All)] - public void GeneralTest(TestImageProvider provider) - where TColor : struct, IPixel + [WithTestPatternImages(300, 300, PixelTypes.StandardImageClass)] + public void GeneralTest(TestImageProvider provider) + where TPixel : struct, IPixel { // does saving a file then repoening mean both files are identical??? - using (Image image = provider.GetImage()) + using (Image image = provider.GetImage()) using (MemoryStream ms = new MemoryStream()) { // image.Save(provider.Utility.GetTestOutputFileName("bmp")); image.Save(ms, new PngEncoder()); ms.Position = 0; - using (Image img2 = Image.Load(ms, new PngDecoder())) + using (Image img2 = Image.Load(ms, new PngDecoder())) { // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); ImageComparer.CheckSimilarity(image, img2); @@ -38,33 +41,76 @@ namespace ImageSharp.Tests.Formats.Png } [Theory] - [WithTestPatternImages(100, 100, PixelTypes.All)] - public void CanSaveIndexedPng(TestImageProvider provider) - where TColor : struct, IPixel + [WithTestPatternImages(100, 100, PixelTypes.StandardImageClass)] + public void CanSaveIndexedPng(TestImageProvider provider) + where TPixel : struct, IPixel { // does saving a file then repoening mean both files are identical??? - using (Image image = provider.GetImage()) + using (Image image = provider.GetImage()) using (MemoryStream ms = new MemoryStream()) { // image.Save(provider.Utility.GetTestOutputFileName("bmp")); image.MetaData.Quality = 256; image.Save(ms, new PngEncoder()); ms.Position = 0; - using (Image img2 = Image.Load(ms, new PngDecoder())) + using (Image img2 = Image.Load(ms, new PngDecoder())) { // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); - ImageComparer.CheckSimilarity(image, img2); + ImageComparer.CheckSimilarity(image, img2, 0.03f); } } } + // JJS: Commented out for now since the test does not take into lossy nature of indexing. + //[Theory] + //[WithTestPatternImages(100, 100, PixelTypes.Color)] + //public void CanSaveIndexedPngTwice(TestImageProvider provider) + // where TPixel : struct, IPixel + //{ + // // does saving a file then repoening mean both files are identical??? + // using (Image source = provider.GetImage()) + // using (MemoryStream ms = new MemoryStream()) + // { + // source.MetaData.Quality = 256; + // source.Save(ms, new PngEncoder(), new PngEncoderOptions { + // Threshold = 200 + // }); + // ms.Position = 0; + // using (Image img1 = Image.Load(ms, new PngDecoder())) + // { + // using (MemoryStream ms2 = new MemoryStream()) + // { + // img1.Save(ms2, new PngEncoder(), new PngEncoderOptions + // { + // Threshold = 200 + // }); + // ms2.Position = 0; + // using (Image img2 = Image.Load(ms2, new PngDecoder())) + // { + // using (PixelAccessor pixels1 = img1.Lock()) + // using (PixelAccessor pixels2 = img2.Lock()) + // { + // for (int y = 0; y < img1.Height; y++) + // { + // for (int x = 0; x < img1.Width; x++) + // { + // Assert.Equal(pixels1[x, y], pixels2[x, y]); + // } + // } + // } + // } + // } + // } + // } + //} + [Theory] - [WithTestPatternImages(300, 300, PixelTypes.All)] - public void Resize(TestImageProvider provider) - where TColor : struct, IPixel + [WithTestPatternImages(300, 300, PixelTypes.StandardImageClass)] + public void Resize(TestImageProvider provider) + where TPixel : struct, IPixel { // does saving a file then repoening mean both files are identical??? - using (Image image = provider.GetImage()) + using (Image image = provider.GetImage()) using (MemoryStream ms = new MemoryStream()) { // image.Save(provider.Utility.GetTestOutputFileName("png")); @@ -73,7 +119,7 @@ namespace ImageSharp.Tests.Formats.Png image.Save(ms, new PngEncoder()); ms.Position = 0; - using (Image img2 = Image.Load(ms, new PngDecoder())) + using (Image img2 = Image.Load(ms, new PngDecoder())) { ImageComparer.CheckSimilarity(image, img2); } diff --git a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs index ddb9414cc4..4cdf529e6d 100644 --- a/tests/ImageSharp.Tests/Image/ImageLoadTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageLoadTests.cs @@ -7,9 +7,10 @@ namespace ImageSharp.Tests { using System; using System.IO; - using System.Linq; + using ImageSharp.Formats; using ImageSharp.IO; + using ImageSharp.PixelFormats; using Moq; using Xunit; @@ -20,7 +21,7 @@ namespace ImageSharp.Tests { private readonly Mock fileSystem; private readonly IDecoderOptions decoderOptions; - private Image returnImage; + private Image returnImage; private Mock localDecoder; private Mock localFormat; private readonly string FilePath; @@ -32,8 +33,8 @@ namespace ImageSharp.Tests public ImageLoadTests() { - this.returnImage = new Image(1, 1); - + this.returnImage = new Image(1, 1); + this.localDecoder = new Mock(); this.localFormat = new Mock(); this.localFormat.Setup(x => x.Decoder).Returns(this.localDecoder.Object); @@ -44,7 +45,7 @@ namespace ImageSharp.Tests this.localFormat.Setup(x => x.IsSupportedFileFormat(It.IsAny())).Returns(true); this.localFormat.Setup(x => x.SupportedExtensions).Returns(new string[] { "png", "jpg" }); - this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny(), It.IsAny())) + this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny(), It.IsAny())) .Callback((c, s, o) => { using (var ms = new MemoryStream()) @@ -56,7 +57,7 @@ namespace ImageSharp.Tests .Returns(this.returnImage); this.fileSystem = new Mock(); - + this.LocalConfiguration = new Configuration(this.localFormat.Object) { FileSystem = this.fileSystem.Object @@ -76,7 +77,21 @@ namespace ImageSharp.Tests [Fact] public void LoadFromStream() { - Image img = Image.Load(this.DataStream); + Image img = Image.Load(this.DataStream); + + Assert.NotNull(img); + Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); + + + TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null, Configuration.Default); + + } + + [Fact] + public void LoadFromNoneSeekableStream() + { + NoneSeekableStream stream = new NoneSeekableStream(this.DataStream); + Image img = Image.Load(stream); Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); @@ -89,10 +104,10 @@ namespace ImageSharp.Tests [Fact] public void LoadFromStreamWithType() { - Image img = Image.Load(this.DataStream); + Image img = Image.Load(this.DataStream); Assert.NotNull(img); - Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); + Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null, Configuration.Default); @@ -102,7 +117,7 @@ namespace ImageSharp.Tests [Fact] public void LoadFromStreamWithOptions() { - Image img = Image.Load(this.DataStream, this.decoderOptions); + Image img = Image.Load(this.DataStream, this.decoderOptions); Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); @@ -114,10 +129,10 @@ namespace ImageSharp.Tests [Fact] public void LoadFromStreamWithTypeAndOptions() { - Image img = Image.Load(this.DataStream, this.decoderOptions); + Image img = Image.Load(this.DataStream, this.decoderOptions); Assert.NotNull(img); - Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); + Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions, Configuration.Default); @@ -128,12 +143,12 @@ namespace ImageSharp.Tests public void LoadFromStreamWithConfig() { Stream stream = new MemoryStream(); - Image img = Image.Load(this.LocalConfiguration, stream); + Image img = Image.Load(this.LocalConfiguration, stream); Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream, null)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream, null)); } @@ -141,13 +156,13 @@ namespace ImageSharp.Tests public void LoadFromStreamWithTypeAndConfig() { Stream stream = new MemoryStream(); - Image img = Image.Load(this.LocalConfiguration, stream); + Image img = Image.Load(this.LocalConfiguration, stream); Assert.NotNull(img); Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream, null)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream, null)); } @@ -155,12 +170,12 @@ namespace ImageSharp.Tests public void LoadFromStreamWithConfigAndOptions() { Stream stream = new MemoryStream(); - Image img = Image.Load(this.LocalConfiguration, stream, this.decoderOptions); + Image img = Image.Load(this.LocalConfiguration, stream, this.decoderOptions); Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream, this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream, this.decoderOptions)); } @@ -168,13 +183,13 @@ namespace ImageSharp.Tests public void LoadFromStreamWithTypeAndConfigAndOptions() { Stream stream = new MemoryStream(); - Image img = Image.Load(this.LocalConfiguration, stream, this.decoderOptions); + Image img = Image.Load(this.LocalConfiguration, stream, this.decoderOptions); Assert.NotNull(img); Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream, this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream, this.decoderOptions)); } @@ -184,48 +199,48 @@ namespace ImageSharp.Tests public void LoadFromStreamWithDecoder() { Stream stream = new MemoryStream(); - Image img = Image.Load(stream, this.localDecoder.Object); + Image img = Image.Load(stream, this.localDecoder.Object); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(Configuration.Default, stream, null)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, stream, null)); } [Fact] public void LoadFromStreamWithTypeAndDecoder() { Stream stream = new MemoryStream(); - Image img = Image.Load(stream, this.localDecoder.Object); + Image img = Image.Load(stream, this.localDecoder.Object); Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(Configuration.Default, stream, null)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, stream, null)); } [Fact] public void LoadFromStreamWithDecoderAndOptions() { Stream stream = new MemoryStream(); - Image img = Image.Load(stream, this.localDecoder.Object, this.decoderOptions); + Image img = Image.Load(stream, this.localDecoder.Object, this.decoderOptions); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(Configuration.Default, stream, this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, stream, this.decoderOptions)); } [Fact] public void LoadFromStreamWithTypeAndDecoderAndOptions() { Stream stream = new MemoryStream(); - Image img = Image.Load(stream, this.localDecoder.Object, this.decoderOptions); + Image img = Image.Load(stream, this.localDecoder.Object, this.decoderOptions); Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(Configuration.Default, stream, this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, stream, this.decoderOptions)); } [Fact] public void LoadFromBytes() { - Image img = Image.Load(this.DataStream.ToArray()); + Image img = Image.Load(this.DataStream.ToArray()); Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); @@ -238,10 +253,10 @@ namespace ImageSharp.Tests [Fact] public void LoadFromBytesWithType() { - Image img = Image.Load(this.DataStream.ToArray()); + Image img = Image.Load(this.DataStream.ToArray()); Assert.NotNull(img); - Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); + Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null, Configuration.Default); @@ -251,7 +266,7 @@ namespace ImageSharp.Tests [Fact] public void LoadFromBytesWithOptions() { - Image img = Image.Load(this.DataStream.ToArray(), this.decoderOptions); + Image img = Image.Load(this.DataStream.ToArray(), this.decoderOptions); Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); @@ -263,10 +278,10 @@ namespace ImageSharp.Tests [Fact] public void LoadFromBytesWithTypeAndOptions() { - Image img = Image.Load(this.DataStream.ToArray(), this.decoderOptions); + Image img = Image.Load(this.DataStream.ToArray(), this.decoderOptions); Assert.NotNull(img); - Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); + Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions, Configuration.Default); @@ -276,12 +291,12 @@ namespace ImageSharp.Tests [Fact] public void LoadFromBytesWithConfig() { - Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray()); + Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray()); Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, It.IsAny(), null)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, It.IsAny(), null)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -289,14 +304,14 @@ namespace ImageSharp.Tests [Fact] public void LoadFromBytesWithTypeAndConfig() { - Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray()); + Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray()); Assert.NotNull(img); Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, It.IsAny(), null)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, It.IsAny(), null)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -304,12 +319,12 @@ namespace ImageSharp.Tests [Fact] public void LoadFromBytesWithConfigAndOptions() { - Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray(), this.decoderOptions); + Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray(), this.decoderOptions); Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, It.IsAny(), this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, It.IsAny(), this.decoderOptions)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -317,13 +332,13 @@ namespace ImageSharp.Tests [Fact] public void LoadFromBytesWithTypeAndConfigAndOptions() { - Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray(), this.decoderOptions); + Image img = Image.Load(this.LocalConfiguration, this.DataStream.ToArray(), this.decoderOptions); Assert.NotNull(img); Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, It.IsAny(), this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, It.IsAny(), this.decoderOptions)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } @@ -332,49 +347,49 @@ namespace ImageSharp.Tests [Fact] public void LoadFromBytesWithDecoder() { - Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object); + Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(Configuration.Default, It.IsAny(), null)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, It.IsAny(), null)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } [Fact] public void LoadFromBytesWithTypeAndDecoder() { - Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object); + Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object); Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(Configuration.Default, It.IsAny(), null)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, It.IsAny(), null)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } [Fact] public void LoadFromBytesWithDecoderAndOptions() { - Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object, this.decoderOptions); + Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object, this.decoderOptions); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(Configuration.Default, It.IsAny(), this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, It.IsAny(), this.decoderOptions)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } [Fact] public void LoadFromBytesWithTypeAndDecoderAndOptions() { - Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object, this.decoderOptions); + Image img = Image.Load(this.DataStream.ToArray(), this.localDecoder.Object, this.decoderOptions); Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(Configuration.Default, It.IsAny(), this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, It.IsAny(), this.decoderOptions)); Assert.Equal(this.DataStream.ToArray(), this.DecodedData); } [Fact] public void LoadFromFile() { - Image img = Image.Load(this.DataStream); + Image img = Image.Load(this.DataStream); Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); @@ -387,10 +402,10 @@ namespace ImageSharp.Tests [Fact] public void LoadFromFileWithType() { - Image img = Image.Load(this.DataStream); + Image img = Image.Load(this.DataStream); Assert.NotNull(img); - Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); + Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, null, Configuration.Default); @@ -400,7 +415,7 @@ namespace ImageSharp.Tests [Fact] public void LoadFromFileWithOptions() { - Image img = Image.Load(this.DataStream, this.decoderOptions); + Image img = Image.Load(this.DataStream, this.decoderOptions); Assert.NotNull(img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); @@ -412,10 +427,10 @@ namespace ImageSharp.Tests [Fact] public void LoadFromFileWithTypeAndOptions() { - Image img = Image.Load(this.DataStream, this.decoderOptions); + Image img = Image.Load(this.DataStream, this.decoderOptions); Assert.NotNull(img); - Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); + Assert.Equal(TestFormat.GlobalTestFormat.Sample(), img); Assert.Equal(TestFormat.GlobalTestFormat, img.CurrentImageFormat); TestFormat.GlobalTestFormat.VerifyDecodeCall(this.Marker, this.decoderOptions, Configuration.Default); @@ -425,50 +440,50 @@ namespace ImageSharp.Tests [Fact] public void LoadFromFileWithConfig() { - Image img = Image.Load(this.LocalConfiguration, this.FilePath); + Image img = Image.Load(this.LocalConfiguration, this.FilePath); Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, this.DataStream, null)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, this.DataStream, null)); } [Fact] public void LoadFromFileWithTypeAndConfig() { - Image img = Image.Load(this.LocalConfiguration, this.FilePath); + Image img = Image.Load(this.LocalConfiguration, this.FilePath); Assert.NotNull(img); Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, this.DataStream, null)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, this.DataStream, null)); } [Fact] public void LoadFromFileWithConfigAndOptions() { - Image img = Image.Load(this.LocalConfiguration, this.FilePath, this.decoderOptions); + Image img = Image.Load(this.LocalConfiguration, this.FilePath, this.decoderOptions); Assert.NotNull(img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, this.DataStream, this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, this.DataStream, this.decoderOptions)); } [Fact] public void LoadFromFileWithTypeAndConfigAndOptions() { - Image img = Image.Load(this.LocalConfiguration, this.FilePath, this.decoderOptions); + Image img = Image.Load(this.LocalConfiguration, this.FilePath, this.decoderOptions); Assert.NotNull(img); Assert.Equal(this.returnImage, img); Assert.Equal(this.localFormat.Object, img.CurrentImageFormat); - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, this.DataStream, this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, this.DataStream, this.decoderOptions)); } @@ -476,39 +491,39 @@ namespace ImageSharp.Tests [Fact] public void LoadFromFileWithDecoder() { - Image img = Image.Load(this.FilePath, this.localDecoder.Object); + Image img = Image.Load(this.FilePath, this.localDecoder.Object); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(Configuration.Default, this.DataStream, null)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, this.DataStream, null)); } [Fact] public void LoadFromFileWithTypeAndDecoder() { - Image img = Image.Load(this.FilePath, this.localDecoder.Object); + Image img = Image.Load(this.FilePath, this.localDecoder.Object); Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(Configuration.Default, this.DataStream, null)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, this.DataStream, null)); } [Fact] public void LoadFromFileWithDecoderAndOptions() { - Image img = Image.Load(this.FilePath, this.localDecoder.Object, this.decoderOptions); + Image img = Image.Load(this.FilePath, this.localDecoder.Object, this.decoderOptions); Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(Configuration.Default, this.DataStream, this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, this.DataStream, this.decoderOptions)); } [Fact] public void LoadFromFileWithTypeAndDecoderAndOptions() { - Image img = Image.Load(this.FilePath, this.localDecoder.Object, this.decoderOptions); + Image img = Image.Load(this.FilePath, this.localDecoder.Object, this.decoderOptions); Assert.NotNull(img); Assert.Equal(this.returnImage, img); - this.localDecoder.Verify(x => x.Decode(Configuration.Default, this.DataStream, this.decoderOptions)); + this.localDecoder.Verify(x => x.Decode(Configuration.Default, this.DataStream, this.decoderOptions)); } public void Dispose() diff --git a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs index 0d1c3e09b5..9e9852cb2c 100644 --- a/tests/ImageSharp.Tests/Image/ImageSaveTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageSaveTests.cs @@ -10,6 +10,8 @@ namespace ImageSharp.Tests using System.Linq; using ImageSharp.Formats; using ImageSharp.IO; + using ImageSharp.PixelFormats; + using Moq; using Xunit; @@ -18,7 +20,7 @@ namespace ImageSharp.Tests /// public class ImageSaveTests : IDisposable { - private readonly Image Image; + private readonly Image Image; private readonly Mock fileSystem; private readonly Mock format; private readonly Mock formatNotRegistered; @@ -47,9 +49,10 @@ namespace ImageSharp.Tests this.fileSystem = new Mock(); this.encoderOptions = new Mock().Object; - this.Image = new Image(1, 1, new Configuration(this.format.Object) { + this.Image = new Image(new Configuration(this.format.Object) + { FileSystem = this.fileSystem.Object - }); + }, 1, 1); } [Fact] @@ -59,7 +62,7 @@ namespace ImageSharp.Tests this.fileSystem.Setup(x => x.Create("path.png")).Returns(stream); this.Image.Save("path.png"); - this.encoder.Verify(x => x.Encode(this.Image, stream, null)); + this.encoder.Verify(x => x.Encode(this.Image, stream, null)); } [Fact] @@ -69,8 +72,8 @@ namespace ImageSharp.Tests this.fileSystem.Setup(x => x.Create("path.jpg")).Returns(stream); this.Image.Save("path.jpg", this.encoderOptions); - - this.encoder.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); + + this.encoder.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); } [Fact] @@ -81,7 +84,7 @@ namespace ImageSharp.Tests this.Image.Save("path.jpg", this.encoderNotInFormat.Object); - this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, null)); + this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, null)); } [Fact] @@ -92,7 +95,7 @@ namespace ImageSharp.Tests this.Image.Save("path.jpg", this.encoderNotInFormat.Object, this.encoderOptions); - this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); + this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); } @@ -105,7 +108,7 @@ namespace ImageSharp.Tests this.Image.Save("path.jpg", this.encoderNotInFormat.Object); - this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, null)); + this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, null)); } [Fact] @@ -116,7 +119,7 @@ namespace ImageSharp.Tests this.Image.Save("path.jpg", this.encoderNotInFormat.Object, this.encoderOptions); - this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); + this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); } [Fact] @@ -124,8 +127,8 @@ namespace ImageSharp.Tests { Stream stream = new MemoryStream(); this.Image.Save(stream); - - this.encoder.Verify(x => x.Encode(this.Image, stream, null)); + + this.encoder.Verify(x => x.Encode(this.Image, stream, null)); } [Fact] @@ -135,7 +138,7 @@ namespace ImageSharp.Tests this.Image.Save(stream, this.encoderOptions); - this.encoder.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); + this.encoder.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); } [Fact] @@ -145,7 +148,7 @@ namespace ImageSharp.Tests this.Image.Save(stream, this.encoderNotInFormat.Object); - this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, null)); + this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, null)); } [Fact] @@ -155,7 +158,7 @@ namespace ImageSharp.Tests this.Image.Save(stream, this.encoderNotInFormat.Object, this.encoderOptions); - this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); + this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); } [Fact] @@ -165,7 +168,7 @@ namespace ImageSharp.Tests this.Image.Save(stream, this.formatNotRegistered.Object); - this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, null)); + this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, null)); } [Fact] @@ -175,7 +178,7 @@ namespace ImageSharp.Tests this.Image.Save(stream, this.formatNotRegistered.Object, this.encoderOptions); - this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); + this.encoderNotInFormat.Verify(x => x.Encode(this.Image, stream, this.encoderOptions)); } public void Dispose() diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index 02b0e5ad9b..a3ec4cec21 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -8,6 +8,7 @@ namespace ImageSharp.Tests using System; using ImageSharp.Formats; + using ImageSharp.PixelFormats; using Xunit; @@ -21,11 +22,11 @@ namespace ImageSharp.Tests { Assert.Throws(() => { - Image.Load((byte[])null); + Image.Load((byte[])null); }); TestFile file = TestFile.Create(TestImages.Bmp.Car); - using (Image image = Image.Load(file.Bytes)) + using (Image image = Image.Load(file.Bytes)) { Assert.Equal(600, image.Width); Assert.Equal(450, image.Height); @@ -36,7 +37,7 @@ namespace ImageSharp.Tests public void ConstructorFileSystem() { TestFile file = TestFile.Create(TestImages.Bmp.Car); - using (Image image = Image.Load(file.FilePath)) + using (Image image = Image.Load(file.FilePath)) { Assert.Equal(600, image.Width); Assert.Equal(450, image.Height); @@ -49,7 +50,7 @@ namespace ImageSharp.Tests System.IO.FileNotFoundException ex = Assert.Throws( () => { - Image.Load(Guid.NewGuid().ToString()); + Image.Load(Guid.NewGuid().ToString()); }); } @@ -59,7 +60,7 @@ namespace ImageSharp.Tests ArgumentNullException ex = Assert.Throws( () => { - Image.Load((string) null); + Image.Load((string)null); }); } @@ -68,13 +69,13 @@ namespace ImageSharp.Tests { string file = TestFile.GetPath("../../TestOutput/Save_DetecedEncoding.png"); System.IO.DirectoryInfo dir = System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(file)); - using (Image image = new Image(10, 10)) + using (Image image = new Image(10, 10)) { image.Save(file); } TestFile c = TestFile.Create("../../TestOutput/Save_DetecedEncoding.png"); - using (Image img = c.CreateImage()) + using (Image img = c.CreateImage()) { Assert.IsType(img.CurrentImageFormat); } @@ -87,7 +88,7 @@ namespace ImageSharp.Tests InvalidOperationException ex = Assert.Throws( () => { - using (Image image = new Image(10, 10)) + using (Image image = new Image(10, 10)) { image.Save(file); } @@ -99,13 +100,13 @@ namespace ImageSharp.Tests { string file = TestFile.GetPath("../../TestOutput/Save_SetFormat.dat"); System.IO.DirectoryInfo dir = System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(file)); - using (Image image = new Image(10, 10)) + using (Image image = new Image(10, 10)) { image.Save(file, new PngFormat()); } TestFile c = TestFile.Create("../../TestOutput/Save_SetFormat.dat"); - using (Image img = c.CreateImage()) + using (Image img = c.CreateImage()) { Assert.IsType(img.CurrentImageFormat); } @@ -116,13 +117,13 @@ namespace ImageSharp.Tests { string file = TestFile.GetPath("../../TestOutput/Save_SetEncoding.dat"); System.IO.DirectoryInfo dir = System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(file)); - using (Image image = new Image(10, 10)) + using (Image image = new Image(10, 10)) { image.Save(file, new PngEncoder()); } TestFile c = TestFile.Create("../../TestOutput/Save_SetEncoding.dat"); - using (Image img = c.CreateImage()) + using (Image img = c.CreateImage()) { Assert.IsType(img.CurrentImageFormat); } diff --git a/tests/ImageSharp.Tests/Image/NoneSeekableStream.cs b/tests/ImageSharp.Tests/Image/NoneSeekableStream.cs new file mode 100644 index 0000000000..bc36b60eb2 --- /dev/null +++ b/tests/ImageSharp.Tests/Image/NoneSeekableStream.cs @@ -0,0 +1,50 @@ +using System; +using System.IO; + +namespace ImageSharp.Tests +{ + internal class NoneSeekableStream : Stream + { + private Stream dataStream; + + public NoneSeekableStream(Stream dataStream) + { + this.dataStream = dataStream; + } + + public override bool CanRead => this.dataStream.CanRead; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override long Length => this.dataStream.Length; + + public override long Position { get => this.dataStream.Position; set => throw new NotImplementedException(); } + + public override void Flush() + { + this.dataStream.Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + return this.dataStream.Read(buffer, offset, count); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs index cd9cd04b72..a6c4b4545d 100644 --- a/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs +++ b/tests/ImageSharp.Tests/Image/PixelAccessorTests.cs @@ -8,6 +8,8 @@ namespace ImageSharp.Tests using System; using System.Numerics; + using ImageSharp.PixelFormats; + using Xunit; /// @@ -15,11 +17,11 @@ namespace ImageSharp.Tests /// public class PixelAccessorTests { - public static Image CreateTestImage(GenericFactory factory) - where TColor : struct, IPixel + public static Image CreateTestImage(GenericFactory factory) + where TPixel : struct, IPixel { - Image image = factory.CreateImage(10, 10); - using (PixelAccessor pixels = image.Lock()) + Image image = factory.CreateImage(10, 10); + using (PixelAccessor pixels = image.Lock()) { for (int i = 0; i < 10; i++) { @@ -28,7 +30,7 @@ namespace ImageSharp.Tests Vector4 v = new Vector4(i, j, 0, 1); v /= 10; - TColor color = default(TColor); + TPixel color = default(TPixel); color.PackFromVector4(v); pixels[i, j] = color; @@ -43,21 +45,21 @@ namespace ImageSharp.Tests [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Zyx)] [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Xyzw)] [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All, ComponentOrder.Zyxw)] - public void CopyTo_Then_CopyFrom_OnFullImageRect(TestImageProvider provider, ComponentOrder order) - where TColor : struct, IPixel + public void CopyTo_Then_CopyFrom_OnFullImageRect(TestImageProvider provider, ComponentOrder order) + where TPixel : struct, IPixel { - using (Image src = provider.GetImage()) + using (Image src = provider.GetImage()) { - using (Image dest = new Image(src.Width, src.Height)) + using (Image dest = new Image(src.Width, src.Height)) { - using (PixelArea area = new PixelArea(src.Width, src.Height, order)) + using (PixelArea area = new PixelArea(src.Width, src.Height, order)) { - using (PixelAccessor srcPixels = src.Lock()) + using (PixelAccessor srcPixels = src.Lock()) { srcPixels.CopyTo(area, 0, 0); } - using (PixelAccessor destPixels = dest.Lock()) + using (PixelAccessor destPixels = dest.Lock()) { destPixels.CopyFrom(area, 0, 0); } @@ -69,10 +71,10 @@ namespace ImageSharp.Tests } // TODO: Need a processor in the library with this signature - private static void Fill(Image image, Rectangle region, TColor color) - where TColor : struct, IPixel + private static void Fill(Image image, Rectangle region, TPixel color) + where TPixel : struct, IPixel { - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { for (int y = region.Top; y < region.Bottom; y++) { @@ -89,21 +91,21 @@ namespace ImageSharp.Tests [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyx)] [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Xyzw)] [WithBlankImages(16, 16, PixelTypes.All, ComponentOrder.Zyxw)] - public void CopyToThenCopyFromWithOffset(TestImageProvider provider, ComponentOrder order) - where TColor : struct, IPixel + public void CopyToThenCopyFromWithOffset(TestImageProvider provider, ComponentOrder order) + where TPixel : struct, IPixel { - using (Image destImage = new Image(8, 8)) + using (Image destImage = new Image(8, 8)) { - using (Image srcImage = provider.GetImage()) + using (Image srcImage = provider.GetImage()) { - Fill(srcImage, new Rectangle(4, 4, 8, 8), NamedColors.Red); - using (PixelAccessor srcPixels = srcImage.Lock()) + Fill(srcImage, new Rectangle(4, 4, 8, 8), NamedColors.Red); + using (PixelAccessor srcPixels = srcImage.Lock()) { - using (PixelArea area = new PixelArea(8, 8, order)) + using (PixelArea area = new PixelArea(8, 8, order)) { srcPixels.CopyTo(area, 4, 4); - using (PixelAccessor destPixels = destImage.Lock()) + using (PixelAccessor destPixels = destImage.Lock()) { destPixels.CopyFrom(area, 0, 0); } @@ -114,7 +116,7 @@ namespace ImageSharp.Tests provider.Utility.SourceFileOrDescription = order.ToString(); provider.Utility.SaveTestOutputFile(destImage, "bmp"); - using (Image expectedImage = new Image(8, 8).Fill(NamedColors.Red)) + using (Image expectedImage = new Image(8, 8).Fill(NamedColors.Red)) { Assert.True(destImage.IsEquivalentTo(expectedImage)); } @@ -125,7 +127,7 @@ namespace ImageSharp.Tests [Fact] public void CopyFromZYX() { - using (Image image = new Image(1, 1)) + using (Image image = new Image(1, 1)) { CopyFromZYX(image); } @@ -134,7 +136,7 @@ namespace ImageSharp.Tests [Fact] public void CopyFromZYXW() { - using (Image image = new Image(1, 1)) + using (Image image = new Image(1, 1)) { CopyFromZYXW(image); } @@ -143,7 +145,7 @@ namespace ImageSharp.Tests [Fact] public void CopyToZYX() { - using (Image image = new Image(1, 1)) + using (Image image = new Image(1, 1)) { CopyToZYX(image); } @@ -152,23 +154,23 @@ namespace ImageSharp.Tests [Fact] public void CopyToZYXW() { - using (Image image = new Image(1, 1)) + using (Image image = new Image(1, 1)) { CopyToZYXW(image); } } - private static void CopyFromZYX(Image image) - where TColor : struct, IPixel + private static void CopyFromZYX(Image image) + where TPixel : struct, IPixel { - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { byte red = 1; byte green = 2; byte blue = 3; byte alpha = 255; - using (PixelArea row = new PixelArea(1, ComponentOrder.Zyx)) + using (PixelArea row = new PixelArea(1, ComponentOrder.Zyx)) { row.Bytes[0] = blue; row.Bytes[1] = green; @@ -176,7 +178,7 @@ namespace ImageSharp.Tests pixels.CopyFrom(row, 0); - Color color = (Color)(object)pixels[0, 0]; + Rgba32 color = (Rgba32)(object)pixels[0, 0]; Assert.Equal(red, color.R); Assert.Equal(green, color.G); Assert.Equal(blue, color.B); @@ -185,17 +187,17 @@ namespace ImageSharp.Tests } } - private static void CopyFromZYXW(Image image) - where TColor : struct, IPixel + private static void CopyFromZYXW(Image image) + where TPixel : struct, IPixel { - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { byte red = 1; byte green = 2; byte blue = 3; byte alpha = 4; - using (PixelArea row = new PixelArea(1, ComponentOrder.Zyxw)) + using (PixelArea row = new PixelArea(1, ComponentOrder.Zyxw)) { row.Bytes[0] = blue; row.Bytes[1] = green; @@ -204,7 +206,7 @@ namespace ImageSharp.Tests pixels.CopyFrom(row, 0); - Color color = (Color)(object)pixels[0, 0]; + Rgba32 color = (Rgba32)(object)pixels[0, 0]; Assert.Equal(red, color.R); Assert.Equal(green, color.G); Assert.Equal(blue, color.B); @@ -213,18 +215,18 @@ namespace ImageSharp.Tests } } - private static void CopyToZYX(Image image) - where TColor : struct, IPixel + private static void CopyToZYX(Image image) + where TPixel : struct, IPixel { - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { byte red = 1; byte green = 2; byte blue = 3; - using (PixelArea row = new PixelArea(1, ComponentOrder.Zyx)) + using (PixelArea row = new PixelArea(1, ComponentOrder.Zyx)) { - pixels[0, 0] = (TColor)(object)new Color(red, green, blue); + pixels[0, 0] = (TPixel)(object)new Rgba32(red, green, blue); pixels.CopyTo(row, 0); @@ -235,19 +237,19 @@ namespace ImageSharp.Tests } } - private static void CopyToZYXW(Image image) - where TColor : struct, IPixel + private static void CopyToZYXW(Image image) + where TPixel : struct, IPixel { - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { byte red = 1; byte green = 2; byte blue = 3; byte alpha = 4; - using (PixelArea row = new PixelArea(1, ComponentOrder.Zyxw)) + using (PixelArea row = new PixelArea(1, ComponentOrder.Zyxw)) { - pixels[0, 0] = (TColor)(object)new Color(red, green, blue, alpha); + pixels[0, 0] = (TPixel)(object)new Rgba32(red, green, blue, alpha); pixels.CopyTo(row, 0); diff --git a/tests/ImageSharp.Tests/ImageComparer.cs b/tests/ImageSharp.Tests/ImageComparer.cs index 41b884dd43..d339dc83d2 100644 --- a/tests/ImageSharp.Tests/ImageComparer.cs +++ b/tests/ImageSharp.Tests/ImageComparer.cs @@ -2,6 +2,9 @@ { using System; using ImageSharp; + using ImageSharp.Memory; + using ImageSharp.PixelFormats; + using Xunit; /// @@ -16,8 +19,8 @@ /// /// Does a visual comparison between 2 images and then asserts the difference is less then a configurable threshold /// - /// The color of the expected image - /// The color type fo the the actual image + /// The color of the expected image + /// The color type fo the the actual image /// The expected image /// The actual image /// @@ -32,9 +35,9 @@ /// This is a sampling factor we sample a grid of average pixels width by high /// The default undefined value is /// - public static void CheckSimilarity(Image expected, Image actual, float imageTheshold = DefaultImageThreshold, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) - where TColorA : struct, IPixel - where TColorB : struct, IPixel + public static void CheckSimilarity(Image expected, Image actual, float imageTheshold = DefaultImageThreshold, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) + where TPixelA : struct, IPixel + where TPixelB : struct, IPixel { float percentage = expected.PercentageDifference(actual, segmentThreshold, scalingFactor); @@ -44,8 +47,8 @@ /// /// Does a visual comparison between 2 images and then and returns the percentage diffence between the 2 /// - /// The color of the source image - /// The color type for the target image + /// The color of the source image + /// The color type for the target image /// The source image /// The target image /// @@ -57,9 +60,9 @@ /// The default undefined value is /// /// Returns a number from 0 - 1 which represents the diference focter between the images. - public static float PercentageDifference(this Image source, Image target, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) - where TColorA : struct, IPixel - where TColorB : struct, IPixel + public static float PercentageDifference(this Image source, Image target, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) + where TPixelA : struct, IPixel + where TPixelB : struct, IPixel { // code adapted from https://www.codeproject.com/Articles/374386/Simple-image-comparison-in-NET Fast2DArray differences = GetDifferences(source, target, scalingFactor); @@ -71,12 +74,12 @@ if (b > segmentThreshold) { diffPixels++; } } - return diffPixels / (scalingFactor * scalingFactor); + return (float)diffPixels / (float)(scalingFactor * scalingFactor); } - private static Fast2DArray GetDifferences(Image source, Image target, int scalingFactor) - where TColorA : struct, IPixel - where TColorB : struct, IPixel + private static Fast2DArray GetDifferences(Image source, Image target, int scalingFactor) + where TPixelA : struct, IPixel + where TPixelB : struct, IPixel { Fast2DArray differences = new Fast2DArray(scalingFactor, scalingFactor); Fast2DArray firstGray = source.GetGrayScaleValues(scalingFactor); @@ -86,20 +89,21 @@ { for (int x = 0; x < scalingFactor; x++) { - differences[x, y] = (byte)Math.Abs(firstGray[x, y] - secondGray[x, y]); + var diff = firstGray[x, y] - secondGray[x, y]; + differences[x, y] = (byte)Math.Abs(diff); } } return differences; } - private static Fast2DArray GetGrayScaleValues(this Image source, int scalingFactor) - where TColorA : struct, IPixel + private static Fast2DArray GetGrayScaleValues(this Image source, int scalingFactor) + where TPixelA : struct, IPixel { byte[] buffer = new byte[4]; - using (Image img = new Image(source).Resize(scalingFactor, scalingFactor).Grayscale()) + using (Image img = new Image(source).Resize(scalingFactor, scalingFactor).Grayscale()) { - using (PixelAccessor pixels = img.Lock()) + using (PixelAccessor pixels = img.Lock()) { Fast2DArray grayScale = new Fast2DArray(scalingFactor, scalingFactor); for (int y = 0; y < scalingFactor; y++) diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 438a9213e1..c2529340d5 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -8,6 +8,7 @@ + @@ -23,4 +24,9 @@ + + + PreserveNewest + + \ No newline at end of file diff --git a/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs b/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs index 24dd2eac56..da2bb41561 100644 --- a/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs +++ b/tests/ImageSharp.Tests/MetaData/ImageFrameMetaDataTests.cs @@ -5,6 +5,7 @@ namespace ImageSharp.Tests { + using ImageSharp.Formats; using Xunit; /// @@ -17,10 +18,12 @@ namespace ImageSharp.Tests { ImageFrameMetaData metaData = new ImageFrameMetaData(); metaData.FrameDelay = 42; + metaData.DisposalMethod = DisposalMethod.RestoreToBackground; ImageFrameMetaData clone = new ImageFrameMetaData(metaData); Assert.Equal(42, clone.FrameDelay); + Assert.Equal(DisposalMethod.RestoreToBackground, clone.DisposalMethod); } } } diff --git a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs b/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs index 3c0057b627..bc64d613ab 100644 --- a/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs +++ b/tests/ImageSharp.Tests/MetaData/ImageMetaDataTests.cs @@ -5,6 +5,9 @@ namespace ImageSharp.Tests { + using ImageSharp.Formats; + using ImageSharp.PixelFormats; + using Xunit; /// @@ -27,6 +30,7 @@ namespace ImageSharp.Tests metaData.Properties.Add(imageProperty); metaData.Quality = 24; metaData.RepeatCount = 1; + metaData.DisposalMethod = DisposalMethod.RestoreToBackground; ImageMetaData clone = new ImageMetaData(metaData); @@ -37,6 +41,7 @@ namespace ImageSharp.Tests Assert.Equal(imageProperty, clone.Properties[0]); Assert.Equal(24, clone.Quality); Assert.Equal(1, clone.RepeatCount); + Assert.Equal(DisposalMethod.RestoreToBackground, clone.DisposalMethod); } [Fact] @@ -78,7 +83,7 @@ namespace ImageSharp.Tests exifProfile.SetValue(ExifTag.XResolution, new Rational(200)); exifProfile.SetValue(ExifTag.YResolution, new Rational(300)); - Image image = new Image(1, 1); + Image image = new Image(1, 1); image.MetaData.ExifProfile = exifProfile; image.MetaData.HorizontalResolution = 400; image.MetaData.VerticalResolution = 500; diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs index 1bc31286da..db22300fa5 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs @@ -10,6 +10,9 @@ namespace ImageSharp.Tests using System.IO; using System.Linq; using System.Text; + + using ImageSharp.PixelFormats; + using Xunit; public class ExifProfileTests @@ -17,7 +20,7 @@ namespace ImageSharp.Tests [Fact] public void Constructor() { - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora).CreateImage(); Assert.Null(image.MetaData.ExifProfile); @@ -67,13 +70,13 @@ namespace ImageSharp.Tests profile.SetValue(ExifTag.ExposureTime, new Rational(exposureTime)); - Image image = new Image(1, 1); + Image image = new Image(1, 1); image.MetaData.ExifProfile = profile; image.SaveAsJpeg(memStream); memStream.Position = 0; - image = Image.Load(memStream); + image = Image.Load(memStream); profile = image.MetaData.ExifProfile; Assert.NotNull(profile); @@ -91,7 +94,7 @@ namespace ImageSharp.Tests image.SaveAsJpeg(memStream); memStream.Position = 0; - image = Image.Load(memStream); + image = Image.Load(memStream); profile = image.MetaData.ExifProfile; Assert.NotNull(profile); @@ -104,7 +107,7 @@ namespace ImageSharp.Tests [Fact] public void ReadWriteInfinity() { - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); image.MetaData.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.PositiveInfinity)); image = WriteAndRead(image); @@ -132,7 +135,7 @@ namespace ImageSharp.Tests { Rational[] latitude = new Rational[] { new Rational(12.3), new Rational(4.56), new Rational(789.0) }; - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); image.MetaData.ExifProfile.SetValue(ExifTag.Software, "ImageSharp"); ExifValue value = image.MetaData.ExifProfile.GetValue(ExifTag.Software); @@ -243,7 +246,7 @@ namespace ImageSharp.Tests TestProfile(profile); - Image thumbnail = profile.CreateThumbnail(); + Image thumbnail = profile.CreateThumbnail(); Assert.NotNull(thumbnail); Assert.Equal(256, thumbnail.Width); Assert.Equal(170, thumbnail.Height); @@ -258,7 +261,7 @@ namespace ImageSharp.Tests junk.Append("I"); } - Image image = new Image(100, 100); + Image image = new Image(100, 100); image.MetaData.ExifProfile = new ExifProfile(); image.MetaData.ExifProfile.SetValue(ExifTag.ImageDescription, junk.ToString()); @@ -268,9 +271,27 @@ namespace ImageSharp.Tests } } + [Fact] + public void ExifTypeUndefined() + { + Image image = TestFile.Create(TestImages.Jpeg.Baseline.Bad.ExifUndefType).CreateImage(); + Assert.NotNull(image); + + ExifProfile profile = image.MetaData.ExifProfile; + Assert.NotNull(profile); + + foreach (ExifValue value in profile.Values) + { + if (value.DataType == ExifDataType.Undefined) + { + Assert.Equal(4, value.NumberOfComponents); + } + } + } + private static ExifProfile GetExifProfile() { - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); ExifProfile profile = image.MetaData.ExifProfile; Assert.NotNull(profile); @@ -278,7 +299,7 @@ namespace ImageSharp.Tests return profile; } - private static Image WriteAndRead(Image image) + private static Image WriteAndRead(Image image) { using (MemoryStream memStream = new MemoryStream()) { @@ -286,7 +307,7 @@ namespace ImageSharp.Tests image.Dispose(); memStream.Position = 0; - return Image.Load(memStream); + return Image.Load(memStream); } } diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs new file mode 100644 index 0000000000..dc62f1cbf3 --- /dev/null +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifReaderTests.cs @@ -0,0 +1,35 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.Collections.ObjectModel; + using Xunit; + + public class ExifReaderTests + { + [Fact] + public void Read_DataIsEmpty_ReturnsEmptyCollection() + { + ExifReader reader = new ExifReader(); + byte[] data = new byte[] { }; + + Collection result = reader.Read(data); + + Assert.Equal(0, result.Count); + } + + [Fact] + public void Read_DataIsMinimal_ReturnsEmptyCollection() + { + ExifReader reader = new ExifReader(); + byte[] data = new byte[] { 69, 120, 105, 102, 0, 0 }; + + Collection result = reader.Read(data); + + Assert.Equal(0, result.Count); + } + } +} diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs index 2014d08dc9..a91eb310d8 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs @@ -6,6 +6,9 @@ namespace ImageSharp.Tests { using System.Linq; + + using ImageSharp.PixelFormats; + using Xunit; public class ExifValueTests @@ -13,7 +16,7 @@ namespace ImageSharp.Tests private static ExifValue GetExifValue() { ExifProfile profile; - using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage()) + using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage()) { profile = image.MetaData.ExifProfile; } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs new file mode 100644 index 0000000000..45962c5893 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests.cs @@ -0,0 +1,183 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.PixelFormats.PixelBlenders +{ + using System; + using System.Collections.Generic; + using System.Numerics; + using System.Text; + using ImageSharp.PixelFormats.PixelBlenders; + using ImageSharp.Tests.TestUtilities; + using Xunit; + + public class PorterDuffFunctionsTests + { + public static TheoryData NormalBlendFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(1,1,1,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(0.6f, 0.6f, 0.6f, 1) }, + }; + + [Theory] + [MemberData(nameof(NormalBlendFunctionData))] + public void NormalBlendFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.NormalBlendFunction(back, source, amount); + Assert.Equal(expected, actual); + } + + public static TheoryData MultiplyFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(1,1,1,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(0.6f, 0.6f, 0.6f, 1) }, + { + new TestVector4(0.9f,0.9f,0.9f,0.9f), + new TestVector4(0.4f,0.4f,0.4f,0.4f), + .5f, + new TestVector4(0.7834783f, 0.7834783f, 0.7834783f, 0.92f) + }, + }; + + [Theory] + [MemberData(nameof(MultiplyFunctionData))] + public void MultiplyFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.MultiplyFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 5); + } + + public static TheoryData AddFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(1,1,1,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(.6f, .6f, .6f, 1f) }, + { + new TestVector4(0.2f,0.2f,0.2f,0.3f), + new TestVector4(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestVector4(.2075676f, .2075676f, .2075676f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(AddFunctionData))] + public void AddFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.MultiplyFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 5); + } + + public static TheoryData SubstractFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(0,0,0,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(1,1,1, 1f) }, + { + new TestVector4(0.2f,0.2f,0.2f,0.3f), + new TestVector4(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestVector4(.2027027f, .2027027f, .2027027f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(SubstractFunctionData))] + public void SubstractFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.SubstractFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 5); + } + + public static TheoryData ScreenFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(1,1,1,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(1,1,1, 1f) }, + { + new TestVector4(0.2f,0.2f,0.2f,0.3f), + new TestVector4(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestVector4(.2383784f, .2383784f, .2383784f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(ScreenFunctionData))] + public void ScreenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.ScreenFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 5); + } + + public static TheoryData DarkenFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(1,1,1,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(.6f,.6f,.6f, 1f) }, + { + new TestVector4(0.2f,0.2f,0.2f,0.3f), + new TestVector4(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestVector4(.2189189f, .2189189f, .2189189f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(DarkenFunctionData))] + public void DarkenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.DarkenFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 5); + } + + public static TheoryData LightenFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(1,1,1,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(1,1,1,1f) }, + { + new TestVector4(0.2f,0.2f,0.2f,0.3f), + new TestVector4(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestVector4(.227027f, .227027f, .227027f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(LightenFunctionData))] + public void LightenFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.LightenFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 5); + } + + public static TheoryData OverlayFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(1,1,1,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(1,1,1,1f) }, + { + new TestVector4(0.2f,0.2f,0.2f,0.3f), + new TestVector4(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestVector4(.2124324f, .2124324f, .2124324f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(OverlayFunctionData))] + public void OverlayFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.OverlayFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 5); + } + + public static TheoryData HardLightFunctionData = new TheoryData() { + { new TestVector4(1,1,1,1), new TestVector4(1,1,1,1), 1, new TestVector4(1,1,1,1) }, + { new TestVector4(1,1,1,1), new TestVector4(0,0,0,.8f), .5f, new TestVector4(0.6f,0.6f,0.6f,1f) }, + { + new TestVector4(0.2f,0.2f,0.2f,0.3f), + new TestVector4(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestVector4(.2124324f, .2124324f, .2124324f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(HardLightFunctionData))] + public void HardLightFunction(TestVector4 back, TestVector4 source, float amount, TestVector4 expected) + { + Vector4 actual = PorterDuffFunctions.HardLightFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 5); + } + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs new file mode 100644 index 0000000000..8932f1ffe9 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs @@ -0,0 +1,370 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.PixelFormats.PixelBlenders +{ + using System; + using System.Collections.Generic; + using System.Numerics; + using System.Text; + using ImageSharp.PixelFormats; + using ImageSharp.PixelFormats.PixelBlenders; + using ImageSharp.Tests.TestUtilities; + using Xunit; + + public class PorterDuffFunctionsTests_TPixel + { + private static Span AsSpan(T value) + where T : struct + { + return new Span(new[] { value }); + } + + public static TheoryData NormalBlendFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(1,1,1,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(0.6f, 0.6f, 0.6f, 1) }, + }; + + [Theory] + [MemberData(nameof(NormalBlendFunctionData))] + public void NormalBlendFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.NormalBlendFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(NormalBlendFunctionData))] + public void NormalBlendFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = new DefaultNormalPixelBlender().Blend(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(NormalBlendFunctionData))] + public void NormalBlendFunction_Blender_Bulk(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + Span dest = new Span(new TPixel[1]); + new DefaultNormalPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + VectorAssert.Equal(expected, dest[0], 2); + } + + public static TheoryData MultiplyFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(1,1,1,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(0.6f, 0.6f, 0.6f, 1) }, + { + new TestPixel(0.9f,0.9f,0.9f,0.9f), + new TestPixel(0.4f,0.4f,0.4f,0.4f), + .5f, + new TestPixel(0.7834783f, 0.7834783f, 0.7834783f, 0.92f) + }, + }; + + [Theory] + [MemberData(nameof(MultiplyFunctionData))] + public void MultiplyFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.MultiplyFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(MultiplyFunctionData))] + public void MultiplyFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = new DefaultMultiplyPixelBlender().Blend(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(MultiplyFunctionData))] + public void MultiplyFunction_Blender_Bulk(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + Span dest = new Span(new TPixel[1]); + new DefaultMultiplyPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + VectorAssert.Equal(expected, dest[0], 2); + } + + public static TheoryData AddFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(1,1,1,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(1f, 1f, 1f, 1f) }, + { + new TestPixel(0.2f,0.2f,0.2f,0.3f), + new TestPixel(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestPixel(.2431373f, .2431373f, .2431373f, .372549f) + }, + }; + + [Theory] + [MemberData(nameof(AddFunctionData))] + public void AddFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.AddFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(AddFunctionData))] + public void AddFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = new DefaultAddPixelBlender().Blend(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(AddFunctionData))] + public void AddFunction_Blender_Bulk(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + Span dest = new Span(new TPixel[1]); + new DefaultAddPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + VectorAssert.Equal(expected, dest[0], 2); + } + + public static TheoryData SubstractFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(0,0,0,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(1,1,1, 1f) }, + { + new TestPixel(0.2f,0.2f,0.2f,0.3f), + new TestPixel(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestPixel(.2027027f, .2027027f, .2027027f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(SubstractFunctionData))] + public void SubstractFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.SubstractFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(SubstractFunctionData))] + public void SubstractFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = new DefaultSubstractPixelBlender().Blend(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(SubstractFunctionData))] + public void SubstractFunction_Blender_Bulk(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + Span dest = new Span(new TPixel[1]); + new DefaultSubstractPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + VectorAssert.Equal(expected, dest[0], 2); + } + + public static TheoryData ScreenFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(1,1,1,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(1,1,1, 1f) }, + { + new TestPixel(0.2f,0.2f,0.2f,0.3f), + new TestPixel(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestPixel(.2383784f, .2383784f, .2383784f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(ScreenFunctionData))] + public void ScreenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.ScreenFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(ScreenFunctionData))] + public void ScreenFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = new DefaultScreenPixelBlender().Blend(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(ScreenFunctionData))] + public void ScreenFunction_Blender_Bulk(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + Span dest = new Span(new TPixel[1]); + new DefaultScreenPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + VectorAssert.Equal(expected, dest[0], 2); + } + + public static TheoryData DarkenFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(1,1,1,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(.6f,.6f,.6f, 1f) }, + { + new TestPixel(0.2f,0.2f,0.2f,0.3f), + new TestPixel(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestPixel(.2189189f, .2189189f, .2189189f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(DarkenFunctionData))] + public void DarkenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.DarkenFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(DarkenFunctionData))] + public void DarkenFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = new DefaultDarkenPixelBlender().Blend(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(DarkenFunctionData))] + public void DarkenFunction_Blender_Bulk(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + Span dest = new Span(new TPixel[1]); + new DefaultDarkenPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + VectorAssert.Equal(expected, dest[0], 2); + } + + public static TheoryData LightenFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(1,1,1,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(1,1,1,1f) }, + { + new TestPixel(0.2f,0.2f,0.2f,0.3f), + new TestPixel(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestPixel(.227027f, .227027f, .227027f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(LightenFunctionData))] + public void LightenFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.LightenFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(LightenFunctionData))] + public void LightenFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = new DefaultLightenPixelBlender().Blend(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(LightenFunctionData))] + public void LightenFunction_Blender_Bulk(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + Span dest = new Span(new TPixel[1]); + new DefaultLightenPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + VectorAssert.Equal(expected, dest[0], 2); + } + + public static TheoryData OverlayFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(1,1,1,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(1,1,1,1f) }, + { + new TestPixel(0.2f,0.2f,0.2f,0.3f), + new TestPixel(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestPixel(.2124324f, .2124324f, .2124324f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(OverlayFunctionData))] + public void OverlayFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.OverlayFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(OverlayFunctionData))] + public void OverlayFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = new DefaultOverlayPixelBlender().Blend(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(OverlayFunctionData))] + public void OverlayFunction_Blender_Bulk(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + Span dest = new Span(new TPixel[1]); + new DefaultOverlayPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + VectorAssert.Equal(expected, dest[0], 2); + } + + public static TheoryData HardLightFunctionData = new TheoryData() { + { new TestPixel(1,1,1,1), new TestPixel(1,1,1,1), 1, new TestPixel(1,1,1,1) }, + { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(0.6f,0.6f,0.6f,1f) }, + { + new TestPixel(0.2f,0.2f,0.2f,0.3f), + new TestPixel(0.3f,0.3f,0.3f,0.2f), + .5f, + new TestPixel(.2124324f, .2124324f, .2124324f, .37f) + }, + }; + + [Theory] + [MemberData(nameof(HardLightFunctionData))] + public void HardLightFunction(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = PorterDuffFunctions.HardLightFunction(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(HardLightFunctionData))] + public void HardLightFunction_Blender(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + TPixel actual = new DefaultHardLightPixelBlender().Blend(back, source, amount); + VectorAssert.Equal(expected, actual, 2); + } + + [Theory] + [MemberData(nameof(HardLightFunctionData))] + public void HardLightFunction_Blender_Bulk(TestPixel back, TestPixel source, float amount, TestPixel expected) + where TPixel : struct, IPixel + { + Span dest = new Span(new TPixel[1]); + new DefaultHardLightPixelBlender().Blend(dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + VectorAssert.Equal(expected, dest[0], 2); + } + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs new file mode 100644 index 0000000000..a9108692ed --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.PixelFormats +{ + using System; + using System.Collections.Generic; + using System.Text; + using ImageSharp.PixelFormats; + using ImageSharp.PixelFormats.PixelBlenders; + using ImageSharp.Tests.TestUtilities; + using Xunit; + + public class PixelOperations + { + public static TheoryData blenderMappings = new TheoryData() + { + { new TestPixel(), typeof(DefaultNormalPixelBlender), PixelBlenderMode.Normal }, + { new TestPixel(), typeof(DefaultScreenPixelBlender), PixelBlenderMode.Screen }, + { new TestPixel(), typeof(DefaultHardLightPixelBlender), PixelBlenderMode.HardLight }, + { new TestPixel(), typeof(DefaultOverlayPixelBlender), PixelBlenderMode.Overlay }, + { new TestPixel(), typeof(DefaultDarkenPixelBlender), PixelBlenderMode.Darken }, + { new TestPixel(), typeof(DefaultLightenPixelBlender), PixelBlenderMode.Lighten }, + { new TestPixel(), typeof(DefaultAddPixelBlender), PixelBlenderMode.Add }, + { new TestPixel(), typeof(DefaultSubstractPixelBlender), PixelBlenderMode.Substract }, + { new TestPixel(), typeof(DefaultMultiplyPixelBlender), PixelBlenderMode.Multiply }, + + { new TestPixel(), typeof(DefaultNormalPixelBlender), PixelBlenderMode.Normal }, + { new TestPixel(), typeof(DefaultScreenPixelBlender), PixelBlenderMode.Screen }, + { new TestPixel(), typeof(DefaultHardLightPixelBlender), PixelBlenderMode.HardLight }, + { new TestPixel(), typeof(DefaultOverlayPixelBlender), PixelBlenderMode.Overlay }, + { new TestPixel(), typeof(DefaultDarkenPixelBlender), PixelBlenderMode.Darken }, + { new TestPixel(), typeof(DefaultLightenPixelBlender), PixelBlenderMode.Lighten }, + { new TestPixel(), typeof(DefaultAddPixelBlender), PixelBlenderMode.Add }, + { new TestPixel(), typeof(DefaultSubstractPixelBlender), PixelBlenderMode.Substract }, + { new TestPixel(), typeof(DefaultMultiplyPixelBlender), PixelBlenderMode.Multiply }, + }; + + [Theory] + [MemberData(nameof(blenderMappings))] + public void ReturnsCorrectBlender(TestPixel pixel, Type type, PixelBlenderMode mode) + where TPixel : struct, IPixel + { + PixelBlender blender = PixelOperations.Instance.GetPixelBlender(mode); + Assert.IsType(type, blender); + } + } +} diff --git a/tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs b/tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs index a8aeb33418..e40e3a205d 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs @@ -7,15 +7,17 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class AlphaTest : FileTestBase { - public static readonly TheoryData AlphaValues - = new TheoryData + public static readonly TheoryData AlphaValues + = new TheoryData { - 20 , - 80 + 20/100f , + 80/100f }; [Theory] @@ -27,7 +29,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Alpha(value).Save(output); @@ -43,8 +45,8 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) + string filename = file.GetFileName(value + "-InBox"); + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Alpha(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); diff --git a/tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs index ef183480c3..470e7150b3 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs @@ -6,6 +6,9 @@ namespace ImageSharp.Tests { using System.IO; + + using ImageSharp.PixelFormats; + using Processing; using Xunit; @@ -33,7 +36,7 @@ namespace ImageSharp.Tests TestFile file = TestFile.Create(TestImages.Bmp.F); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) { image.MetaData.ExifProfile = new ExifProfile(); image.MetaData.ExifProfile.SetValue(ExifTag.Orientation, orientation); diff --git a/tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs index fd08b87a47..d7ca78d1bf 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class BackgroundColorTest : FileTestBase @@ -18,10 +20,26 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { - image.BackgroundColor(Color.HotPink).Save(output); + image.BackgroundColor(Rgba32.HotPink).Save(output); + } + } + } + + [Fact] + public void ImageShouldApplyBackgroundColorFilterInBox() + { + string path = this.CreateOutputDirectory("BackgroundColor"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName("-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.BackgroundColor(Rgba32.HotPink, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/BinaryThreshold.cs b/tests/ImageSharp.Tests/Processors/Filters/BinaryThresholdTest.cs similarity index 57% rename from tests/ImageSharp.Tests/Processors/Filters/BinaryThreshold.cs rename to tests/ImageSharp.Tests/Processors/Filters/BinaryThresholdTest.cs index d7d4eac058..4e5e8a82ee 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/BinaryThreshold.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/BinaryThresholdTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class BinaryThresholdTest : FileTestBase @@ -27,12 +29,29 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.BinaryThreshold(value).Save(output); } } } + + [Theory] + [MemberData(nameof(BinaryThresholdValues))] + public void ImageShouldApplyBinaryThresholdInBox(float value) + { + string path = this.CreateOutputDirectory("BinaryThreshold"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName(value + "-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.BinaryThreshold(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/BlackWhiteTest.cs b/tests/ImageSharp.Tests/Processors/Filters/BlackWhiteTest.cs index 6b9a48f72d..d10698a994 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/BlackWhiteTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/BlackWhiteTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class BlackWhiteTest : FileTestBase @@ -18,12 +20,28 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { image.BlackWhite().Save(output); } } } + + [Fact] + public void ImageShouldApplyBlackWhiteFilterInBox() + { + string path = this.CreateOutputDirectory("BlackWhite"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName("-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.BlackWhite(new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processors/Filters/BoxBlurTest.cs index 5d4f628eea..03226a9615 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/BoxBlurTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class BoxBlurTest : FileTestBase @@ -27,12 +29,29 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.BoxBlur(value).Save(output); } } } + + [Theory] + [MemberData(nameof(BoxBlurValues))] + public void ImageShouldApplyBoxBlurFilterInBox(int value) + { + string path = this.CreateOutputDirectory("BoxBlur"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName(value + "-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.BoxBlur(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processors/Filters/BrightnessTest.cs index e274ef0417..ce434d734a 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/BrightnessTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/BrightnessTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class BrightnessTest : FileTestBase @@ -27,12 +29,29 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Brightness(value).Save(output); } } } + + [Theory] + [MemberData(nameof(BrightnessValues))] + public void ImageShouldApplyBrightnessFilterInBox(int value) + { + string path = this.CreateOutputDirectory("Brightness"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName(value + "-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Brightness(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processors/Filters/ColorBlindnessTest.cs index d18f32caf2..c287322530 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/ColorBlindnessTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/ColorBlindnessTest.cs @@ -8,6 +8,8 @@ namespace ImageSharp.Tests using Processing; using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class ColorBlindnessTest : FileTestBase @@ -34,12 +36,29 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(colorBlindness); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.ColorBlindness(colorBlindness).Save(output); } } } + + [Theory] + [MemberData(nameof(ColorBlindnessFilters))] + public void ImageShouldApplyBrightnessFilterInBox(ColorBlindness colorBlindness) + { + string path = this.CreateOutputDirectory("ColorBlindness"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName(colorBlindness + "-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.ColorBlindness(colorBlindness, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/ContrastTest.cs b/tests/ImageSharp.Tests/Processors/Filters/ContrastTest.cs index 09376f2c05..4626fbb6e7 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/ContrastTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/ContrastTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class ContrastTest : FileTestBase @@ -26,12 +28,29 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { image.Contrast(value).Save(output); } } } + + [Theory] + [MemberData(nameof(ContrastValues))] + public void ImageShouldApplyContrastFilterInBox(int value) + { + string path = this.CreateOutputDirectory("Contrast"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName(value + "-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Contrast(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/CropTest.cs b/tests/ImageSharp.Tests/Processors/Filters/CropTest.cs index 69c9d9372b..6713d0d381 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/CropTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/CropTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class CropTest : FileTestBase @@ -18,7 +20,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { image.Crop(image.Width / 2, image.Height / 2).Save(output); diff --git a/tests/ImageSharp.Tests/Processors/Filters/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processors/Filters/DetectEdgesTest.cs index e12440106d..00440e8a5a 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/DetectEdgesTest.cs @@ -8,6 +8,8 @@ namespace ImageSharp.Tests using Processing; using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class DetectEdgesTest : FileTestBase @@ -36,7 +38,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(detector); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.DetectEdges(detector).Save(output); @@ -53,7 +55,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(detector + "-InBox"); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.DetectEdges(detector, new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2)) diff --git a/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs b/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs index e89a1b1ec2..066e2d134b 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs @@ -9,6 +9,7 @@ namespace ImageSharp.Tests using ImageSharp.Dithering; using ImageSharp.Dithering.Ordered; + using ImageSharp.PixelFormats; using Xunit; @@ -41,7 +42,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Dither(ditherer).Save(output); @@ -58,7 +59,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName($"{name}-InBox"); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Dither(ditherer, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); @@ -75,7 +76,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Dither(diffuser, .5F).Save(output); @@ -92,7 +93,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName($"{name}-InBox"); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Dither(diffuser, .5F, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); diff --git a/tests/ImageSharp.Tests/Processors/Filters/EntropyCropTest.cs b/tests/ImageSharp.Tests/Processors/Filters/EntropyCropTest.cs index 1299d9814b..710e23e370 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/EntropyCropTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/EntropyCropTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class EntropyCropTest : FileTestBase @@ -27,7 +29,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.EntropyCrop(value).Save(output); diff --git a/tests/ImageSharp.Tests/Processors/Filters/FlipTests.cs b/tests/ImageSharp.Tests/Processors/Filters/FlipTests.cs index 26bc240d57..87f5e1025a 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/FlipTests.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/FlipTests.cs @@ -6,6 +6,9 @@ namespace ImageSharp.Tests { using System.IO; + + using ImageSharp.PixelFormats; + using Processing; using Xunit; @@ -28,7 +31,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(flipType); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Flip(flipType).Save(output); diff --git a/tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs index 809ffa2f56..4b2ac8b7cf 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs @@ -6,10 +6,10 @@ namespace ImageSharp.Tests { using System.IO; - + using ImageSharp.PixelFormats; using Xunit; - public class GaussianBlurTest : FileTestBase + public class GaussianBlurTest { public static readonly TheoryData GaussianBlurValues = new TheoryData @@ -19,19 +19,33 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData(nameof(GaussianBlurValues))] - public void ImageShouldApplyGaussianBlurFilter(int value) + [WithTestPatternImages(nameof(GaussianBlurValues), 320, 240, PixelTypes.StandardImageClass)] + public void ImageShouldApplyGaussianBlurFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel { - string path = this.CreateOutputDirectory("GaussianBlur"); + using (Image image = provider.GetImage()) + { + image.GaussianBlur(value) + .DebugSave(provider, value.ToString()); + } + } - foreach (TestFile file in Files) + [Theory] + [WithTestPatternImages(nameof(GaussianBlurValues), 320, 240, PixelTypes.StandardImageClass)] + public void ImageShouldApplyGaussianBlurFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (Image image = new Image(source)) { - string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.GaussianBlur(value).Save(output); - } + Rectangle rect = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + image.GaussianBlur(value, rect) + .DebugSave(provider, value.ToString()); + + // lets draw identical shapes over the blured areas and ensure that it didn't change the outer area + image.Fill(NamedColors.HotPink, rect); + source.Fill(NamedColors.HotPink, rect); + ImageComparer.CheckSimilarity(image, source); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs index c1aa069414..1fa1ae15c1 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs @@ -6,7 +6,7 @@ namespace ImageSharp.Tests { using System.IO; - + using ImageSharp.PixelFormats; using Xunit; public class GaussianSharpenTest : FileTestBase @@ -19,19 +19,33 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData(nameof(GaussianSharpenValues))] - public void ImageShouldApplyGaussianSharpenFilter(int value) + [WithTestPatternImages(nameof(GaussianSharpenValues), 320, 240, PixelTypes.StandardImageClass)] + public void ImageShouldApplyGaussianSharpenFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel { - string path = this.CreateOutputDirectory("GaussianSharpen"); + using (Image image = provider.GetImage()) + { + image.GaussianSharpen(value) + .DebugSave(provider, value.ToString()); + } + } - foreach (TestFile file in Files) + [Theory] + [WithTestPatternImages(nameof(GaussianSharpenValues), 320, 240, PixelTypes.StandardImageClass)] + public void ImageShouldApplyGaussianSharpenFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (Image image = new Image(source)) { - string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.GaussianSharpen(value).Save(output); - } + Rectangle rect = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + image.GaussianSharpen(value, rect) + .DebugSave(provider, value.ToString()); + + // lets draw identical shapes over the Sharpened areas and ensure that it didn't change the outer area + image.Fill(NamedColors.HotPink, rect); + source.Fill(NamedColors.HotPink, rect); + ImageComparer.CheckSimilarity(image, source); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs b/tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs index 1afb1300a9..43e45f9ca7 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class GlowTest : FileTestBase @@ -18,7 +20,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { image.Glow().Save(output); @@ -34,10 +36,10 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName("Color"); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Glow(Color.HotPink).Save(output); + image.Glow(Rgba32.HotPink).Save(output); } } } @@ -50,7 +52,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName("Radius"); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Glow(image.Width / 4F).Save(output); @@ -66,10 +68,10 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName("InBox"); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Glow(new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2)) + image.Glow(new Rectangle(image.Width / 8, image.Height / 8, image.Width / 2, image.Height / 2)) .Save(output); } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs index 97947a7874..9a7d878546 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs @@ -5,12 +5,10 @@ namespace ImageSharp.Tests { - using System.IO; - using Xunit; using ImageSharp.Processing; - using ImageSharp.Tests; - using System.Numerics; + + using ImageSharp.PixelFormats; public class GrayscaleTest : FileTestBase { @@ -18,23 +16,43 @@ namespace ImageSharp.Tests /// Use test patterns over loaded images to save decode time. /// [Theory] - [WithTestPatternImages(50, 50, PixelTypes.StandardImageClass, GrayscaleMode.Bt709)] + [WithTestPatternImages(50, 50, PixelTypes.StandardImageClass, GrayscaleMode.Bt709)] [WithTestPatternImages(50, 50, PixelTypes.StandardImageClass, GrayscaleMode.Bt601)] - public void ImageShouldApplyGrayscaleFilterAll(TestImageProvider provider, GrayscaleMode value) - where TColor : struct, IPixel + public void ImageShouldApplyGrayscaleFilterAll(TestImageProvider provider, GrayscaleMode value) + where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) + using (Image image = provider.GetImage()) { image.Grayscale(value); byte[] data = new byte[3]; - foreach (TColor p in image.Pixels) + foreach (TPixel p in image.Pixels) { p.ToXyzBytes(data, 0); Assert.Equal(data[0], data[1]); Assert.Equal(data[1], data[2]); } - image.DebugSave(provider); + image.DebugSave(provider, value.ToString()); + } + } + + [Theory] + [WithTestPatternImages(50, 50, PixelTypes.StandardImageClass, GrayscaleMode.Bt709)] + [WithTestPatternImages(50, 50, PixelTypes.StandardImageClass, GrayscaleMode.Bt601)] + public void ImageShouldApplyGrayscaleFilterInBox(TestImageProvider provider, GrayscaleMode value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (Image image = new Image(source)) + { + Rectangle rect = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + image.Grayscale(rect, value) + .DebugSave(provider, value.ToString()); + + // Let's draw identical shapes over the greyed areas and ensure that it didn't change the outer area + image.Fill(NamedColors.HotPink, rect); + source.Fill(NamedColors.HotPink, rect); + ImageComparer.CheckSimilarity(image, source); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/HueTest.cs b/tests/ImageSharp.Tests/Processors/Filters/HueTest.cs index 4241dc8333..488433931c 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/HueTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/HueTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class HueTest : FileTestBase @@ -27,12 +29,29 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Hue(value).Save(output); } } } + + [Theory] + [MemberData(nameof(HueValues))] + public void ImageShouldApplyHueFilterInBox(int value) + { + string path = this.CreateOutputDirectory("Hue"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName(value + "-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Hue(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/InvertTest.cs b/tests/ImageSharp.Tests/Processors/Filters/InvertTest.cs index da672f8307..6d375d09a1 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/InvertTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/InvertTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class InvertTest : FileTestBase @@ -17,7 +19,7 @@ namespace ImageSharp.Tests string path = this.CreateOutputDirectory("Invert"); foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { image.Invert().Save(output); @@ -33,7 +35,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName("InBox"); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Invert(new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); diff --git a/tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs b/tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs index 40734e02a0..29a459f974 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class KodachromeTest : FileTestBase @@ -18,12 +20,28 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { image.Kodachrome().Save(output); } } } + + [Fact] + public void ImageShouldApplyKodachromeFilterInBox() + { + string path = this.CreateOutputDirectory("Kodachrome"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName("InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Kodachrome(new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/LomographTest.cs b/tests/ImageSharp.Tests/Processors/Filters/LomographTest.cs index 57ca72d39e..6ceedecbd8 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/LomographTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/LomographTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class LomographTest : FileTestBase @@ -18,7 +20,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { image.Lomograph().Save(output); @@ -34,7 +36,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName("InBox"); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Lomograph(new Rectangle(image.Width / 4, image.Width / 4, image.Width / 2, image.Height / 2)) diff --git a/tests/ImageSharp.Tests/Processors/Filters/OilPaintTest.cs b/tests/ImageSharp.Tests/Processors/Filters/OilPaintTest.cs index a9b552e216..5facee346b 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/OilPaintTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/OilPaintTest.cs @@ -8,6 +8,8 @@ namespace ImageSharp.Tests using System; using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class OilPaintTest : FileTestBase @@ -28,7 +30,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { if (image.Width > value.Item2 && image.Height > value.Item2) @@ -48,7 +50,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(value + "-InBox"); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) { if (image.Width > value.Item2 && image.Height > value.Item2) { diff --git a/tests/ImageSharp.Tests/Processors/Filters/PadTest.cs b/tests/ImageSharp.Tests/Processors/Filters/PadTest.cs index f00cdd4f36..6095410a9a 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/PadTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/PadTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class PadTest : FileTestBase @@ -18,7 +20,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { image.Pad(image.Width + 50, image.Height + 50).Save(output); diff --git a/tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs b/tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs index 3a5fbc5567..b21a8c969c 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs @@ -6,10 +6,10 @@ namespace ImageSharp.Tests { using System.IO; - + using ImageSharp.PixelFormats; using Xunit; - public class PixelateTest : FileTestBase + public class PixelateTest { public static readonly TheoryData PixelateValues = new TheoryData @@ -19,37 +19,74 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData(nameof(PixelateValues))] - public void ImageShouldApplyPixelateFilter(int value) + [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.StandardImageClass)] + public void ImageShouldApplyPixelateFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel { - string path = CreateOutputDirectory("Pixelate"); - - foreach (TestFile file in Files) + using (Image image = provider.GetImage()) { - string filename = file.GetFileName(value); - Image image = file.CreateImage(); + image.Pixelate(value) + .DebugSave(provider, new + { + size = value + }); - using (FileStream output = File.OpenWrite($"{path}/{filename}")) + using (PixelAccessor pixels = image.Lock()) { - image.Pixelate(value) - .Save(output); + for (int y = 0; y < pixels.Height; y += value) + { + for (int x = 0; x < pixels.Width; x += value) + { + TPixel source = pixels[x, y]; + for (int pixY = y; pixY < y + value && pixY < pixels.Height; pixY++) + { + for (int pixX = x; pixX < x + value && pixX < pixels.Width; pixX++) + { + Assert.Equal(source, pixels[pixX, pixY]); + } + } + } + } } } } [Theory] - [MemberData(nameof(PixelateValues))] - public void ImageShouldApplyPixelateFilterInBox(int value) + [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.StandardImageClass)] + public void ImageShouldApplyPixelateFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel { - string path = this.CreateOutputDirectory("Pixelate"); - - foreach (TestFile file in Files) + using (Image source = provider.GetImage()) + using (Image image = new Image(source)) { - string filename = file.GetFileName(value + "-InBox"); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) + Rectangle rect = new Rectangle(image.Width/4, image.Height / 4, image.Width / 2, image.Height / 2); + + image.Pixelate(value, rect) + .DebugSave(provider, new + { + size = value + }); + + using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { - image.Pixelate(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + for (int y = 0; y < pixels.Height; y++) + { + for (int x = 0; x < pixels.Width; x++) + { + var tx = x; + var ty = y; + TPixel sourceColor = sourcePixels[tx, ty]; + if (rect.Contains(tx, ty)) + { + var sourceX = tx - ((tx - rect.Left) % value) + (value / 2); + var sourceY = ty - ((ty - rect.Top) % value) + (value / 2); + + sourceColor = pixels[sourceX, sourceY]; + } + Assert.Equal(sourceColor, pixels[tx, ty]); + } + } } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs b/tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs index 040f8b4a24..df38032552 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class PolaroidTest : FileTestBase @@ -18,12 +20,28 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { image.Polaroid().Save(output); } } } + + [Fact] + public void ImageShouldApplyPolaroidFilterInBox() + { + string path = this.CreateOutputDirectory("Polaroid"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName("InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Polaroid(new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/ResizeProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Processors/Filters/ResizeProfilingBenchmarks.cs index da09aa85e7..a743665d4b 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/ResizeProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/ResizeProfilingBenchmarks.cs @@ -3,6 +3,7 @@ namespace ImageSharp.Tests using System.IO; using System.Text; + using ImageSharp.PixelFormats; using ImageSharp.Processing; using ImageSharp.Processing.Processors; @@ -26,7 +27,7 @@ namespace ImageSharp.Tests this.Measure(this.ExecutionCount, () => { - using (Image image = new Image(width, height)) + using (Image image = new Image(width, height)) { image.Resize(width / 4, height / 4); } @@ -36,13 +37,13 @@ namespace ImageSharp.Tests // [Fact] public void PrintWeightsData() { - ResizeProcessor proc = new ResizeProcessor(new BicubicResampler(), 200, 200); + ResizeProcessor proc = new ResizeProcessor(new BicubicResampler(), 200, 200); - ResamplingWeightedProcessor.WeightsBuffer weights = proc.PrecomputeWeights(200, 500); + ResamplingWeightedProcessor.WeightsBuffer weights = proc.PrecomputeWeights(200, 500); StringBuilder bld = new StringBuilder(); - foreach (ResamplingWeightedProcessor.WeightsWindow window in weights.Weights) + foreach (ResamplingWeightedProcessor.WeightsWindow window in weights.Weights) { for (int i = 0; i < window.Length; i++) { diff --git a/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs b/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs index 643033f4c4..31f4020b6f 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs @@ -6,6 +6,9 @@ namespace ImageSharp.Tests { using System; using System.IO; + + using ImageSharp.PixelFormats; + using Processing; using Xunit; @@ -42,7 +45,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Resize(image.Width / 2, image.Height / 2, sampler, true).Save(output); @@ -61,7 +64,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { Rectangle sourceRectangle = new Rectangle(image.Width / 8, image.Height / 8, image.Width / 4, image.Height / 4); @@ -82,7 +85,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Resize(image.Width / 3, 0, sampler, false).Save(output); @@ -101,7 +104,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Resize(0, image.Height / 3, sampler, false).Save(output); @@ -120,7 +123,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { ResizeOptions options = new ResizeOptions @@ -145,7 +148,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { ResizeOptions options = new ResizeOptions @@ -170,7 +173,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { ResizeOptions options = new ResizeOptions @@ -195,7 +198,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { ResizeOptions options = new ResizeOptions @@ -221,7 +224,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { ResizeOptions options = new ResizeOptions @@ -247,7 +250,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { ResizeOptions options = new ResizeOptions @@ -273,7 +276,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { ResizeOptions options = new ResizeOptions diff --git a/tests/ImageSharp.Tests/Processors/Filters/RotateFlipTest.cs b/tests/ImageSharp.Tests/Processors/Filters/RotateFlipTest.cs index e235ed229e..c4c4fa8d7a 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/RotateFlipTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/RotateFlipTest.cs @@ -6,6 +6,9 @@ namespace ImageSharp.Tests { using System.IO; + + using ImageSharp.PixelFormats; + using Processing; using Xunit; @@ -30,7 +33,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(rotateType + "-" + flipType); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.RotateFlip(rotateType, flipType).Save(output); diff --git a/tests/ImageSharp.Tests/Processors/Filters/RotateTest.cs b/tests/ImageSharp.Tests/Processors/Filters/RotateTest.cs index a504fd9891..b30c795ad6 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/RotateTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/RotateTest.cs @@ -6,6 +6,9 @@ namespace ImageSharp.Tests { using System.IO; + + using ImageSharp.PixelFormats; + using Processing; using Xunit; @@ -36,7 +39,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Rotate(value).Save(output); @@ -53,7 +56,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Rotate(value).Save(output); diff --git a/tests/ImageSharp.Tests/Processors/Filters/SaturationTest.cs b/tests/ImageSharp.Tests/Processors/Filters/SaturationTest.cs index abd596d708..e28847fa3b 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/SaturationTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/SaturationTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class SaturationTest : FileTestBase @@ -27,12 +29,29 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Saturation(value).Save(output); } } } + + [Theory] + [MemberData(nameof(SaturationValues))] + public void ImageShouldApplySaturationFilterInBox(int value) + { + string path = this.CreateOutputDirectory("Saturation"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName(value + "-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Saturation(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/SepiaTest.cs b/tests/ImageSharp.Tests/Processors/Filters/SepiaTest.cs index fbae10fa55..490213ce7b 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/SepiaTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/SepiaTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class SepiaTest : FileTestBase @@ -18,12 +20,28 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { image.Sepia().Save(output); } } } + + [Fact] + public void ImageShouldApplySepiaFilterInBox() + { + string path = this.CreateOutputDirectory("Sepia"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName("InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Sepia(new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/SkewTest.cs b/tests/ImageSharp.Tests/Processors/Filters/SkewTest.cs index 231f5dae82..d096b3913e 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/SkewTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/SkewTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class SkewTest : FileTestBase @@ -29,7 +31,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName(x + "-" + y); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Skew(x, y).Save(output); diff --git a/tests/ImageSharp.Tests/Processors/Filters/VignetteTest.cs b/tests/ImageSharp.Tests/Processors/Filters/VignetteTest.cs index 7f40ef1d21..4191ae8fa0 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/VignetteTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/VignetteTest.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System.IO; + using ImageSharp.PixelFormats; + using Xunit; public class VignetteTest : FileTestBase @@ -18,7 +20,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) { image.Vignette().Save(output); @@ -34,10 +36,10 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName("Color"); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { - image.Vignette(Color.HotPink).Save(output); + image.Vignette(Rgba32.HotPink).Save(output); } } } @@ -50,7 +52,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName("Radius"); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Vignette(image.Width / 4F, image.Height / 4F).Save(output); @@ -66,7 +68,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { string filename = file.GetFileName("InBox"); - using (Image image = file.CreateImage()) + using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { image.Vignette(new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2)) diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs index eedc0d306a..f1b78383cb 100644 --- a/tests/ImageSharp.Tests/TestFile.cs +++ b/tests/ImageSharp.Tests/TestFile.cs @@ -12,6 +12,8 @@ namespace ImageSharp.Tests using System.Linq; using System.Reflection; + using ImageSharp.PixelFormats; + /// /// A test image file. /// @@ -30,7 +32,7 @@ namespace ImageSharp.Tests /// /// The image. /// - private readonly Image image; + private readonly Image image; /// /// The file. @@ -46,7 +48,7 @@ namespace ImageSharp.Tests this.file = file; this.Bytes = File.ReadAllBytes(file); - this.image = Image.Load(this.Bytes); + this.image = Image.Load(this.Bytes); } /// @@ -125,9 +127,9 @@ namespace ImageSharp.Tests /// /// The . /// - public Image CreateImage() + public Image CreateImage() { - return new Image(this.image); + return new Image(this.image); } /// @@ -137,9 +139,9 @@ namespace ImageSharp.Tests /// /// The . /// - public Image CreateImage(IDecoderOptions options) + public Image CreateImage(IDecoderOptions options) { - return Image.Load(this.Bytes, options); + return Image.Load(this.Bytes, options); } /// diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 084ad59938..318df5ead3 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -12,6 +12,8 @@ namespace ImageSharp.Tests using System.Linq; using System.Reflection; using ImageSharp.Formats; + using ImageSharp.PixelFormats; + using Xunit; /// @@ -68,17 +70,17 @@ namespace ImageSharp.Tests } } - public Image Sample() - where TColor : struct, IPixel + public Image Sample() + where TPixel : struct, IPixel { lock (this._sampleImages) { - if (!this._sampleImages.ContainsKey(typeof(TColor))) + if (!this._sampleImages.ContainsKey(typeof(TPixel))) { - this._sampleImages.Add(typeof(TColor), new Image(1, 1)); + this._sampleImages.Add(typeof(TPixel), new Image(1, 1)); } - return (Image)this._sampleImages[typeof(TColor)]; + return (Image)this._sampleImages[typeof(TPixel)]; } } @@ -149,7 +151,7 @@ namespace ImageSharp.Tests } - public Image Decode(Configuration config, Stream stream, IDecoderOptions options) where TColor : struct, IPixel + public Image Decode(Configuration config, Stream stream, IDecoderOptions options) where TPixel : struct, IPixel { var ms = new MemoryStream(); @@ -163,7 +165,7 @@ namespace ImageSharp.Tests }); // TODO record this happend so we an verify it. - return this.testFormat.Sample(); + return this.testFormat.Sample(); } } @@ -176,7 +178,7 @@ namespace ImageSharp.Tests this.testFormat = testFormat; } - public void Encode(Image image, Stream stream, IEncoderOptions options) where TColor : struct, IPixel + public void Encode(Image image, Stream stream, IEncoderOptions options) where TPixel : struct, IPixel { // TODO record this happend so we an verify it. } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index f0a0e8dd81..44c8c34ee3 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -21,6 +21,7 @@ namespace ImageSharp.Tests public const string Blur = "Png/blur.png"; public const string Indexed = "Png/indexed.png"; public const string Splash = "Png/splash.png"; + public const string Cross = "Png/cross.png"; public const string Powerpoint = "Png/pp.png"; public const string SplashInterlaced = "Png/splash-interlaced.png"; public const string Interlaced = "Png/interlaced.png"; @@ -61,6 +62,7 @@ namespace ImageSharp.Tests public static class Bad { public const string MissingEOF = "Jpg/baseline/badeof.jpg"; + public const string ExifUndefType = "Jpg/baseline/ExifUndefType.jpg"; } public const string Cmyk = "Jpg/baseline/cmyk.jpg"; @@ -102,6 +104,7 @@ namespace ImageSharp.Tests public const string Rings = "Gif/rings.gif"; public const string Giphy = "Gif/giphy.gif"; public const string Cheers = "Gif/cheers.gif"; + public const string Trans = "Gif/trans.gif"; } } } diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Gif/trans.gif b/tests/ImageSharp.Tests/TestImages/Formats/Gif/trans.gif new file mode 100644 index 0000000000..6a7577fa10 --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Gif/trans.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:99b20b417a63c30f62fa69a00fa0a76c2cb9848988e5def1b886460a129197f7 +size 13707 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/ExifUndefType.jpg b/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/ExifUndefType.jpg new file mode 100644 index 0000000000..e1a8a80e23 --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Jpg/baseline/ExifUndefType.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c8f3a1392c59bd60a71c689e04bc028a5b6dc7051edbb17cee2e48d91245f98 +size 6582 diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/cross.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/cross.png new file mode 100644 index 0000000000..1d176fb7b8 --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Png/cross.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0677fbb7f1bd8dd19e8bd7ee802e07f3600193dafbfccf89f43e64e4fdf02d8f +size 15227 diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs index 206393e274..379ce3d054 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Tests using Xunit.Sdk; /// - /// Base class for Theory Data attributes which pass an instance of to the test case. + /// Base class for Theory Data attributes which pass an instance of to the test case. /// public abstract class ImageDataAttributeBase : DataAttribute { @@ -21,26 +21,72 @@ namespace ImageSharp.Tests protected readonly PixelTypes PixelTypes; - protected ImageDataAttributeBase(PixelTypes pixelTypes, object[] additionalParameters) + protected ImageDataAttributeBase(string memberName, PixelTypes pixelTypes, object[] additionalParameters) { this.PixelTypes = pixelTypes; this.AdditionalParameters = additionalParameters; + this.MemberName = memberName; + } + public string MemberName { get; private set; } + + public Type MemberType { get; set; } + public override IEnumerable GetData(MethodInfo testMethod) { - TypeInfo type = testMethod.GetParameters().First().ParameterType.GetTypeInfo(); - if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(TestImageProvider<>)) + IEnumerable addedRows = Enumerable.Empty(); + if (!string.IsNullOrWhiteSpace(this.MemberName)) + { + Type type = this.MemberType ?? testMethod.DeclaringType; + Func accessor = GetPropertyAccessor(type) ?? GetFieldAccessor(type);// ?? GetMethodAccessor(type); + + if (accessor != null) + { + object obj = accessor(); + if (obj is IEnumerable memberItems) + { + addedRows = memberItems.Select(x => x as object[]); + if (addedRows.Any(x => x == null)) + { + throw new ArgumentException($"Property {MemberName} on {MemberType ?? testMethod.DeclaringType} yielded an item that is not an object[]"); + } + } + } + } + + if (!addedRows.Any()) { - yield return this.AdditionalParameters; + addedRows = new[] { new object[0] }; + } + + bool firstIsprovider = FirstIsProvider(testMethod); + IEnumerable dataItems = Enumerable.Empty(); + if (firstIsprovider) + { + return InnerGetData(testMethod, addedRows); } else { - foreach (KeyValuePair kv in this.PixelTypes.ExpandAllTypes()) - { - Type factoryType = typeof(TestImageProvider<>).MakeGenericType(kv.Value); + return addedRows.Select(x => x.Concat(this.AdditionalParameters).ToArray()); + } + } + + private bool FirstIsProvider(MethodInfo testMethod) + { + TypeInfo dataType = testMethod.GetParameters().First().ParameterType.GetTypeInfo(); + return dataType.IsGenericType && dataType.GetGenericTypeDefinition() == typeof(TestImageProvider<>); + } + + private IEnumerable InnerGetData(MethodInfo testMethod, IEnumerable memberData) + { + foreach (KeyValuePair kv in this.PixelTypes.ExpandAllTypes()) + { + Type factoryType = typeof(TestImageProvider<>).MakeGenericType(kv.Value); - foreach (object[] originalFacoryMethodArgs in this.GetAllFactoryMethodArgs(testMethod, factoryType)) + foreach (object[] originalFacoryMethodArgs in this.GetAllFactoryMethodArgs(testMethod, factoryType)) + { + foreach (object[] row in memberData) { object[] actualFactoryMethodArgs = new object[originalFacoryMethodArgs.Length + 2]; Array.Copy(originalFacoryMethodArgs, actualFactoryMethodArgs, originalFacoryMethodArgs.Length); @@ -50,9 +96,10 @@ namespace ImageSharp.Tests object factory = factoryType.GetMethod(this.GetFactoryMethodName(testMethod)) .Invoke(null, actualFactoryMethodArgs); - object[] result = new object[this.AdditionalParameters.Length + 1]; + object[] result = new object[this.AdditionalParameters.Length + 1 + row.Length]; result[0] = factory; - Array.Copy(this.AdditionalParameters, 0, result, 1, this.AdditionalParameters.Length); + Array.Copy(row, 0, result, 1, row.Length); + Array.Copy(this.AdditionalParameters, 0, result, 1 + row.Length, this.AdditionalParameters.Length); yield return result; } } @@ -71,5 +118,56 @@ namespace ImageSharp.Tests } protected abstract string GetFactoryMethodName(MethodInfo testMethod); + + Func GetFieldAccessor(Type type) + { + FieldInfo fieldInfo = null; + for (Type reflectionType = type; reflectionType != null; reflectionType = reflectionType.GetTypeInfo().BaseType) + { + fieldInfo = reflectionType.GetRuntimeField(MemberName); + if (fieldInfo != null) + break; + } + + if (fieldInfo == null || !fieldInfo.IsStatic) + return null; + + return () => fieldInfo.GetValue(null); + } + + //Func GetMethodAccessor(Type type) + //{ + // MethodInfo methodInfo = null; + // var parameterTypes = Parameters == null ? new Type[0] : Parameters.Select(p => p?.GetType()).ToArray(); + // for (var reflectionType = type; reflectionType != null; reflectionType = reflectionType.GetTypeInfo().BaseType) + // { + // methodInfo = reflectionType.GetRuntimeMethods() + // .FirstOrDefault(m => m.Name == MemberName && ParameterTypesCompatible(m.GetParameters(), parameterTypes)); + // if (methodInfo != null) + // break; + // } + + // if (methodInfo == null || !methodInfo.IsStatic) + // return null; + + // return () => methodInfo.Invoke(null, Parameters); + //} + + Func GetPropertyAccessor(Type type) + { + PropertyInfo propInfo = null; + for (Type reflectionType = type; reflectionType != null; reflectionType = reflectionType.GetTypeInfo().BaseType) + { + propInfo = reflectionType.GetRuntimeProperty(MemberName); + if (propInfo != null) + break; + } + + if (propInfo == null || propInfo.GetMethod == null || !propInfo.GetMethod.IsStatic) + return null; + + return () => propInfo.GetValue(null, null); + } + } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs index e1f8f4c551..32278d7558 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs @@ -9,20 +9,34 @@ namespace ImageSharp.Tests using System.Reflection; /// - /// Triggers passing instances which produce a blank image of size width * height. - /// One instance will be passed for each the pixel format defined by the pixelTypes parameter + /// Triggers passing instances which produce a blank image of size width * height. + /// One instance will be passed for each the pixel format defined by the pixelTypes parameter /// public class WithBlankImagesAttribute : ImageDataAttributeBase { /// - /// Triggers passing an that produces a blank image of size width * height + /// Triggers passing an that produces a blank image of size width * height /// /// The required width /// The required height /// The requested parameter /// Additional theory parameter values public WithBlankImagesAttribute(int width, int height, PixelTypes pixelTypes, params object[] additionalParameters) - : base(pixelTypes, additionalParameters) + : base(null, pixelTypes, additionalParameters) + { + this.Width = width; + this.Height = height; + } + + /// + /// Triggers passing an that produces a blank image of size width * height + /// + /// The required width + /// The required height + /// The requested parameter + /// Additional theory parameter values + public WithBlankImagesAttribute(string memberData, int width, int height, PixelTypes pixelTypes, params object[] additionalParameters) + : base(memberData, pixelTypes, additionalParameters) { this.Width = width; this.Height = height; diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs index 617a9a2374..ec8421853e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs @@ -9,22 +9,35 @@ namespace ImageSharp.Tests using System.Reflection; /// - /// Triggers passing instances which read an image from the given file - /// One instance will be passed for each the pixel format defined by the pixelTypes parameter + /// Triggers passing instances which read an image from the given file + /// One instance will be passed for each the pixel format defined by the pixelTypes parameter /// public class WithFileAttribute : ImageDataAttributeBase { private readonly string fileName; /// - /// Triggers passing instances which read an image from the given file - /// One instance will be passed for each the pixel format defined by the pixelTypes parameter + /// Triggers passing instances which read an image from the given file + /// One instance will be passed for each the pixel format defined by the pixelTypes parameter /// /// The name of the file /// The requested pixel types /// Additional theory parameter values public WithFileAttribute(string fileName, PixelTypes pixelTypes, params object[] additionalParameters) - : base(pixelTypes, additionalParameters) + : base(null, pixelTypes, additionalParameters) + { + this.fileName = fileName; + } + + /// + /// Triggers passing instances which read an image from the given file + /// One instance will be passed for each the pixel format defined by the pixelTypes parameter + /// + /// The name of the file + /// The requested pixel types + /// Additional theory parameter values + public WithFileAttribute(string fileName, string dataMemberName, PixelTypes pixelTypes, params object[] additionalParameters) + : base(dataMemberName, pixelTypes, additionalParameters) { this.fileName = fileName; } diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs index be0fa7b3f9..df8f8d0909 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs @@ -11,16 +11,16 @@ namespace ImageSharp.Tests using System.Reflection; /// - /// Triggers passing instances which read an image for each file being enumerated by the (static) test class field/property defined by enumeratorMemberName - /// instances will be passed for each the pixel format defined by the pixelTypes parameter + /// Triggers passing instances which read an image for each file being enumerated by the (static) test class field/property defined by enumeratorMemberName + /// instances will be passed for each the pixel format defined by the pixelTypes parameter /// public class WithFileCollectionAttribute : ImageDataAttributeBase { private readonly string enumeratorMemberName; /// - /// Triggers passing instances which read an image for each file being enumerated by the (static) test class field/property defined by enumeratorMemberName - /// instances will be passed for each the pixel format defined by the pixelTypes parameter + /// Triggers passing instances which read an image for each file being enumerated by the (static) test class field/property defined by enumeratorMemberName + /// instances will be passed for each the pixel format defined by the pixelTypes parameter /// /// The name of the static test class field/property enumerating the files /// The requested pixel types @@ -29,7 +29,24 @@ namespace ImageSharp.Tests string enumeratorMemberName, PixelTypes pixelTypes, params object[] additionalParameters) - : base(pixelTypes, additionalParameters) + : base(null, pixelTypes, additionalParameters) + { + this.enumeratorMemberName = enumeratorMemberName; + } + + /// + /// Triggers passing instances which read an image for each file being enumerated by the (static) test class field/property defined by enumeratorMemberName + /// instances will be passed for each the pixel format defined by the pixelTypes parameter + /// + /// The name of the static test class field/property enumerating the files + /// The requested pixel types + /// Additional theory parameter values + public WithFileCollectionAttribute( + string enumeratorMemberName, + string DataMemberName, + PixelTypes pixelTypes, + params object[] additionalParameters) + : base(DataMemberName, pixelTypes, additionalParameters) { this.enumeratorMemberName = enumeratorMemberName; } diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs index fa5e57dd09..6c6198c38d 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs @@ -10,23 +10,23 @@ namespace ImageSharp.Tests using System.Reflection; /// - /// Triggers passing instances which return the image produced by the given test class member method - /// instances will be passed for each the pixel format defined by the pixelTypes parameter - /// The parameter of the factory method must be a instance + /// Triggers passing instances which return the image produced by the given test class member method + /// instances will be passed for each the pixel format defined by the pixelTypes parameter + /// The parameter of the factory method must be a instance /// public class WithMemberFactoryAttribute : ImageDataAttributeBase { private readonly string memberMethodName; /// - /// Triggers passing instances which return the image produced by the given test class member method - /// instances will be passed for each the pixel format defined by the pixelTypes parameter + /// Triggers passing instances which return the image produced by the given test class member method + /// instances will be passed for each the pixel format defined by the pixelTypes parameter /// /// The name of the static test class which returns the image /// The requested pixel types /// Additional theory parameter values public WithMemberFactoryAttribute(string memberMethodName, PixelTypes pixelTypes, params object[] additionalParameters) - : base(pixelTypes, additionalParameters) + : base(null, pixelTypes, additionalParameters) { this.memberMethodName = memberMethodName; } diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs index d225f8a776..9a8538e78b 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithSolidFilledImagesAttribute.cs @@ -9,14 +9,14 @@ namespace ImageSharp.Tests using System.Reflection; /// - /// Triggers passing instances which produce an image of size width * height filled with the requested color. - /// One instance will be passed for each the pixel format defined by the pixelTypes parameter + /// Triggers passing instances which produce an image of size width * height filled with the requested color. + /// One instance will be passed for each the pixel format defined by the pixelTypes parameter /// public class WithSolidFilledImagesAttribute : WithBlankImagesAttribute { /// - /// Triggers passing instances which produce an image of size width * height filled with the requested color. - /// One instance will be passed for each the pixel format defined by the pixelTypes parameter + /// Triggers passing instances which produce an image of size width * height filled with the requested color. + /// One instance will be passed for each the pixel format defined by the pixelTypes parameter /// /// The width of the requested image /// The height of the requested image @@ -38,8 +38,8 @@ namespace ImageSharp.Tests } /// - /// Triggers passing instances which produce an image of size width * height filled with the requested color. - /// One instance will be passed for each the pixel format defined by the pixelTypes parameter + /// Triggers passing instances which produce an image of size width * height filled with the requested color. + /// One instance will be passed for each the pixel format defined by the pixelTypes parameter /// /// The width of the requested image /// The height of the requested image diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs index 98bc45f5b2..77c60a9433 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs @@ -9,20 +9,32 @@ namespace ImageSharp.Tests using System.Reflection; /// - /// Triggers passing instances which produce a blank image of size width * height. - /// One instance will be passed for each the pixel format defined by the pixelTypes parameter + /// Triggers passing instances which produce a blank image of size width * height. + /// One instance will be passed for each the pixel format defined by the pixelTypes parameter /// public class WithTestPatternImagesAttribute : ImageDataAttributeBase { /// - /// Triggers passing an that produces a test pattern image of size width * height + /// Triggers passing an that produces a test pattern image of size width * height /// /// The required width /// The required height /// The requested parameter /// Additional theory parameter values public WithTestPatternImagesAttribute(int width, int height, PixelTypes pixelTypes, params object[] additionalParameters) - : base(pixelTypes, additionalParameters) + : this(null, width, height, pixelTypes,additionalParameters) + { + } + + /// + /// Triggers passing an that produces a test pattern image of size width * height + /// + /// The required width + /// The required height + /// The requested parameter + /// Additional theory parameter values + public WithTestPatternImagesAttribute(string memberData, int width, int height, PixelTypes pixelTypes, params object[] additionalParameters) + : base(memberData, pixelTypes, additionalParameters) { this.Width = width; this.Height = height; diff --git a/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs b/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs index c2fe0dc5c2..4a0950788d 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Factories/GenericFactory.cs @@ -7,26 +7,28 @@ namespace ImageSharp.Tests { using System; + using ImageSharp.PixelFormats; + /// /// Utility class to create specialized subclasses of generic classes (eg. ) /// Used as parameter for -based factory methods /// - public class GenericFactory - where TColor : struct, IPixel + public class GenericFactory + where TPixel : struct, IPixel { - public virtual Image CreateImage(int width, int height) + public virtual Image CreateImage(int width, int height) { - return new Image(width, height); + return new Image(width, height); } - public virtual Image CreateImage(byte[] bytes) + public virtual Image CreateImage(byte[] bytes) { - return Image.Load(bytes); + return Image.Load(bytes); } - public virtual Image CreateImage(Image other) + public virtual Image CreateImage(Image other) { - return new Image(other); + return new Image(other); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs b/tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs index 2361bc01ce..20af430a5f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Factories/ImageFactory.cs @@ -5,16 +5,17 @@ namespace ImageSharp.Tests { - public class ImageFactory : GenericFactory + using ImageSharp.PixelFormats; + + public class ImageFactory : GenericFactory { - public override Image CreateImage(byte[] bytes) => Image.Load(bytes); + public override Image CreateImage(byte[] bytes) => Image.Load(bytes); - public override Image CreateImage(int width, int height) => new Image(width, height); + public override Image CreateImage(int width, int height) => new Image(width, height); - public override Image CreateImage(Image other) + public override Image CreateImage(Image other) { - Image img = (Image)other; - return new Image(img); + return new Image(other); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs index 6dc0d89c52..4252a60b5e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs @@ -6,12 +6,15 @@ namespace ImageSharp.Tests { using System; + + using ImageSharp.PixelFormats; + using Xunit.Abstractions; - public abstract partial class TestImageProvider - where TColor : struct, IPixel + public abstract partial class TestImageProvider + where TPixel : struct, IPixel { - private class BlankProvider : TestImageProvider, IXunitSerializable + private class BlankProvider : TestImageProvider, IXunitSerializable { public BlankProvider(int width, int height) { @@ -30,7 +33,7 @@ namespace ImageSharp.Tests protected int Width { get; private set; } - public override Image GetImage() => this.Factory.CreateImage(this.Width, this.Height); + public override Image GetImage() => this.Factory.CreateImage(this.Width, this.Height); public override void Deserialize(IXunitSerializationInfo info) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index bc18209f32..4217d52b06 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -7,14 +7,17 @@ namespace ImageSharp.Tests { using System; using System.Collections.Concurrent; + + using ImageSharp.PixelFormats; + using Xunit.Abstractions; - public abstract partial class TestImageProvider - where TColor : struct, IPixel + public abstract partial class TestImageProvider + where TPixel : struct, IPixel { - private class FileProvider : TestImageProvider, IXunitSerializable + private class FileProvider : TestImageProvider, IXunitSerializable { - // Need PixelTypes in the dictionary key, because result images of TestImageProvider.FileProvider + // Need PixelTypes in the dictionary key, because result images of TestImageProvider.FileProvider // are shared between PixelTypes.Color & PixelTypes.StandardImageClass private class Key : Tuple { @@ -24,8 +27,8 @@ namespace ImageSharp.Tests } } - private static ConcurrentDictionary> cache = - new ConcurrentDictionary>(); + private static ConcurrentDictionary> cache = + new ConcurrentDictionary>(); private string filePath; @@ -40,11 +43,11 @@ namespace ImageSharp.Tests public override string SourceFileOrDescription => this.filePath; - public override Image GetImage() + public override Image GetImage() { Key key = new Key(this.PixelType, this.filePath); - Image cachedImage = cache.GetOrAdd( + Image cachedImage = cache.GetOrAdd( key, fn => { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs index 9addc8ca6c..30e7a63b50 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/LambdaProvider.cs @@ -7,23 +7,25 @@ namespace ImageSharp.Tests { using System; + using ImageSharp.PixelFormats; + /// - /// Provides instances for parametric unit tests. + /// Provides instances for parametric unit tests. /// - /// The pixel format of the image - public abstract partial class TestImageProvider - where TColor : struct, IPixel + /// The pixel format of the image + public abstract partial class TestImageProvider + where TPixel : struct, IPixel { - private class LambdaProvider : TestImageProvider + private class LambdaProvider : TestImageProvider { - private readonly Func, Image> creator; + private readonly Func, Image> creator; - public LambdaProvider(Func, Image> creator) + public LambdaProvider(Func, Image> creator) { this.creator = creator; } - public override Image GetImage() => this.creator(this.Factory); + public override Image GetImage() => this.creator(this.Factory); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs index 9a67508721..65d55da554 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs @@ -6,14 +6,17 @@ namespace ImageSharp.Tests { using System; + + using ImageSharp.PixelFormats; + using Xunit.Abstractions; /// - /// Provides instances for parametric unit tests. + /// Provides instances for parametric unit tests. /// - /// The pixel format of the image - public abstract partial class TestImageProvider - where TColor : struct, IPixel + /// The pixel format of the image + public abstract partial class TestImageProvider + where TPixel : struct, IPixel { private class SolidProvider : BlankProvider { @@ -46,10 +49,10 @@ namespace ImageSharp.Tests public override string SourceFileOrDescription => $"Solid{this.Width}x{this.Height}_({this.r},{this.g},{this.b},{this.a})"; - public override Image GetImage() + public override Image GetImage() { - Image image = base.GetImage(); - TColor color = default(TColor); + Image image = base.GetImage(); + TPixel color = default(TPixel); color.PackFromBytes(this.r, this.g, this.b, this.a); return image.Fill(color); diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 26192ba1e1..bf30d48474 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -7,16 +7,23 @@ namespace ImageSharp.Tests { using System; using System.Reflection; - using Xunit.Abstractions; + using ImageSharp.PixelFormats; + + using Xunit.Abstractions; + public interface ITestImageProvider + { + PixelTypes PixelType { get; } + ImagingTestCaseUtility Utility { get; } + } /// - /// Provides instances for parametric unit tests. + /// Provides instances for parametric unit tests. /// - /// The pixel format of the image - public abstract partial class TestImageProvider - where TColor : struct, IPixel + /// The pixel format of the image + public abstract partial class TestImageProvider : ITestImageProvider + where TPixel : struct, IPixel { - public PixelTypes PixelType { get; private set; } = typeof(TColor).GetPixelType(); + public PixelTypes PixelType { get; private set; } = typeof(TPixel).GetPixelType(); public virtual string SourceFileOrDescription => ""; @@ -25,25 +32,25 @@ namespace ImageSharp.Tests /// public ImagingTestCaseUtility Utility { get; private set; } - public GenericFactory Factory { get; private set; } = new GenericFactory(); + public GenericFactory Factory { get; private set; } = new GenericFactory(); public string TypeName { get; private set; } public string MethodName { get; private set; } - public static TestImageProvider TestPattern( + public static TestImageProvider TestPattern( int width, int height, MethodInfo testMethod = null, PixelTypes pixelTypeOverride = PixelTypes.Undefined) => new TestPatternProvider(width, height).Init(testMethod, pixelTypeOverride); - public static TestImageProvider Blank( + public static TestImageProvider Blank( int width, int height, MethodInfo testMethod = null, PixelTypes pixelTypeOverride = PixelTypes.Undefined) => new BlankProvider(width, height).Init(testMethod, pixelTypeOverride); - public static TestImageProvider File( + public static TestImageProvider File( string filePath, MethodInfo testMethod = null, PixelTypes pixelTypeOverride = PixelTypes.Undefined) @@ -51,13 +58,13 @@ namespace ImageSharp.Tests return new FileProvider(filePath).Init(testMethod, pixelTypeOverride); } - public static TestImageProvider Lambda( - Func, Image> func, + public static TestImageProvider Lambda( + Func, Image> func, MethodInfo testMethod = null, PixelTypes pixelTypeOverride = PixelTypes.Undefined) => new LambdaProvider(func).Init(testMethod, pixelTypeOverride); - public static TestImageProvider Solid( + public static TestImageProvider Solid( int width, int height, byte r, @@ -71,9 +78,9 @@ namespace ImageSharp.Tests } /// - /// Returns an instance to the test case with the necessary traits. + /// Returns an instance to the test case with the necessary traits. /// - public abstract Image GetImage(); + public abstract Image GetImage(); public virtual void Deserialize(IXunitSerializationInfo info) { @@ -91,7 +98,7 @@ namespace ImageSharp.Tests info.AddValue("MethodName", this.MethodName); } - protected TestImageProvider Init(string typeName, string methodName, PixelTypes pixelTypeOverride) + protected TestImageProvider Init(string typeName, string methodName, PixelTypes pixelTypeOverride) { if (pixelTypeOverride != PixelTypes.Undefined) { @@ -102,7 +109,7 @@ namespace ImageSharp.Tests if (pixelTypeOverride == PixelTypes.StandardImageClass) { - this.Factory = new ImageFactory() as GenericFactory; + this.Factory = new ImageFactory() as GenericFactory; } this.Utility = new ImagingTestCaseUtility() @@ -119,7 +126,7 @@ namespace ImageSharp.Tests return this; } - protected TestImageProvider Init(MethodInfo testMethod, PixelTypes pixelTypeOverride) + protected TestImageProvider Init(MethodInfo testMethod, PixelTypes pixelTypeOverride) { return Init(testMethod?.DeclaringType.Name, testMethod?.Name, pixelTypeOverride); } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index 39ce614956..96d38fc401 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -8,19 +8,22 @@ namespace ImageSharp.Tests using System; using System.Collections.Generic; using System.Numerics; + + using ImageSharp.PixelFormats; + using Xunit.Abstractions; - public abstract partial class TestImageProvider - where TColor : struct, IPixel + public abstract partial class TestImageProvider + where TPixel : struct, IPixel { /// /// A test image provider that produces test patterns. /// - /// + /// private class TestPatternProvider : BlankProvider { - static Dictionary> testImages = new Dictionary>(); + static Dictionary> testImages = new Dictionary>(); public TestPatternProvider(int width, int height) : base(width, height) @@ -34,51 +37,51 @@ namespace ImageSharp.Tests public override string SourceFileOrDescription => $"TestPattern{this.Width}x{this.Height}"; - public override Image GetImage() + public override Image GetImage() { lock (testImages) { if (!testImages.ContainsKey(this.SourceFileOrDescription)) { - Image image = new Image(this.Width, this.Height); + Image image = new Image(this.Width, this.Height); DrawTestPattern(image); testImages.Add(this.SourceFileOrDescription, image); } } - return new Image(testImages[this.SourceFileOrDescription]); + return new Image(testImages[this.SourceFileOrDescription]); } /// /// Draws the test pattern on an image by drawing 4 other patterns in the for quadrants of the image. /// /// - private static void DrawTestPattern(Image image) + private static void DrawTestPattern(Image image) { // first lets split the image into 4 quadrants - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { BlackWhiteChecker(pixels); // top left VirticalBars(pixels); // top right TransparentGradients(pixels); // bottom left - Rainbow(pixels); // bottom right + Rainbow(pixels); // bottom right } } /// /// Fills the top right quadrant with alternating solid vertical bars. /// /// - private static void VirticalBars(PixelAccessor pixels) + private static void VirticalBars(PixelAccessor pixels) { - // topLeft + // topLeft int left = pixels.Width / 2; int right = pixels.Width; int top = 0; int bottom = pixels.Height / 2; int stride = pixels.Width / 12; - TColor[] c = { - NamedColors.HotPink, - NamedColors.Blue + TPixel[] c = { + NamedColors.HotPink, + NamedColors.Blue }; int p = 0; for (int y = top; y < bottom; y++) @@ -99,17 +102,17 @@ namespace ImageSharp.Tests /// fills the top left quadrant with a black and white checker board. /// /// - private static void BlackWhiteChecker(PixelAccessor pixels) + private static void BlackWhiteChecker(PixelAccessor pixels) { - // topLeft + // topLeft int left = 0; int right = pixels.Width / 2; int top = 0; int bottom = pixels.Height / 2; int stride = pixels.Width / 6; - TColor[] c = { - NamedColors.Black, - NamedColors.White + TPixel[] c = { + NamedColors.Black, + NamedColors.White }; int p = 0; @@ -138,20 +141,20 @@ namespace ImageSharp.Tests /// Fills the bottom left quadrent with 3 horizental bars in Red, Green and Blue with a alpha gradient from left (transparent) to right (solid). /// /// - private static void TransparentGradients(PixelAccessor pixels) + private static void TransparentGradients(PixelAccessor pixels) { - // topLeft + // topLeft int left = 0; int right = pixels.Width / 2; int top = pixels.Height / 2; int bottom = pixels.Height; int height = (int)Math.Ceiling(pixels.Height / 6f); - Vector4 red = Color.Red.ToVector4(); // use real color so we can see har it translates in the test pattern - Vector4 green = Color.Green.ToVector4(); // use real color so we can see har it translates in the test pattern - Vector4 blue = Color.Blue.ToVector4(); // use real color so we can see har it translates in the test pattern + Vector4 red = Rgba32.Red.ToVector4(); // use real color so we can see har it translates in the test pattern + Vector4 green = Rgba32.Green.ToVector4(); // use real color so we can see har it translates in the test pattern + Vector4 blue = Rgba32.Blue.ToVector4(); // use real color so we can see har it translates in the test pattern - TColor c = default(TColor); + TPixel c = default(TPixel); for (int x = left; x < right; x++) { @@ -183,7 +186,7 @@ namespace ImageSharp.Tests /// A better algorithm could be used but it works /// /// - private static void Rainbow(PixelAccessor pixels) + private static void Rainbow(PixelAccessor pixels) { int left = pixels.Width / 2; int right = pixels.Width; @@ -192,8 +195,8 @@ namespace ImageSharp.Tests int pixelCount = left * top; uint stepsPerPixel = (uint)(uint.MaxValue / pixelCount); - TColor c = default(TColor); - Color t = new Color(0); + TPixel c = default(TPixel); + Rgba32 t = new Rgba32(0); for (int x = left; x < right; x++) for (int y = top; y < bottom; y++) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index 9fd33d90b6..2901238856 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -11,6 +11,7 @@ namespace ImageSharp.Tests using System.Reflection; using ImageSharp.Formats; + using ImageSharp.PixelFormats; /// /// Utility class to provide information about the test image & the test case for the test code, @@ -19,12 +20,12 @@ namespace ImageSharp.Tests public class ImagingTestCaseUtility : TestBase { /// - /// Name of the TColor in the owner + /// Name of the TPixel in the owner /// public string PixelTypeName { get; set; } = string.Empty; /// - /// The name of the file which is provided by + /// The name of the file which is provided by /// Or a short string describing the image in the case of a non-file based image provider. /// public string SourceFileOrDescription { get; set; } = string.Empty; @@ -91,15 +92,15 @@ namespace ImageSharp.Tests /// /// Encodes image by the format matching the required extension, than saves it to the recommended output file. /// - /// The pixel format of the image + /// The pixel format of the image /// The image instance /// The requested extension /// Optional encoder /// Optional encoder options - public void SaveTestOutputFile(Image image, string extension = null, IImageEncoder encoder = null, IEncoderOptions options = null) - where TColor : struct, IPixel + public void SaveTestOutputFile(Image image, string extension = null, IImageEncoder encoder = null, IEncoderOptions options = null, string tag = null) + where TPixel : struct, IPixel { - string path = this.GetTestOutputFileName(extension); + string path = this.GetTestOutputFileName(extension: extension, tag:tag); extension = Path.GetExtension(path); IImageFormat format = GetImageFormatByExtension(extension); diff --git a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs index 489ef970af..f64b772717 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Tests /// /// Flags that are mapped to PackedPixel types. - /// They trigger the desired parametrization for . + /// They trigger the desired parametrization for . /// [Flags] public enum PixelTypes : uint @@ -18,7 +18,7 @@ namespace ImageSharp.Tests Alpha8 = 1 << 0, - Argb = 1 << 1, + Argb32 = 1 << 1, Bgr565 = 1 << 2, @@ -26,32 +26,34 @@ namespace ImageSharp.Tests Byte4 = 1 << 4, - Color = 1 << 5, + HalfSingle = 1 << 5, - HalfSingle = 1 << 6, + HalfVector2 = 1 << 6, - HalfVector2 = 1 << 7, + HalfVector4 = 1 << 7, - HalfVector4 = 1 << 8, + NormalizedByte2 = 1 << 8, - NormalizedByte2 = 1 << 9, + NormalizedByte4 = 1 << 9, - NormalizedByte4 = 1 << 10, + NormalizedShort4 = 1 << 10, - NormalizedShort4 = 1 << 11, + Rg32 = 1 << 11, - Rg32 = 1 << 12, + Rgba1010102 = 1 << 12, - Rgba1010102 = 1 << 13, + Rgba32 = 1 << 13, Rgba64 = 1 << 14, - Short2 = 1 << 15, + RgbaVector = 1 << 15, - Short4 = 1 << 16, + Short2 = 1 << 16, + + Short4 = 1 << 17, /// - /// Triggers instantiating the subclass of + /// Triggers instantiating the subclass of /// StandardImageClass = 1 << 29, diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index e2bc2bd2d7..dbd316423a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -3,17 +3,32 @@ namespace ImageSharp.Tests { using System; using System.Collections.Generic; + using System.Linq; + using System.Reflection; using System.Text; + using ImageSharp.PixelFormats; + public static class TestImageExtensions { - public static void DebugSave(this Image img, TestImageProvider provider, string extension = "png") - where TColor : struct, IPixel + public static void DebugSave(this Image img, ITestImageProvider provider, object settings = null, string extension = "png") + where TPixel : struct, IPixel { + string tag = null; + if (settings is string) + { + tag = (string)settings; + } + else if (settings != null) + { + var properties = settings.GetType().GetRuntimeProperties(); + + tag = string.Join("_", properties.ToDictionary(x => x.Name, x => x.GetValue(settings)).Select(x => $"{x.Key}-{x.Value}")); + } if(!bool.TryParse(Environment.GetEnvironmentVariable("CI"), out bool isCI) || !isCI) { // we are running locally then we want to save it out - provider.Utility.SaveTestOutputFile(img, extension); + provider.Utility.SaveTestOutputFile(img, extension, tag: tag); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs new file mode 100644 index 0000000000..852bc587d0 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ImageSharp.PixelFormats; +using Xunit.Abstractions; + +namespace ImageSharp.Tests.TestUtilities +{ + public class TestPixel : IXunitSerializable + where TPixel : struct, IPixel + { + public TestPixel() + { + } + + public TestPixel(float red, float green, float blue, float alpha) + { + this.Red = red; + this.Green = green; + this.Blue = blue; + this.Alpha = alpha; + } + + public float Red { get; set; } + public float Green { get; set; } + public float Blue { get; set; } + public float Alpha { get; set; } + + public static implicit operator TPixel(TestPixel d) + { + return d?.AsPixel() ?? default(TPixel); + } + + public TPixel AsPixel() + { + TPixel pix = default(TPixel); + pix.PackFromVector4(new System.Numerics.Vector4(this.Red, this.Green, this.Blue, this.Alpha)); + return pix; + } + + internal Span AsSpan() + { + return new Span(new[] { AsPixel() }); + } + + public void Deserialize(IXunitSerializationInfo info) + { + this.Red = info.GetValue("red"); + this.Green = info.GetValue("green"); + this.Blue = info.GetValue("blue"); + this.Alpha = info.GetValue("alpha"); + } + + public void Serialize(IXunitSerializationInfo info) + { + info.AddValue("red", this.Red); + info.AddValue("green", this.Green); + info.AddValue("blue", this.Blue); + info.AddValue("alpha", this.Alpha); + } + + public override string ToString() + { + return $"{typeof(TPixel).Name}{this.AsPixel().ToString()}"; + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs index 260a677d3d..dfaf1c0528 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs @@ -11,6 +11,8 @@ namespace ImageSharp.Tests using System.Linq; using System.Reflection; + using ImageSharp.PixelFormats; + /// /// Extension methods for TestUtilities /// @@ -18,37 +20,38 @@ namespace ImageSharp.Tests { private static readonly Dictionary ClrTypes2PixelTypes = new Dictionary(); - private static readonly Assembly ImageSharpAssembly = typeof(Color).GetTypeInfo().Assembly; + private static readonly Assembly ImageSharpAssembly = typeof(Rgba32).GetTypeInfo().Assembly; private static readonly Dictionary PixelTypes2ClrTypes = new Dictionary(); private static readonly PixelTypes[] AllConcretePixelTypes = GetAllPixelTypes() - .Except(new [] {PixelTypes.Undefined, PixelTypes.All }) + .Except(new[] { PixelTypes.Undefined, PixelTypes.All }) .ToArray(); static TestUtilityExtensions() { - string nameSpace = typeof(Color).FullName; - nameSpace = nameSpace.Substring(0, nameSpace.Length - typeof(Color).Name.Length - 1); - foreach (PixelTypes pt in AllConcretePixelTypes.Where(pt => pt != PixelTypes.StandardImageClass)) + // Add Rgba32 Our default. + Type defaultPixelFormatType = typeof(Rgba32); + PixelTypes2ClrTypes[PixelTypes.Rgba32] = defaultPixelFormatType; + ClrTypes2PixelTypes[defaultPixelFormatType] = PixelTypes.Rgba32; + + // Add PixelFormat types + string nameSpace = typeof(Alpha8).FullName; + nameSpace = nameSpace.Substring(0, nameSpace.Length - typeof(Alpha8).Name.Length - 1); + foreach (PixelTypes pt in AllConcretePixelTypes.Where(pt => pt != PixelTypes.StandardImageClass && pt != PixelTypes.Rgba32)) { - string typeName = $"{nameSpace}.{pt.ToString()}"; + string typeName = $"{nameSpace}.{pt}"; Type t = ImageSharpAssembly.GetType(typeName); - if (t == null) - { - throw new InvalidOperationException($"Could not find: {typeName}"); - } - - PixelTypes2ClrTypes[pt] = t; + PixelTypes2ClrTypes[pt] = t ?? throw new InvalidOperationException($"Could not find: {typeName}"); ClrTypes2PixelTypes[t] = pt; } - PixelTypes2ClrTypes[PixelTypes.StandardImageClass] = typeof(Color); + PixelTypes2ClrTypes[PixelTypes.StandardImageClass] = defaultPixelFormatType; } public static bool HasFlag(this PixelTypes pixelTypes, PixelTypes flag) => (pixelTypes & flag) == flag; - public static bool IsEquivalentTo(this Image a, Image b, bool compareAlpha = true) - where TColor : struct, IPixel + public static bool IsEquivalentTo(this Image a, Image b, bool compareAlpha = true) + where TPixel : struct, IPixel { if (a.Width != b.Width || a.Height != b.Height) { @@ -58,16 +61,16 @@ namespace ImageSharp.Tests byte[] bytesA = new byte[3]; byte[] bytesB = new byte[3]; - using (PixelAccessor pixA = a.Lock()) + using (PixelAccessor pixA = a.Lock()) { - using (PixelAccessor pixB = b.Lock()) + using (PixelAccessor pixB = b.Lock()) { for (int y = 0; y < a.Height; y++) { for (int x = 0; x < a.Width; x++) { - TColor ca = pixA[x, y]; - TColor cb = pixB[x, y]; + TPixel ca = pixA[x, y]; + TPixel cb = pixB[x, y]; if (compareAlpha) { diff --git a/tests/ImageSharp.Tests/TestUtilities/TestVector4.cs b/tests/ImageSharp.Tests/TestUtilities/TestVector4.cs new file mode 100644 index 0000000000..beb3fcd970 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/TestVector4.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Text; +using ImageSharp.PixelFormats; +using Xunit.Abstractions; + +namespace ImageSharp.Tests.TestUtilities +{ + public class TestVector4 : IXunitSerializable + { + public TestVector4() + { + } + + public TestVector4(float x, float y, float z, float w) + { + this.X = x; + this.Y = y; + this.Z = x; + this.W = w; + } + + public float X { get; set; } + public float Y { get; set; } + public float Z { get; set; } + public float W { get; set; } + + public static implicit operator Vector4(TestVector4 d) + { + return d?.AsVector() ?? default(Vector4); + } + + public Vector4 AsVector() + { + return new Vector4(this.X, this.Y, this.Z, this.W); + } + + public void Deserialize(IXunitSerializationInfo info) + { + this.X = info.GetValue("x"); + this.Y = info.GetValue("y"); + this.Z= info.GetValue("z"); + this.W= info.GetValue("w"); + } + + public void Serialize(IXunitSerializationInfo info) + { + info.AddValue("x", this.X); + info.AddValue("y", this.Y); + info.AddValue("z", this.Z); + info.AddValue("w", this.W); + } + + public override string ToString() + { + return $"{this.AsVector().ToString()}"; + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 6760735d1f..4d3a0d9912 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -7,6 +7,8 @@ namespace ImageSharp.Tests { using System; + using ImageSharp.PixelFormats; + using Xunit; using Xunit.Abstractions; @@ -20,11 +22,11 @@ namespace ImageSharp.Tests private ITestOutputHelper Output { get; } [Theory] - [WithBlankImages(42, 666, PixelTypes.Color | PixelTypes.Argb | PixelTypes.HalfSingle, "hello")] - public void Use_WithEmptyImageAttribute(TestImageProvider provider, string message) - where TColor : struct, IPixel + [WithBlankImages(42, 666, PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.HalfSingle, "hello")] + public void Use_WithEmptyImageAttribute(TestImageProvider provider, string message) + where TPixel : struct, IPixel { - Image img = provider.GetImage(); + Image img = provider.GetImage(); Assert.Equal(42, img.Width); Assert.Equal(666, img.Height); @@ -33,12 +35,12 @@ namespace ImageSharp.Tests [Theory] [WithBlankImages(42, 666, PixelTypes.All, "hello")] - public void Use_WithBlankImagesAttribute_WithAllPixelTypes( - TestImageProvider provider, + public void Use_WithBlankImagesAttribute_WithAllPixelTypes( + TestImageProvider provider, string message) - where TColor : struct, IPixel + where TPixel : struct, IPixel { - Image img = provider.GetImage(); + Image img = provider.GetImage(); Assert.Equal(42, img.Width); Assert.Equal(666, img.Height); @@ -46,11 +48,11 @@ namespace ImageSharp.Tests } [Theory] - [WithBlankImages(1, 1, PixelTypes.Color, PixelTypes.Color)] + [WithBlankImages(1, 1, PixelTypes.Rgba32, PixelTypes.Rgba32)] [WithBlankImages(1, 1, PixelTypes.Alpha8, PixelTypes.Alpha8)] [WithBlankImages(1, 1, PixelTypes.StandardImageClass, PixelTypes.StandardImageClass)] - public void PixelType_PropertyValueIsCorrect(TestImageProvider provider, PixelTypes expected) - where TColor : struct, IPixel + public void PixelType_PropertyValueIsCorrect(TestImageProvider provider, PixelTypes expected) + where TPixel : struct, IPixel { Assert.Equal(expected, provider.PixelType); } @@ -58,23 +60,23 @@ namespace ImageSharp.Tests [Theory] [WithBlankImages(1, 1, PixelTypes.StandardImageClass)] [WithFile(TestImages.Bmp.F, PixelTypes.StandardImageClass)] - public void PixelTypes_ColorWithDefaultImageClass_TriggersCreatingTheNonGenericDerivedImageClass( - TestImageProvider provider) - where TColor : struct, IPixel + public void PixelTypes_ColorWithDefaultImageClass_TriggersCreatingTheNonGenericDerivedImageClass( + TestImageProvider provider) + where TPixel : struct, IPixel { - Image img = provider.GetImage(); + Image img = provider.GetImage(); - Assert.IsType(img); + Assert.IsType>(img); } [Theory] [WithFile(TestImages.Bmp.Car, PixelTypes.All, 88)] [WithFile(TestImages.Bmp.F, PixelTypes.All, 88)] - public void Use_WithFileAttribute(TestImageProvider provider, int yo) - where TColor : struct, IPixel + public void Use_WithFileAttribute(TestImageProvider provider, int yo) + where TPixel : struct, IPixel { Assert.NotNull(provider.Utility.SourceFileOrDescription); - Image img = provider.GetImage(); + Image img = provider.GetImage(); Assert.True(img.Width * img.Height > 0); Assert.Equal(88, yo); @@ -86,27 +88,27 @@ namespace ImageSharp.Tests public static string[] AllBmpFiles => TestImages.Bmp.All; [Theory] - [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Color | PixelTypes.Argb)] - public void Use_WithFileCollection(TestImageProvider provider) - where TColor : struct, IPixel + [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32 | PixelTypes.Argb32)] + public void Use_WithFileCollection(TestImageProvider provider) + where TPixel : struct, IPixel { Assert.NotNull(provider.Utility.SourceFileOrDescription); - Image image = provider.GetImage(); + Image image = provider.GetImage(); provider.Utility.SaveTestOutputFile(image, "png"); } [Theory] - [WithSolidFilledImages(10, 20, 255, 100, 50, 200, PixelTypes.Color | PixelTypes.Argb)] - public void Use_WithSolidFilledImagesAttribute(TestImageProvider provider) - where TColor : struct, IPixel + [WithSolidFilledImages(10, 20, 255, 100, 50, 200, PixelTypes.Rgba32 | PixelTypes.Argb32)] + public void Use_WithSolidFilledImagesAttribute(TestImageProvider provider) + where TPixel : struct, IPixel { - Image img = provider.GetImage(); + Image img = provider.GetImage(); Assert.Equal(img.Width, 10); Assert.Equal(img.Height, 20); byte[] colors = new byte[4]; - using (PixelAccessor pixels = img.Lock()) + using (PixelAccessor pixels = img.Lock()) { for (int y = 0; y < pixels.Height; y++) { @@ -124,34 +126,34 @@ namespace ImageSharp.Tests } /// - /// Need to us to create instance of when pixelType is StandardImageClass + /// Need to us to create instance of when pixelType is StandardImageClass /// - /// + /// /// /// - public static Image CreateTestImage(GenericFactory factory) - where TColor : struct, IPixel + public static Image CreateTestImage(GenericFactory factory) + where TPixel : struct, IPixel { return factory.CreateImage(3, 3); } [Theory] [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All)] - public void Use_WithMemberFactoryAttribute(TestImageProvider provider) - where TColor : struct, IPixel + public void Use_WithMemberFactoryAttribute(TestImageProvider provider) + where TPixel : struct, IPixel { - Image img = provider.GetImage(); + Image img = provider.GetImage(); Assert.Equal(img.Width, 3); if (provider.PixelType == PixelTypes.StandardImageClass) { - Assert.IsType(img); + Assert.IsType>(img); } } public static readonly TheoryData BasicData = new TheoryData() { - TestImageProvider.Blank(10, 20), + TestImageProvider.Blank(10, 20), TestImageProvider.Blank( 10, 20), @@ -159,17 +161,17 @@ namespace ImageSharp.Tests [Theory] [MemberData(nameof(BasicData))] - public void Blank_MemberData(TestImageProvider provider) - where TColor : struct, IPixel + public void Blank_MemberData(TestImageProvider provider) + where TPixel : struct, IPixel { - Image img = provider.GetImage(); + Image img = provider.GetImage(); Assert.True(img.Width * img.Height > 0); } public static readonly TheoryData FileData = new TheoryData() { - TestImageProvider.File( + TestImageProvider.File( TestImages.Bmp.Car), TestImageProvider.File( TestImages.Bmp.F) @@ -177,13 +179,13 @@ namespace ImageSharp.Tests [Theory] [MemberData(nameof(FileData))] - public void File_MemberData(TestImageProvider provider) - where TColor : struct, IPixel + public void File_MemberData(TestImageProvider provider) + where TPixel : struct, IPixel { this.Output.WriteLine("SRC: " + provider.Utility.SourceFileOrDescription); this.Output.WriteLine("OUT: " + provider.Utility.GetTestOutputFileName()); - Image img = provider.GetImage(); + Image img = provider.GetImage(); Assert.True(img.Width * img.Height > 0); } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs index 63c24a157c..9ff0ca64e7 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs @@ -11,6 +11,8 @@ namespace ImageSharp.Tests using System.Numerics; using System.Reflection; + using ImageSharp.PixelFormats; + using Xunit; using Xunit.Abstractions; @@ -23,12 +25,12 @@ namespace ImageSharp.Tests private ITestOutputHelper Output { get; } - public static Image CreateTestImage(GenericFactory factory) - where TColor : struct, IPixel + public static Image CreateTestImage(GenericFactory factory) + where TPixel : struct, IPixel { - Image image = factory.CreateImage(10, 10); + Image image = factory.CreateImage(10, 10); - using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { for (int i = 0; i < 10; i++) { @@ -37,7 +39,7 @@ namespace ImageSharp.Tests Vector4 v = new Vector4(i, j, 0, 1); v /= 10; - TColor color = default(TColor); + TPixel color = default(TPixel); color.PackFromVector4(v); pixels[i, j] = color; @@ -51,51 +53,51 @@ namespace ImageSharp.Tests [Fact] public void Baz() { - Type type = typeof(Color).GetTypeInfo().Assembly.GetType("ImageSharp.Color"); + Type type = typeof(Rgba32).GetTypeInfo().Assembly.GetType("ImageSharp.Rgba32"); this.Output.WriteLine(type.ToString()); - Type fake = typeof(Color).GetTypeInfo().Assembly.GetType("ImageSharp.dsaada_DASqewrr"); + Type fake = typeof(Rgba32).GetTypeInfo().Assembly.GetType("ImageSharp.dsaada_DASqewrr"); Assert.Null(fake); } - + [Theory] - [WithFile(TestImages.Bmp.Car, PixelTypes.Color, true)] - [WithFile(TestImages.Bmp.Car, PixelTypes.Color, false)] - public void IsEquivalentTo_WhenFalse(TestImageProvider provider, bool compareAlpha) - where TColor : struct, IPixel + [WithFile(TestImages.Bmp.Car, PixelTypes.Rgba32, true)] + [WithFile(TestImages.Bmp.Car, PixelTypes.Rgba32, false)] + public void IsEquivalentTo_WhenFalse(TestImageProvider provider, bool compareAlpha) + where TPixel : struct, IPixel { - Image a = provider.GetImage(); - Image b = provider.GetImage(); + Image a = provider.GetImage(); + Image b = provider.GetImage(); b = b.OilPaint(3, 2); Assert.False(a.IsEquivalentTo(b, compareAlpha)); } [Theory] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Color | PixelTypes.Bgr565, true)] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Color | PixelTypes.Bgr565, false)] - public void IsEquivalentTo_WhenTrue(TestImageProvider provider, bool compareAlpha) - where TColor : struct, IPixel + [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Rgba32 | PixelTypes.Bgr565, true)] + [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Rgba32 | PixelTypes.Bgr565, false)] + public void IsEquivalentTo_WhenTrue(TestImageProvider provider, bool compareAlpha) + where TPixel : struct, IPixel { - Image a = provider.GetImage(); - Image b = provider.GetImage(); + Image a = provider.GetImage(); + Image b = provider.GetImage(); Assert.True(a.IsEquivalentTo(b, compareAlpha)); } [Theory] - [InlineData(PixelTypes.Color, typeof(Color))] - [InlineData(PixelTypes.Argb, typeof(Argb))] + [InlineData(PixelTypes.Rgba32, typeof(Rgba32))] + [InlineData(PixelTypes.Argb32, typeof(Argb32))] [InlineData(PixelTypes.HalfVector4, typeof(HalfVector4))] - [InlineData(PixelTypes.StandardImageClass, typeof(Color))] + [InlineData(PixelTypes.StandardImageClass, typeof(Rgba32))] public void ToType(PixelTypes pt, Type expectedType) { Assert.Equal(pt.ToType(), expectedType); } [Theory] - [InlineData(typeof(Color), PixelTypes.Color)] - [InlineData(typeof(Argb), PixelTypes.Argb)] + [InlineData(typeof(Rgba32), PixelTypes.Rgba32)] + [InlineData(typeof(Argb32), PixelTypes.Argb32)] public void GetPixelType(Type clrType, PixelTypes expectedPixelType) { Assert.Equal(expectedPixelType, clrType.GetPixelType()); @@ -112,7 +114,7 @@ namespace ImageSharp.Tests [Fact] public void ToTypes() { - PixelTypes pixelTypes = PixelTypes.Alpha8 | PixelTypes.Bgr565 | PixelTypes.Color | PixelTypes.HalfVector2 | PixelTypes.StandardImageClass; + PixelTypes pixelTypes = PixelTypes.Alpha8 | PixelTypes.Bgr565 | PixelTypes.Rgba32 | PixelTypes.HalfVector2 | PixelTypes.StandardImageClass; IEnumerable> expanded = pixelTypes.ExpandAllTypes(); @@ -120,9 +122,9 @@ namespace ImageSharp.Tests AssertContainsPixelType(PixelTypes.Alpha8, expanded); AssertContainsPixelType(PixelTypes.Bgr565, expanded); - AssertContainsPixelType(PixelTypes.Color, expanded); + AssertContainsPixelType(PixelTypes.Rgba32, expanded); AssertContainsPixelType(PixelTypes.HalfVector2, expanded); - AssertContainsPixelType(PixelTypes.StandardImageClass, expanded); + AssertContainsPixelType(PixelTypes.StandardImageClass, expanded); } [Fact] @@ -131,8 +133,8 @@ namespace ImageSharp.Tests KeyValuePair[] expanded = PixelTypes.All.ExpandAllTypes().ToArray(); Assert.True(expanded.Length >= TestUtilityExtensions.GetAllPixelTypes().Length - 2); - AssertContainsPixelType(PixelTypes.Color, expanded); - AssertContainsPixelType(PixelTypes.StandardImageClass, expanded); + AssertContainsPixelType(PixelTypes.Rgba32, expanded); + AssertContainsPixelType(PixelTypes.StandardImageClass, expanded); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/VectorAssert.cs b/tests/ImageSharp.Tests/VectorAssert.cs new file mode 100644 index 0000000000..ad26963d43 --- /dev/null +++ b/tests/ImageSharp.Tests/VectorAssert.cs @@ -0,0 +1,98 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +// ReSharper disable MemberHidesStaticFromOuterClass +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Generic; + using System.Numerics; + using ImageSharp; + using ImageSharp.PixelFormats; + using Xunit; + + /// + /// Class to perform simple image comparisons. + /// + public static class VectorAssert + { + public static void Equal(TPixel expected, TPixel actual, int precision = int.MaxValue) + where TPixel : struct, IPixel + { + Equal(expected.ToVector4(), actual.ToVector4(), precision); + } + + public static void Equal(Vector4 expected, Vector4 actual, int precision = int.MaxValue) + { + Assert.Equal(expected, actual, new PrecisionEqualityComparer(precision)); + } + + public static void Equal(Vector3 expected, Vector3 actual, int precision = int.MaxValue) + { + Assert.Equal(expected, actual, new PrecisionEqualityComparer(precision)); + } + + public static void Equal(Vector2 expected, Vector2 actual, int precision = int.MaxValue) + { + Assert.Equal(expected, actual, new PrecisionEqualityComparer(precision)); + } + + private struct PrecisionEqualityComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer + { + private readonly int precision; + + public PrecisionEqualityComparer(int precision) + { + this.precision = precision; + } + + public bool Equals(Vector2 x, Vector2 y) + { + return Equals(x.X, y.X) && + Equals(x.Y, y.Y); + + } + public bool Equals(Vector3 x, Vector3 y) + { + return Equals(x.X, y.X) && + Equals(x.Y, y.Y) && + Equals(x.Z, y.Z); + + } + + public bool Equals(Vector4 x, Vector4 y) + { + return Equals(x.W, y.W) && + Equals(x.X, y.X) && + Equals(x.Y, y.Y) && + Equals(x.Z, y.Z); + + } + + public bool Equals(float x, float y) + { + return Math.Round(x, this.precision) == Math.Round(y, this.precision); + } + + public int GetHashCode(Vector4 obj) + { + return obj.GetHashCode(); + } + public int GetHashCode(Vector3 obj) + { + return obj.GetHashCode(); + } + public int GetHashCode(Vector2 obj) + { + return obj.GetHashCode(); + } + + public int GetHashCode(float obj) + { + return obj.GetHashCode(); + } + } + } +} diff --git a/tests/ImageSharp.Tests/xunit.runner.json b/tests/ImageSharp.Tests/xunit.runner.json new file mode 100644 index 0000000000..df1c3d50d0 --- /dev/null +++ b/tests/ImageSharp.Tests/xunit.runner.json @@ -0,0 +1,3 @@ +{ + "methodDisplay": "method" +} \ No newline at end of file