diff --git a/.vscode/tasks.json b/.vscode/tasks.json index a1a55a389a..4a7b35ac2c 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -16,7 +16,7 @@ { "taskName": "build benchmark", "suppressTaskName": true, - "args": [ "build", "tests/ImageSharp.Benchmarks/project.json", "-f", "netcoreapp1.1", "-c", "Release" ], + "args": [ "build", "tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj", "-f", "netcoreapp1.1", "-c", "Release" ], "showOutput": "always", "problemMatcher": "$msCompile" }, diff --git a/src/ImageSharp.Drawing/Drawable.cs b/src/ImageSharp.Drawing/Drawable.cs deleted file mode 100644 index 62f5e62c14..0000000000 --- a/src/ImageSharp.Drawing/Drawable.cs +++ /dev/null @@ -1,51 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing -{ - /// - /// Represents a path or set of paths that can be drawn as an outline. - /// - public abstract class Drawable - { - /// - /// Gets the maximum number of intersections to could be returned. - /// - public abstract int MaxIntersections { get; } - - /// - /// Gets the bounds. - /// - public abstract Rectangle Bounds { get; } - - /// - /// Gets the point information for the specified x and y location. - /// - /// The x. - /// The y. - /// Information about the point in relation to a drawable edge - public abstract PointInfo GetPointInfo(int x, int y); - - /// - /// Scans the X axis for intersections. - /// - /// The x. - /// The buffer. - /// The length. - /// The offset. - /// The number of intersections found. - public abstract int ScanX(int x, float[] buffer, int length, int offset); - - /// - /// Scans the Y axis for intersections. - /// - /// The position along the y axis to find intersections. - /// The buffer. - /// The length. - /// The offset. - /// The number of intersections found. - public abstract int ScanY(int y, float[] buffer, int length, int offset); - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index a3552a09cc..13500b65af 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -36,11 +36,10 @@ + All - - ..\..\ImageSharp.ruleset diff --git a/src/ImageSharp.Drawing/Paths/DrawPath.cs b/src/ImageSharp.Drawing/Paths/DrawPath.cs index 176539663c..1fba06370d 100644 --- a/src/ImageSharp.Drawing/Paths/DrawPath.cs +++ b/src/ImageSharp.Drawing/Paths/DrawPath.cs @@ -28,7 +28,7 @@ namespace ImageSharp 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); + return source.Fill(pen.StrokeFill, new ShapePath(path, pen), options); } /// diff --git a/src/ImageSharp.Drawing/DrawPath.cs b/src/ImageSharp.Drawing/Paths/DrawPathCollection.cs similarity index 79% rename from src/ImageSharp.Drawing/DrawPath.cs rename to src/ImageSharp.Drawing/Paths/DrawPathCollection.cs index 09d3dbb028..877737653d 100644 --- a/src/ImageSharp.Drawing/DrawPath.cs +++ b/src/ImageSharp.Drawing/Paths/DrawPathCollection.cs @@ -8,8 +8,8 @@ namespace ImageSharp using Drawing; using Drawing.Brushes; using Drawing.Pens; - using Drawing.Processors; using ImageSharp.PixelFormats; + using SixLabors.Shapes; /// /// Extension methods for the type. @@ -17,18 +17,23 @@ namespace ImageSharp public static partial class ImageExtensions { /// - /// Draws the outline of the region with the provided pen. + /// Draws the outline of the polygon with the provided pen. /// /// The type of the color. /// The image this method extends. /// The pen. - /// The path. + /// The paths. /// The options. /// The . - public static Image Draw(this Image source, IPen pen, Drawable path, GraphicsOptions options) + public static Image Draw(this Image source, IPen pen, IPathCollection paths, GraphicsOptions options) where TPixel : struct, IPixel { - return source.Apply(new DrawPathProcessor(pen, path, options)); + foreach (IPath path in paths) + { + source.Draw(pen, path, options); + } + + return source; } /// @@ -37,12 +42,12 @@ namespace ImageSharp /// The type of the color. /// The image this method extends. /// The pen. - /// The path. + /// The paths. /// The . - public static Image Draw(this Image source, IPen pen, Drawable path) + public static Image Draw(this Image source, IPen pen, IPathCollection paths) where TPixel : struct, IPixel { - return source.Draw(pen, path, GraphicsOptions.Default); + return source.Draw(pen, paths, GraphicsOptions.Default); } /// @@ -52,13 +57,13 @@ namespace ImageSharp /// The image this method extends. /// The brush. /// The thickness. - /// The path. + /// The shapes. /// The options. /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, Drawable path, GraphicsOptions options) + public static Image Draw(this Image source, IBrush brush, float thickness, IPathCollection paths, GraphicsOptions options) where TPixel : struct, IPixel { - return source.Draw(new Pen(brush, thickness), path, options); + return source.Draw(new Pen(brush, thickness), paths, options); } /// @@ -68,12 +73,12 @@ namespace ImageSharp /// The image this method extends. /// The brush. /// The thickness. - /// The path. + /// The paths. /// The . - public static Image Draw(this Image source, IBrush brush, float thickness, Drawable path) + public static Image Draw(this Image source, IBrush brush, float thickness, IPathCollection paths) where TPixel : struct, IPixel { - return source.Draw(new Pen(brush, thickness), path); + return source.Draw(new Pen(brush, thickness), paths); } /// @@ -83,13 +88,13 @@ namespace ImageSharp /// The image this method extends. /// The color. /// The thickness. - /// The path. + /// The paths. /// The options. /// The . - public static Image Draw(this Image source, TPixel color, float thickness, Drawable path, GraphicsOptions options) + public static Image Draw(this Image source, TPixel color, float thickness, IPathCollection paths, GraphicsOptions options) where TPixel : struct, IPixel { - return source.Draw(new SolidBrush(color), thickness, path, options); + return source.Draw(new SolidBrush(color), thickness, paths, options); } /// @@ -99,12 +104,12 @@ namespace ImageSharp /// The image this method extends. /// The color. /// The thickness. - /// The path. + /// The paths. /// The . - public static Image Draw(this Image source, TPixel color, float thickness, Drawable path) + public static Image Draw(this Image source, TPixel color, float thickness, IPathCollection paths) where TPixel : struct, IPixel { - return source.Draw(new SolidBrush(color), thickness, path); + return source.Draw(new SolidBrush(color), thickness, paths); } } } diff --git a/src/ImageSharp.Drawing/Paths/FillPathCollection.cs b/src/ImageSharp.Drawing/Paths/FillPathCollection.cs new file mode 100644 index 0000000000..3ea9fb94b8 --- /dev/null +++ b/src/ImageSharp.Drawing/Paths/FillPathCollection.cs @@ -0,0 +1,81 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using Drawing; + using Drawing.Brushes; + using ImageSharp.PixelFormats; + using SixLabors.Shapes; + + /// + /// 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 image this method extends. + /// The brush. + /// The shapes. + /// The graphics options. + /// The . + public static Image Fill(this Image source, IBrush brush, IPathCollection paths, GraphicsOptions options) + where TPixel : struct, IPixel + { + foreach (IPath s in paths) + { + source.Fill(brush, s, options); + } + + return source; + } + + /// + /// Flood fills the image in the shape of the provided polygon with the specified brush. + /// + /// The type of the color. + /// The image this method extends. + /// The brush. + /// The paths. + /// The . + public static Image Fill(this Image source, IBrush brush, IPathCollection paths) + where TPixel : struct, IPixel + { + return source.Fill(brush, paths, GraphicsOptions.Default); + } + + /// + /// Flood fills the image in the shape of the provided polygon with the specified brush.. + /// + /// The type of the color. + /// The image this method extends. + /// The color. + /// The paths. + /// The options. + /// The . + public static Image Fill(this Image source, TPixel color, IPathCollection paths, GraphicsOptions options) + where TPixel : struct, IPixel + { + return source.Fill(new SolidBrush(color), paths, options); + } + + /// + /// Flood fills the image in the shape of the provided polygon with the specified brush.. + /// + /// The type of the color. + /// The image this method extends. + /// The color. + /// The paths. + /// The . + public static Image Fill(this Image source, TPixel color, IPathCollection paths) + where TPixel : struct, IPixel + { + return source.Fill(new SolidBrush(color), paths); + } + } +} diff --git a/src/ImageSharp.Drawing/Paths/ShapePath.cs b/src/ImageSharp.Drawing/Paths/ShapePath.cs index d0376ccc1e..63c814f2d5 100644 --- a/src/ImageSharp.Drawing/Paths/ShapePath.cs +++ b/src/ImageSharp.Drawing/Paths/ShapePath.cs @@ -1,12 +1,12 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageSharp.Drawing { + using System; using System.Buffers; - using System.Collections.Immutable; using System.Numerics; using SixLabors.Shapes; @@ -14,91 +14,19 @@ namespace ImageSharp.Drawing using Rectangle = ImageSharp.Rectangle; /// - /// A drawable mapping between a and a drawable region. + /// A mapping between a and a region. /// - internal class ShapePath : Drawable + internal class ShapePath : ShapeRegion { /// /// Initializes a new instance of the class. /// - /// The path. - public ShapePath(IPath path) + /// The shape. + /// The pen to apply to the shape. + // SixLabors.shape willbe moving to a Span/ReadOnlySpan based API shortly use ToArray for now. + public ShapePath(IPath shape, Pens.IPen pen) + : base(shape.GenerateOutline(pen.StrokeWidth, pen.StrokePattern.ToArray())) { - this.Path = path; - this.Bounds = path.Bounds.Convert(); - } - - /// - /// Gets the fillable shape - /// - public IPath Path { get; } - - /// - public override int MaxIntersections => this.Path.MaxIntersections; - - /// - public override Rectangle Bounds { get; } - - /// - public override int ScanX(int x, float[] buffer, int length, int offset) - { - Vector2 start = new Vector2(x, this.Bounds.Top - 1); - Vector2 end = new Vector2(x, this.Bounds.Bottom + 1); - Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); - try - { - int count = this.Path.FindIntersections(start, end, innerbuffer, length, 0); - - for (int i = 0; i < count; i++) - { - buffer[i + offset] = innerbuffer[i].Y; - } - - return count; - } - finally - { - ArrayPool.Shared.Return(innerbuffer); - } - } - - /// - public override int ScanY(int y, float[] buffer, int length, int offset) - { - Vector2 start = new Vector2(this.Bounds.Left - 1, y); - Vector2 end = new Vector2(this.Bounds.Right + 1, y); - Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); - try - { - int count = this.Path.FindIntersections(start, end, innerbuffer, length, 0); - - for (int i = 0; i < count; i++) - { - buffer[i + offset] = innerbuffer[i].X; - } - - return count; - } - finally - { - ArrayPool.Shared.Return(innerbuffer); - } - } - - /// - public override PointInfo GetPointInfo(int x, int y) - { - Vector2 point = new Vector2(x, y); - SixLabors.Shapes.PointInfo dist = this.Path.Distance(point); - - return new PointInfo - { - DistanceAlongPath = dist.DistanceAlongPath, - DistanceFromPath = - dist.DistanceFromPath < 0 - ? -dist.DistanceFromPath - : dist.DistanceFromPath - }; } } } diff --git a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs index 0868945318..9dbf52285b 100644 --- a/src/ImageSharp.Drawing/Paths/ShapeRegion.cs +++ b/src/ImageSharp.Drawing/Paths/ShapeRegion.cs @@ -40,18 +40,18 @@ namespace ImageSharp.Drawing public override Rectangle Bounds { get; } /// - public override int Scan(float y, float[] buffer, int length, int offset) + public override int Scan(float y, Span buffer) { Vector2 start = new Vector2(this.Bounds.Left - 1, y); Vector2 end = new Vector2(this.Bounds.Right + 1, y); - Vector2[] innerbuffer = ArrayPool.Shared.Rent(length); + Vector2[] innerbuffer = ArrayPool.Shared.Rent(buffer.Length); try { - int count = this.Shape.FindIntersections(start, end, innerbuffer, length, 0); + int count = this.Shape.FindIntersections(start, end, innerbuffer, buffer.Length, 0); for (int i = 0; i < count; i++) { - buffer[i + offset] = innerbuffer[i].X; + buffer[i] = innerbuffer[i].X; } return count; diff --git a/src/ImageSharp.Drawing/Pens/IPen.cs b/src/ImageSharp.Drawing/Pens/IPen.cs index 81d273091e..20ac53dafc 100644 --- a/src/ImageSharp.Drawing/Pens/IPen.cs +++ b/src/ImageSharp.Drawing/Pens/IPen.cs @@ -12,21 +12,28 @@ namespace ImageSharp.Drawing.Pens /// Interface representing a Pen /// /// The type of the color. - public interface IPen + public interface IPen : IPen where TPixel : struct, IPixel { /// - /// Creates the applicator for applying this pen to an Image + /// Gets the stroke fill. /// - /// The source image. - /// 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(ImageBase source, RectangleF region, GraphicsOptions options); + IBrush StrokeFill { get; } + } + + /// + /// Iterface represting the pattern and size of the stroke to apply with a Pen. + /// + public interface IPen + { + /// + /// Gets the width to apply to the stroke + /// + float StrokeWidth { get; } + + /// + /// Gets the stoke pattern. + /// + System.ReadOnlySpan StrokePattern { get; } } } diff --git a/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs b/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs index 53a3c8c995..37cfff9e93 100644 --- a/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs +++ b/src/ImageSharp.Drawing/Pens/Pen{TPixel}.cs @@ -5,6 +5,7 @@ namespace ImageSharp.Drawing.Pens { + using System; using System.Numerics; using ImageSharp.Drawing.Brushes; @@ -48,8 +49,8 @@ namespace ImageSharp.Drawing.Pens /// The pattern. public Pen(IBrush brush, float width, float[] pattern) { - this.Brush = brush; - this.Width = width; + this.StrokeFill = brush; + this.StrokeWidth = width; this.pattern = pattern; } @@ -73,195 +74,13 @@ namespace ImageSharp.Drawing.Pens { } - /// - /// Initializes a new instance of the class. - /// - /// The pen. - internal Pen(Pen pen) - : this(pen.Brush, pen.Width, pen.pattern) - { - } - - /// - /// Gets the brush. - /// - /// - /// The brush. - /// - public IBrush Brush { get; } - - /// - /// Gets the width. - /// - /// - /// The width. - /// - public float Width { get; } - - /// - /// Creates the applicator for applying this pen to an Image - /// - /// The source image. - /// The region the pen will be applied to. - /// The Graphics options - /// - /// Returns a the applicator for the pen. - /// - /// - /// 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(ImageBase source, 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(source, this.Brush, region, this.Width, options); - } - - return new PatternPenApplicator(source, this.Brush, region, this.Width, this.pattern, options); - } - - private class SolidPenApplicator : PenApplicator - { - private readonly BrushApplicator brush; - private readonly float halfWidth; - - public SolidPenApplicator(ImageBase sourcePixels, IBrush brush, RectangleF region, float width, GraphicsOptions options) - { - this.brush = brush.CreateApplicator(sourcePixels, region, options); - this.halfWidth = width / 2; - this.RequiredRegion = RectangleF.Outset(region, width); - } - - public override RectangleF RequiredRegion - { - get; - } - - public override void Dispose() - { - this.brush.Dispose(); - } + /// + public IBrush StrokeFill { get; } - public override ColoredPointInfo GetColor(int x, int y, PointInfo info) - { - var result = default(ColoredPointInfo); - result.Color = this.brush[x, y]; + /// + public float StrokeWidth { get; } - if (info.DistanceFromPath < this.halfWidth) - { - // inside strip - result.DistanceFromElement = 0; - } - else - { - result.DistanceFromElement = info.DistanceFromPath - this.halfWidth; - } - - return result; - } - } - - private class PatternPenApplicator : PenApplicator - { - private readonly BrushApplicator brush; - private readonly float halfWidth; - private readonly float[] pattern; - private readonly float totalLength; - - public PatternPenApplicator(ImageBase source, IBrush brush, RectangleF region, float width, float[] pattern, GraphicsOptions options) - { - this.brush = brush.CreateApplicator(source, region, options); - this.halfWidth = width / 2; - this.totalLength = 0; - - this.pattern = new float[pattern.Length + 1]; - this.pattern[0] = 0; - for (int i = 0; i < pattern.Length; i++) - { - this.totalLength += pattern[i] * width; - this.pattern[i + 1] = this.totalLength; - } - - this.RequiredRegion = RectangleF.Outset(region, width); - } - - public override RectangleF RequiredRegion - { - get; - } - - public override void Dispose() - { - this.brush.Dispose(); - } - - public override ColoredPointInfo GetColor(int x, int y, PointInfo info) - { - var 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. - infoResult.Color = this.brush[x, y]; - - float distanceWAway = 0; - - if (info.DistanceFromPath < this.halfWidth) - { - // inside strip - distanceWAway = 0; - } - else - { - distanceWAway = info.DistanceFromPath - this.halfWidth; - } - - for (int i = 0; i < this.pattern.Length - 1; i++) - { - float start = this.pattern[i]; - float end = this.pattern[i + 1]; - - if (length >= start && length < end) - { - // in section - if (i % 2 == 0) - { - // solid part return the maxDistance - infoResult.DistanceFromElement = distanceWAway; - return infoResult; - } - else - { - // this is a none solid part - float distanceFromStart = length - start; - float distanceFromEnd = end - length; - - float closestEdge = MathF.Min(distanceFromStart, distanceFromEnd); - - float distanceAcross = closestEdge; - - if (distanceWAway > 0) - { - infoResult.DistanceFromElement = new Vector2(distanceAcross, distanceWAway).Length(); - } - else - { - infoResult.DistanceFromElement = closestEdge; - } - - return infoResult; - } - } - } - - return infoResult; - } - } + /// + public ReadOnlySpan StrokePattern => this.pattern; } } diff --git a/src/ImageSharp.Drawing/Pens/Processors/ColoredPointInfo.cs b/src/ImageSharp.Drawing/Pens/Processors/ColoredPointInfo.cs deleted file mode 100644 index 65a8a61319..0000000000 --- a/src/ImageSharp.Drawing/Pens/Processors/ColoredPointInfo.cs +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Processors -{ - 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 TPixel : struct, IPixel - { - /// - /// The color - /// - public TPixel Color; - - /// - /// The distance from element - /// - public float DistanceFromElement; - } -} diff --git a/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs b/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs deleted file mode 100644 index ac18890685..0000000000 --- a/src/ImageSharp.Drawing/Pens/Processors/PenApplicator.cs +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Processors -{ - using System; - 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 TPixel : struct, IPixel - { - /// - /// Gets the required region. - /// - /// - /// The required region. - /// - public abstract RectangleF RequiredRegion { get; } - - /// - public abstract void Dispose(); - - /// - /// Gets a from a point represented by a . - /// - /// The x. - /// The y. - /// The information to extract color details about. - /// - /// Returns the color details and distance from a solid bit of the line. - /// - public abstract ColoredPointInfo GetColor(int x, int y, PointInfo info); - } -} diff --git a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs b/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs deleted file mode 100644 index 860c4c4f06..0000000000 --- a/src/ImageSharp.Drawing/Processors/DrawPathProcessor.cs +++ /dev/null @@ -1,141 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing.Processors -{ - using System; - 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 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. - /// - /// 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) - { - this.Path = drawable; - this.Pen = pen; - this.Options = options; - } - - /// - /// Gets the graphics options. - /// - public GraphicsOptions Options { get; } - - /// - /// Gets the pen. - /// - public IPen Pen { get; } - - /// - /// Gets the path. - /// - public Drawable Path { get; } - - /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) - { - using (PenApplicator applicator = this.Pen.CreateApplicator(source, this.Path.Bounds, this.Options)) - { - Rectangle rect = RectangleF.Ceiling(applicator.RequiredRegion); - - int polyStartY = rect.Y - PaddingFactor; - int polyEndY = rect.Bottom + PaddingFactor; - int startX = rect.X - PaddingFactor; - int endX = rect.Right + PaddingFactor; - - int minX = Math.Max(sourceRectangle.Left, startX); - int maxX = Math.Min(sourceRectangle.Right, endX); - int minY = Math.Max(sourceRectangle.Top, polyStartY); - int maxY = Math.Min(sourceRectangle.Bottom, polyEndY); - - // Align start/end positions. - minX = Math.Max(0, minX); - maxX = Math.Min(source.Width, maxX); - minY = Math.Max(0, minY); - maxY = Math.Min(source.Height, maxY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - 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; - - using (var amount = new Buffer(width)) - using (var colors = new Buffer(width)) - { - for (int i = 0; i < width; i++) - { - int x = i + minX; - int offsetX = x - startX; - PointInfo info = this.Path.GetPointInfo(offsetX, offsetY); - 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 = source.GetRowSpan(offsetY).Slice(minX - startX, width); - blender.Blend(destination, destination, colors, amount); - } - }); - } - } - - /// - /// Returns the correct opacity for the given distance. - /// - /// Thw distance from the central point. - /// The - private float Opacity(float distance) - { - if (distance <= 0) - { - return 1; - } - - if (this.Options.Antialias && distance < AntialiasFactor) - { - return 1 - (distance / AntialiasFactor); - } - - return 0; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs index ae828e112c..f2e09d2897 100644 --- a/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs @@ -93,6 +93,7 @@ namespace ImageSharp.Drawing.Processors using (BrushApplicator applicator = this.Brush.CreateApplicator(source, rect, this.Options)) { float[] buffer = arrayPool.Rent(maxIntersections); + Span bufferSpan = buffer.AsSpan().Slice(0, maxIntersections); int scanlineWidth = maxX - minX; using (var scanline = new Buffer(scanlineWidth)) { @@ -116,14 +117,14 @@ namespace ImageSharp.Drawing.Processors float subpixelFractionPoint = subpixelFraction / subpixelCount; for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction) { - int pointsFound = region.Scan(subPixel, buffer, maxIntersections, 0); + int pointsFound = region.Scan(subPixel, bufferSpan); if (pointsFound == 0) { // nothing on this line skip continue; } - QuickSort(buffer, pointsFound); + QuickSort(bufferSpan.Slice(0, pointsFound)); for (int point = 0; point < pointsFound; point += 2) { @@ -153,13 +154,11 @@ namespace ImageSharp.Drawing.Processors int nextX = startX + 1; endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge - if (nextX >= 0) + nextX = Math.Max(nextX, 0); + for (int x = nextX; x < endX; x++) { - for (int x = nextX; x < endX; x++) - { - scanline[x] += subpixelFraction; - scanlineDirty = true; - } + scanline[x] += subpixelFraction; + scanlineDirty = true; } } } @@ -194,20 +193,20 @@ namespace ImageSharp.Drawing.Processors } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Swap(float[] data, int left, int right) + private static void Swap(Span data, int left, int right) { float tmp = data[left]; data[left] = data[right]; data[right] = tmp; } - private static void QuickSort(float[] data, int size) + private static void QuickSort(Span data) { - int hi = Math.Min(data.Length - 1, size - 1); + int hi = Math.Min(data.Length - 1, data.Length - 1); QuickSort(data, 0, hi); } - private static void QuickSort(float[] data, int lo, int hi) + private static void QuickSort(Span data, int lo, int hi) { if (lo < hi) { @@ -217,7 +216,7 @@ namespace ImageSharp.Drawing.Processors } } - private static int Partition(float[] data, int lo, int hi) + private static int Partition(Span data, int lo, int hi) { float pivot = data[lo]; int i = lo - 1; diff --git a/src/ImageSharp.Drawing/Region.cs b/src/ImageSharp.Drawing/Region.cs index 8cab885021..687ee23fdd 100644 --- a/src/ImageSharp.Drawing/Region.cs +++ b/src/ImageSharp.Drawing/Region.cs @@ -5,6 +5,8 @@ namespace ImageSharp.Drawing { + using System; + /// /// Represents a region of an image. /// @@ -19,7 +21,7 @@ namespace ImageSharp.Drawing /// Gets the bounding box that entirely surrounds this region. /// /// - /// This should always contains all possible points returned from . + /// This should always contains all possible points returned from . /// public abstract Rectangle Bounds { get; } @@ -28,9 +30,7 @@ namespace ImageSharp.Drawing /// /// The position along the y axis to find intersections. /// The buffer. - /// The length. - /// The offset. /// The number of intersections found. - public abstract int Scan(float y, float[] buffer, int length, int offset); + public abstract int Scan(float y, Span buffer); } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Text/DrawText.Path.cs b/src/ImageSharp.Drawing/Text/DrawText.Path.cs new file mode 100644 index 0000000000..2bc23b64bc --- /dev/null +++ b/src/ImageSharp.Drawing/Text/DrawText.Path.cs @@ -0,0 +1,200 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System.Numerics; + + using Drawing; + using Drawing.Brushes; + using Drawing.Pens; + using ImageSharp.PixelFormats; + using SixLabors.Fonts; + using SixLabors.Shapes; + + /// + /// Extension methods for the type. + /// + public static partial class ImageExtensions + { + /// + /// Draws the text onto the the image filled via the brush. + /// + /// The type of the color. + /// The image this method extends. + /// The text. + /// The font. + /// The color. + /// The path. + /// + /// The . + /// + public static Image DrawText(this Image source, string text, Font font, TPixel color, IPath path) + where TPixel : struct, IPixel + { + return source.DrawText(text, font, color, path, TextGraphicsOptions.Default); + } + + /// + /// Draws the text onto the the image filled via the brush. + /// + /// The type of the color. + /// The image this method extends. + /// The text. + /// The font. + /// The color. + /// The path. + /// The options. + /// + /// The . + /// + public static Image DrawText(this Image source, string text, Font font, TPixel color, IPath path, TextGraphicsOptions options) + where TPixel : struct, IPixel + { + return source.DrawText(text, font, Brushes.Solid(color), null, path, options); + } + + /// + /// Draws the text onto the the image filled via the brush. + /// + /// The type of the color. + /// The image this method extends. + /// The text. + /// The font. + /// The brush. + /// The location. + /// + /// The . + /// + public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPath path) + where TPixel : struct, IPixel + { + return source.DrawText(text, font, brush, path, TextGraphicsOptions.Default); + } + + /// + /// Draws the text onto the the image filled via the brush. + /// + /// The type of the color. + /// The image this method extends. + /// The text. + /// The font. + /// The brush. + /// The path. + /// The options. + /// + /// The . + /// + public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPath path, TextGraphicsOptions options) + where TPixel : struct, IPixel + { + return source.DrawText(text, font, brush, null, path, options); + } + + /// + /// Draws the text onto the the image outlined via the pen. + /// + /// The type of the color. + /// The image this method extends. + /// The text. + /// The font. + /// The pen. + /// The path. + /// + /// The . + /// + public static Image DrawText(this Image source, string text, Font font, IPen pen, IPath path) + where TPixel : struct, IPixel + { + return source.DrawText(text, font, pen, path, TextGraphicsOptions.Default); + } + + /// + /// Draws the text onto the the image outlined via the pen. + /// + /// The type of the color. + /// The image this method extends. + /// The text. + /// The font. + /// The pen. + /// The path. + /// The options. + /// + /// The . + /// + public static Image DrawText(this Image source, string text, Font font, IPen pen, IPath path, TextGraphicsOptions options) + where TPixel : struct, IPixel + { + return source.DrawText(text, font, null, pen, path, options); + } + + /// + /// Draws the text onto the the image filled via the brush then outlined via the pen. + /// + /// The type of the color. + /// The image this method extends. + /// The text. + /// The font. + /// The brush. + /// The pen. + /// The path. + /// + /// The . + /// + public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, IPath path) + where TPixel : struct, IPixel + { + return source.DrawText(text, font, brush, pen, path, TextGraphicsOptions.Default); + } + + /// + /// Draws the text onto the the image filled via the brush then outlined via the pen. + /// + /// The type of the color. + /// The image this method extends. + /// The text. + /// The font. + /// The brush. + /// The pen. + /// The path. + /// The options. + /// + /// The . + /// + public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, IPath path, TextGraphicsOptions options) + where TPixel : struct, IPixel + { + Vector2 dpi = DefaultTextDpi; + if (options.UseImageResolution) + { + dpi = new Vector2((float)source.MetaData.HorizontalResolution, (float)source.MetaData.VerticalResolution); + } + + var style = new FontSpan(font, dpi) + { + ApplyKerning = options.ApplyKerning, + TabWidth = options.TabWidth, + WrappingWidth = options.WrapTextWidth, + HorizontalAlignment = options.HorizontalAlignment, + VerticalAlignment = options.VerticalAlignment + }; + + IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, path, style); + + var pathOptions = (GraphicsOptions)options; + if (brush != null) + { + source.Fill(brush, glyphs, pathOptions); + } + + if (pen != null) + { + source.Draw(pen, glyphs, pathOptions); + } + + return source; + } + } +} diff --git a/src/ImageSharp.Drawing/Text/DrawText.cs b/src/ImageSharp.Drawing/Text/DrawText.cs index bd33289fa6..3b0d3db411 100644 --- a/src/ImageSharp.Drawing/Text/DrawText.cs +++ b/src/ImageSharp.Drawing/Text/DrawText.cs @@ -12,6 +12,7 @@ namespace ImageSharp using Drawing.Pens; using ImageSharp.PixelFormats; using SixLabors.Fonts; + using SixLabors.Shapes; /// /// Extension methods for the type. @@ -167,43 +168,32 @@ namespace ImageSharp 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); - - TextRenderer renderer = new TextRenderer(glyphBuilder); - Vector2 dpi = DefaultTextDpi; if (options.UseImageResolution) { dpi = new Vector2((float)source.MetaData.HorizontalResolution, (float)source.MetaData.VerticalResolution); } - FontSpan style = new FontSpan(font, dpi) + var style = new FontSpan(font, dpi) { ApplyKerning = options.ApplyKerning, TabWidth = options.TabWidth, WrappingWidth = options.WrapTextWidth, - Alignment = options.TextAlignment + HorizontalAlignment = options.HorizontalAlignment, + VerticalAlignment = options.VerticalAlignment }; - renderer.RenderText(text, style); - - System.Collections.Generic.IEnumerable shapesToDraw = glyphBuilder.Paths; + IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, location, style); - GraphicsOptions pathOptions = (GraphicsOptions)options; + var pathOptions = (GraphicsOptions)options; if (brush != null) { - foreach (SixLabors.Shapes.IPath s in shapesToDraw) - { - source.Fill(brush, s, pathOptions); - } + source.Fill(brush, glyphs, pathOptions); } if (pen != null) { - foreach (SixLabors.Shapes.IPath s in shapesToDraw) - { - source.Draw(pen, s, pathOptions); - } + source.Draw(pen, glyphs, pathOptions); } return source; diff --git a/src/ImageSharp.Drawing/Text/GlyphBuilder.cs b/src/ImageSharp.Drawing/Text/GlyphBuilder.cs deleted file mode 100644 index 0033a608c3..0000000000 --- a/src/ImageSharp.Drawing/Text/GlyphBuilder.cs +++ /dev/null @@ -1,127 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Drawing -{ - using System.Collections.Generic; - using System.Numerics; - - using SixLabors.Fonts; - using SixLabors.Shapes; - - /// - /// rendering surface that Fonts can use to generate Shapes. - /// - internal class GlyphBuilder : IGlyphRenderer - { - private readonly PathBuilder builder = new PathBuilder(); - private readonly List paths = new List(); - private Vector2 currentPoint = default(Vector2); - - /// - /// Initializes a new instance of the class. - /// - public GlyphBuilder() - : this(Vector2.Zero) - { - // glyphs are renderd realative to bottom left so invert the Y axis to allow it to render on top left origin surface - this.builder = new PathBuilder(); - } - - /// - /// Initializes a new instance of the class. - /// - /// The origin. - public GlyphBuilder(Vector2 origin) - { - this.builder = new PathBuilder(); - this.builder.SetOrigin(origin); - } - - /// - /// Gets the paths that have been rendered by this. - /// - public IEnumerable Paths => this.paths; - - /// - /// Begins the glyph. - /// - /// The offset that the glyph will be rendered at. - void IGlyphRenderer.BeginGlyph(Vector2 location) - { - this.builder.Clear(); - } - - /// - /// Begins the figure. - /// - void IGlyphRenderer.BeginFigure() - { - this.builder.StartFigure(); - } - - /// - /// Draws a cubic bezier from the current point to the - /// - /// The second control point. - /// The third control point. - /// The point. - void IGlyphRenderer.CubicBezierTo(Vector2 secondControlPoint, Vector2 thirdControlPoint, Vector2 point) - { - this.builder.AddBezier(this.currentPoint, secondControlPoint, thirdControlPoint, point); - this.currentPoint = point; - } - - /// - /// Ends the glyph. - /// - void IGlyphRenderer.EndGlyph() - { - this.paths.Add(this.builder.Build()); - } - - /// - /// Ends the figure. - /// - void IGlyphRenderer.EndFigure() - { - this.builder.CloseFigure(); - } - - /// - /// Draws a line from the current point to the . - /// - /// The point. - void IGlyphRenderer.LineTo(Vector2 point) - { - this.builder.AddLine(this.currentPoint, point); - this.currentPoint = point; - } - - /// - /// Moves to current point to the supplied vector. - /// - /// The point. - void IGlyphRenderer.MoveTo(Vector2 point) - { - this.builder.StartFigure(); - this.currentPoint = point; - } - - /// - /// Draws a quadratics bezier from the current point to the - /// - /// The second control point. - /// The point. - void IGlyphRenderer.QuadraticBezierTo(Vector2 secondControlPoint, Vector2 point) - { - Vector2 c1 = (((secondControlPoint - this.currentPoint) * 2) / 3) + this.currentPoint; - Vector2 c2 = (((secondControlPoint - point) * 2) / 3) + point; - - this.builder.AddBezier(this.currentPoint, c1, c2, point); - this.currentPoint = point; - } - } -} diff --git a/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs b/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs index 388b39bcc5..593ac36d4f 100644 --- a/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs +++ b/src/ImageSharp.Drawing/Text/TextGraphicsOptions.cs @@ -33,7 +33,8 @@ namespace ImageSharp.Drawing private float wrapTextWidth; - private SixLabors.Fonts.TextAlignment? textAlignment; + private SixLabors.Fonts.HorizontalAlignment? horizontalAlignment; + private SixLabors.Fonts.VerticalAlignment? verticalAlignment; /// /// Initializes a new instance of the struct. @@ -45,7 +46,8 @@ namespace ImageSharp.Drawing this.tabWidth = 4; this.useImageResolution = false; this.wrapTextWidth = 0; - this.textAlignment = SixLabors.Fonts.TextAlignment.Left; + this.horizontalAlignment = HorizontalAlignment.Left; + this.verticalAlignment = VerticalAlignment.Top; this.antialiasSubpixelDepth = 16; this.blenderMode = PixelBlenderMode.Normal; @@ -104,7 +106,12 @@ namespace ImageSharp.Drawing /// 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; } + public HorizontalAlignment HorizontalAlignment { get => this.horizontalAlignment ?? HorizontalAlignment.Left; set => this.horizontalAlignment = value; } + + /// + /// Gets or sets a value indicating how to align the text relative to the rendering space. + /// + public VerticalAlignment VerticalAlignment { get => this.verticalAlignment ?? VerticalAlignment.Top; set => this.verticalAlignment = value; } /// /// Performs an implicit conversion from to . diff --git a/src/ImageSharp/Common/Extensions/ByteExtensions.cs b/src/ImageSharp/Common/Extensions/ByteExtensions.cs index a6dc19ded4..f1161eb6f6 100644 --- a/src/ImageSharp/Common/Extensions/ByteExtensions.cs +++ b/src/ImageSharp/Common/Extensions/ByteExtensions.cs @@ -5,10 +5,13 @@ namespace ImageSharp { + using System; using System.Runtime.CompilerServices; + using ImageSharp.PixelFormats; + /// - /// Extension methods for the struct. + /// Extension methods for the struct buffers. /// internal static class ByteExtensions { @@ -44,5 +47,45 @@ namespace ImageSharp j--; } } + + /// + /// Returns a reference to the given position of the array unsafe casted to . + /// + /// The byte array. + /// The offset in bytes. + /// The reference at the given offset. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref Rgb24 GetRgb24(this byte[] bytes, int offset) + { + DebugGuard.MustBeLessThan(offset + 2, bytes.Length, nameof(offset)); + + return ref Unsafe.As(ref bytes[offset]); + } + + /// + /// Returns a reference to the given position of the span unsafe casted to . + /// + /// The byte span. + /// The offset in bytes. + /// The reference at the given offset. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref Rgb24 GetRgb24(this Span bytes, int offset) + { + DebugGuard.MustBeLessThan(offset + 2, bytes.Length, nameof(offset)); + + return ref Unsafe.As(ref bytes[offset]); + } + + /// + /// Returns a reference to the given position of the buffer pointed by `baseRef` unsafe casted to . + /// + /// A reference to the beginning of the buffer + /// The offset in bytes. + /// The reference at the given offset. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref Rgb24 GetRgb24(ref byte baseRef, int offset) + { + return ref Unsafe.As(ref Unsafe.Add(ref baseRef, offset)); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs index a4b392fcf1..8e55c18af8 100644 --- a/src/ImageSharp/Common/Helpers/Guard.cs +++ b/src/ImageSharp/Common/Helpers/Guard.cs @@ -242,7 +242,6 @@ namespace ImageSharp /// is true /// public static void MustBeSizedAtLeast(Span target, int minLength, string parameterName) - where T : struct { if (target.Length < minLength) { diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index dd91aa11d9..997a77d6c5 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -6,7 +6,8 @@ namespace ImageSharp.Formats { using System; using System.IO; - + using System.Runtime.CompilerServices; + using ImageSharp.Memory; using ImageSharp.PixelFormats; /// @@ -243,13 +244,16 @@ namespace ImageSharp.Formats byte[] row = new byte[arrayWidth + padding]; TPixel color = default(TPixel); + Rgba32 rgba = default(Rgba32); + for (int y = 0; y < height; y++) { int newY = Invert(y, height, inverted); - this.currentStream.Read(row, 0, row.Length); - int offset = 0; + Span pixelRow = pixels.GetRowSpan(y); + + // TODO: Could use PixelOperations here! for (int x = 0; x < arrayWidth; x++) { int colOffset = x * ppb; @@ -260,8 +264,9 @@ namespace ImageSharp.Formats int newX = colOffset + shift; // Stored in b-> g-> r order. - color.PackFromBytes(colors[colorIndex + 2], colors[colorIndex + 1], colors[colorIndex], 255); - pixels[newX, newY] = color; + rgba.Bgr = Unsafe.As(ref colors[colorIndex]); + color.PackFromRgba32(rgba); + pixelRow[newX] = color; } offset++; @@ -286,6 +291,8 @@ namespace ImageSharp.Formats const int ComponentCount = 2; TPixel color = default(TPixel); + Rgba32 rgba = new Rgba32(0, 0, 0, 255); + using (PixelArea row = new PixelArea(width, ComponentOrder.Xyz)) { for (int y = 0; y < height; y++) @@ -294,17 +301,19 @@ namespace ImageSharp.Formats int newY = Invert(y, height, inverted); + Span pixelRow = pixels.GetRowSpan(newY); + int offset = 0; for (int x = 0; x < width; x++) { short temp = BitConverter.ToInt16(row.Bytes, offset); - byte r = (byte)(((temp & Rgb16RMask) >> 11) * ScaleR); - byte g = (byte)(((temp & Rgb16GMask) >> 5) * ScaleG); - byte b = (byte)((temp & Rgb16BMask) * ScaleR); + rgba.R = (byte)(((temp & Rgb16RMask) >> 11) * ScaleR); + rgba.G = (byte)(((temp & Rgb16GMask) >> 5) * ScaleG); + rgba.B = (byte)((temp & Rgb16BMask) * ScaleR); - color.PackFromBytes(r, g, b, 255); - pixels[x, newY] = color; + color.PackFromRgba32(rgba); + pixelRow[x] = color; offset += ComponentCount; } } diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 618d268f70..5b56c4c02f 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -435,6 +435,8 @@ namespace ImageSharp.Formats Span rowSpan = image.GetRowSpan(writeY); + Rgba32 rgba = new Rgba32(0, 0, 0, 255); + for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++) { int index = indices[i]; @@ -446,7 +448,9 @@ namespace ImageSharp.Formats int indexOffset = index * 3; ref TPixel pixel = ref rowSpan[x]; - pixel.PackFromBytes(colorTable[indexOffset], colorTable[indexOffset + 1], colorTable[indexOffset + 2], 255); + rgba.Rgb = colorTable.GetRgb24(indexOffset); + + pixel.PackFromRgba32(rgba); } i++; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs index 211b66dacc..f84dc977f1 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs @@ -5,53 +5,120 @@ // ReSharper disable InconsistentNaming // #pragma warning disable -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - - namespace ImageSharp.Formats.Jpg { + using System.Numerics; + using System.Runtime.CompilerServices; + internal partial struct Block8x8F { - private static readonly Vector4 CMin4 = new Vector4(-128f); - private static readonly Vector4 CMax4 = new Vector4(127f); - private static readonly Vector4 COff4 = new Vector4(128f); + private static readonly Vector4 CMin4 = new Vector4(0F); + private static readonly Vector4 CMax4 = new Vector4(255F); + private static readonly Vector4 COff4 = new Vector4(128F); /// - /// Transpose the block into d + /// Transpose the block into the destination block. /// - /// Destination + /// The destination block [MethodImpl(MethodImplOptions.AggressiveInlining)] public void TransposeInto(ref Block8x8F d) { - d.V0L.X = V0L.X; d.V1L.X = V0L.Y; d.V2L.X = V0L.Z; d.V3L.X = V0L.W; d.V4L.X = V0R.X; d.V5L.X = V0R.Y; d.V6L.X = V0R.Z; d.V7L.X = V0R.W; - d.V0L.Y = V1L.X; d.V1L.Y = V1L.Y; d.V2L.Y = V1L.Z; d.V3L.Y = V1L.W; d.V4L.Y = V1R.X; d.V5L.Y = V1R.Y; d.V6L.Y = V1R.Z; d.V7L.Y = V1R.W; - d.V0L.Z = V2L.X; d.V1L.Z = V2L.Y; d.V2L.Z = V2L.Z; d.V3L.Z = V2L.W; d.V4L.Z = V2R.X; d.V5L.Z = V2R.Y; d.V6L.Z = V2R.Z; d.V7L.Z = V2R.W; - d.V0L.W = V3L.X; d.V1L.W = V3L.Y; d.V2L.W = V3L.Z; d.V3L.W = V3L.W; d.V4L.W = V3R.X; d.V5L.W = V3R.Y; d.V6L.W = V3R.Z; d.V7L.W = V3R.W; - d.V0R.X = V4L.X; d.V1R.X = V4L.Y; d.V2R.X = V4L.Z; d.V3R.X = V4L.W; d.V4R.X = V4R.X; d.V5R.X = V4R.Y; d.V6R.X = V4R.Z; d.V7R.X = V4R.W; - d.V0R.Y = V5L.X; d.V1R.Y = V5L.Y; d.V2R.Y = V5L.Z; d.V3R.Y = V5L.W; d.V4R.Y = V5R.X; d.V5R.Y = V5R.Y; d.V6R.Y = V5R.Z; d.V7R.Y = V5R.W; - d.V0R.Z = V6L.X; d.V1R.Z = V6L.Y; d.V2R.Z = V6L.Z; d.V3R.Z = V6L.W; d.V4R.Z = V6R.X; d.V5R.Z = V6R.Y; d.V6R.Z = V6R.Z; d.V7R.Z = V6R.W; - d.V0R.W = V7L.X; d.V1R.W = V7L.Y; d.V2R.W = V7L.Z; d.V3R.W = V7L.W; d.V4R.W = V7R.X; d.V5R.W = V7R.Y; d.V6R.W = V7R.Z; d.V7R.W = V7R.W; + d.V0L.X = V0L.X; + d.V1L.X = V0L.Y; + d.V2L.X = V0L.Z; + d.V3L.X = V0L.W; + d.V4L.X = V0R.X; + d.V5L.X = V0R.Y; + d.V6L.X = V0R.Z; + d.V7L.X = V0R.W; + + d.V0L.Y = V1L.X; + d.V1L.Y = V1L.Y; + d.V2L.Y = V1L.Z; + d.V3L.Y = V1L.W; + d.V4L.Y = V1R.X; + d.V5L.Y = V1R.Y; + d.V6L.Y = V1R.Z; + d.V7L.Y = V1R.W; + + d.V0L.Z = V2L.X; + d.V1L.Z = V2L.Y; + d.V2L.Z = V2L.Z; + d.V3L.Z = V2L.W; + d.V4L.Z = V2R.X; + d.V5L.Z = V2R.Y; + d.V6L.Z = V2R.Z; + d.V7L.Z = V2R.W; + + d.V0L.W = V3L.X; + d.V1L.W = V3L.Y; + d.V2L.W = V3L.Z; + d.V3L.W = V3L.W; + d.V4L.W = V3R.X; + d.V5L.W = V3R.Y; + d.V6L.W = V3R.Z; + d.V7L.W = V3R.W; + + d.V0R.X = V4L.X; + d.V1R.X = V4L.Y; + d.V2R.X = V4L.Z; + d.V3R.X = V4L.W; + d.V4R.X = V4R.X; + d.V5R.X = V4R.Y; + d.V6R.X = V4R.Z; + d.V7R.X = V4R.W; + + d.V0R.Y = V5L.X; + d.V1R.Y = V5L.Y; + d.V2R.Y = V5L.Z; + d.V3R.Y = V5L.W; + d.V4R.Y = V5R.X; + d.V5R.Y = V5R.Y; + d.V6R.Y = V5R.Z; + d.V7R.Y = V5R.W; + + d.V0R.Z = V6L.X; + d.V1R.Z = V6L.Y; + d.V2R.Z = V6L.Z; + d.V3R.Z = V6L.W; + d.V4R.Z = V6R.X; + d.V5R.Z = V6R.Y; + d.V6R.Z = V6R.Z; + d.V7R.Z = V6R.W; + + d.V0R.W = V7L.X; + d.V1R.W = V7L.Y; + d.V2R.W = V7L.Z; + d.V3R.W = V7L.W; + d.V4R.W = V7R.X; + d.V5R.W = V7R.Y; + d.V6R.W = V7R.Z; + d.V7R.W = V7R.W; } /// /// Level shift by +128, clip to [0, 255] /// - /// Destination + /// The destination block [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void TransformByteConvetibleColorValuesInto(ref Block8x8F d) { - d.V0L = Vector4.Max(Vector4.Min(V0L, CMax4), CMin4) + COff4;d.V0R = Vector4.Max(Vector4.Min(V0R, CMax4), CMin4) + COff4; - d.V1L = Vector4.Max(Vector4.Min(V1L, CMax4), CMin4) + COff4;d.V1R = Vector4.Max(Vector4.Min(V1R, CMax4), CMin4) + COff4; - d.V2L = Vector4.Max(Vector4.Min(V2L, CMax4), CMin4) + COff4;d.V2R = Vector4.Max(Vector4.Min(V2R, CMax4), CMin4) + COff4; - d.V3L = Vector4.Max(Vector4.Min(V3L, CMax4), CMin4) + COff4;d.V3R = Vector4.Max(Vector4.Min(V3R, CMax4), CMin4) + COff4; - d.V4L = Vector4.Max(Vector4.Min(V4L, CMax4), CMin4) + COff4;d.V4R = Vector4.Max(Vector4.Min(V4R, CMax4), CMin4) + COff4; - d.V5L = Vector4.Max(Vector4.Min(V5L, CMax4), CMin4) + COff4;d.V5R = Vector4.Max(Vector4.Min(V5R, CMax4), CMin4) + COff4; - d.V6L = Vector4.Max(Vector4.Min(V6L, CMax4), CMin4) + COff4;d.V6R = Vector4.Max(Vector4.Min(V6R, CMax4), CMin4) + COff4; - d.V7L = Vector4.Max(Vector4.Min(V7L, CMax4), CMin4) + COff4;d.V7R = Vector4.Max(Vector4.Min(V7R, CMax4), CMin4) + COff4; + d.V0L = Vector4.Clamp(V0L + COff4, CMin4, CMax4); + d.V0R = Vector4.Clamp(V0R + COff4, CMin4, CMax4); + d.V1L = Vector4.Clamp(V1L + COff4, CMin4, CMax4); + d.V1R = Vector4.Clamp(V1R + COff4, CMin4, CMax4); + d.V2L = Vector4.Clamp(V2L + COff4, CMin4, CMax4); + d.V2R = Vector4.Clamp(V2R + COff4, CMin4, CMax4); + d.V3L = Vector4.Clamp(V3L + COff4, CMin4, CMax4); + d.V3R = Vector4.Clamp(V3R + COff4, CMin4, CMax4); + d.V4L = Vector4.Clamp(V4L + COff4, CMin4, CMax4); + d.V4R = Vector4.Clamp(V4R + COff4, CMin4, CMax4); + d.V5L = Vector4.Clamp(V5L + COff4, CMin4, CMax4); + d.V5R = Vector4.Clamp(V5R + COff4, CMin4, CMax4); + d.V6L = Vector4.Clamp(V6L + COff4, CMin4, CMax4); + d.V6R = Vector4.Clamp(V6R + COff4, CMin4, CMax4); + d.V7L = Vector4.Clamp(V7L + COff4, CMin4, CMax4); + d.V7R = Vector4.Clamp(V7R + COff4, CMin4, CMax4); } - - } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt index be198a6fa7..03566acbbc 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt @@ -11,31 +11,29 @@ <#@ output extension=".cs" #> // #pragma warning disable -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - <# char[] coordz = {'X', 'Y', 'Z', 'W'}; #> - namespace ImageSharp.Formats.Jpg { + using System.Numerics; + using System.Runtime.CompilerServices; + internal partial struct Block8x8F { - private static readonly Vector4 CMin4 = new Vector4(-128f); - private static readonly Vector4 CMax4 = new Vector4(127f); - private static readonly Vector4 COff4 = new Vector4(128f); + private static readonly Vector4 CMin4 = new Vector4(0F); + private static readonly Vector4 CMax4 = new Vector4(255F); + private static readonly Vector4 COff4 = new Vector4(128F); /// - /// Transpose the block into d + /// Transpose the block into the destination block. /// - /// Destination + /// The destination block [MethodImpl(MethodImplOptions.AggressiveInlining)] public void TransposeInto(ref Block8x8F d) { <# - PushIndent(" "); + PushIndent(" "); for (int i = 0; i < 8; i++) { @@ -44,13 +42,16 @@ namespace ImageSharp.Formats.Jpg for (int j = 0; j < 8; j++) { + if(i > 0 && j == 0){ + WriteLine(""); + } + char srcCoord = coordz[j % 4]; char srcSide = (j / 4) % 2 == 0 ? 'L' : 'R'; - - string expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord}; "; + + string expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord};\r\n"; Write(expression); } - WriteLine(""); } PopIndent(); #> @@ -59,27 +60,24 @@ namespace ImageSharp.Formats.Jpg /// /// Level shift by +128, clip to [0, 255] /// - /// Destination + /// The destination block [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void TransformByteConvetibleColorValuesInto(ref Block8x8F d) { <# - PushIndent(" "); + PushIndent(" "); for (int i = 0; i < 8; i++) { for (int j = 0; j < 2; j++) { char side = j == 0 ? 'L' : 'R'; - Write($"d.V{i}{side} = Vector4.Max(Vector4.Min(V{i}{side}, CMax4), CMin4) + COff4;"); + Write($"d.V{i}{side} = Vector4.Clamp(V{i}{side} + COff4, CMin4, CMax4);\r\n"); } - WriteLine(""); } PopIndent(); #> } - - } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index 56466d7a0f..130b5856c0 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -57,6 +57,9 @@ namespace ImageSharp.Formats.Jpg public Vector4 V7R; #pragma warning restore SA1600 // ElementsMustBeDocumented + private static readonly Vector4 NegativeOne = new Vector4(-1); + private static readonly Vector4 Offset = new Vector4(.5F); + /// /// Get/Set scalar elements at a given index /// @@ -402,12 +405,11 @@ namespace ImageSharp.Formats.Jpg [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor) { - // sign(v) = max(min(v, 1), -1) - Vector4 sign = Vector4.Min(dividend, Vector4.One); - sign = Vector4.Max(sign, new Vector4(-1)); + // sign(dividend) = max(min(dividend, 1), -1) + var sign = Vector4.Clamp(dividend, NegativeOne, Vector4.One); // AlmostRound(dividend/divisor) = dividend/divisior + 0.5*sign(dividend) - return (dividend / divisor) + (sign * new Vector4(0.5f)); + return (dividend / divisor) + (sign * Offset); } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrToRgbTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrToRgbTables.cs index 27324b5f68..5c9e8f9fc3 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrToRgbTables.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/YCbCrToRgbTables.cs @@ -91,7 +91,7 @@ namespace ImageSharp.Formats.Jpg // 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); + packed.PackFromRgba32(new Rgba32(r, g, b, 255)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 13c51db243..9716843719 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -613,25 +613,23 @@ namespace ImageSharp.Formats private void ConvertFromGrayScale(Image image) where TPixel : struct, IPixel { - using (PixelAccessor pixels = image.Lock()) - { - Parallel.For( - 0, - image.Height, - image.Configuration.ParallelOptions, - y => + Parallel.For( + 0, + image.Height, + image.Configuration.ParallelOptions, + y => { + ref TPixel pixelRowBaseRef = ref image.GetPixelReference(0, y); + int yoff = this.grayImage.GetRowOffset(y); + for (int x = 0; x < image.Width; x++) { byte rgb = this.grayImage.Pixels[yoff + x]; - - TPixel packed = default(TPixel); - packed.PackFromBytes(rgb, rgb, rgb, 255); - pixels[x, y] = packed; + ref TPixel pixel = ref Unsafe.Add(ref pixelRowBaseRef, x); + pixel.PackFromRgba32(new Rgba32(rgb, rgb, rgb, 255)); } }); - } this.AssignResolution(image); } @@ -646,30 +644,29 @@ namespace ImageSharp.Formats { int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - using (PixelAccessor pixels = image.Lock()) - { - Parallel.For( - 0, - image.Height, - image.Configuration.ParallelOptions, - y => + 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); + ref TPixel pixelRowBaseRef = ref image.GetPixelReference(0, y); + + Rgba32 rgba = new Rgba32(0, 0, 0, 255); for (int x = 0; x < image.Width; x++) { - byte red = this.ycbcrImage.YChannel[yo + x]; - byte green = this.ycbcrImage.CbChannel[co + (x / scale)]; - byte blue = this.ycbcrImage.CrChannel[co + (x / scale)]; + rgba.R = this.ycbcrImage.YChannel[yo + x]; + rgba.G = this.ycbcrImage.CbChannel[co + (x / scale)]; + rgba.B = this.ycbcrImage.CrChannel[co + (x / scale)]; - TPixel packed = default(TPixel); - packed.PackFromBytes(red, green, blue, 255); - pixels[x, y] = packed; + ref TPixel pixel = ref Unsafe.Add(ref pixelRowBaseRef, x); + pixel.PackFromRgba32(rgba); } }); - } this.AssignResolution(image); } @@ -729,16 +726,15 @@ namespace ImageSharp.Formats { int scale = this.ComponentArray[0].HorizontalFactor / this.ComponentArray[1].HorizontalFactor; - using (PixelAccessor pixels = image.Lock()) - { - Parallel.For( - 0, - image.Height, - y => + Parallel.For( + 0, + image.Height, + y => { // TODO: Simplify + optimize + share duplicate code across converter methods int yo = this.ycbcrImage.GetRowYOffset(y); int co = this.ycbcrImage.GetRowCOffset(y); + ref TPixel pixelRowBaseRef = ref image.GetPixelReference(0, y); for (int x = 0; x < image.Width; x++) { @@ -746,12 +742,10 @@ namespace ImageSharp.Formats byte cb = this.ycbcrImage.CbChannel[co + (x / scale)]; byte cr = this.ycbcrImage.CrChannel[co + (x / scale)]; - TPixel packed = default(TPixel); - this.PackYcck(ref packed, yy, cb, cr, x, y); - pixels[x, y] = packed; + ref TPixel pixel = ref Unsafe.Add(ref pixelRowBaseRef, x); + this.PackYcck(ref pixel, yy, cb, cr, x, y); } }); - } this.AssignResolution(image); } @@ -860,7 +854,7 @@ namespace ImageSharp.Formats byte g = (byte)(((m / 255F) * (1F - keyline)).Clamp(0, 1) * 255); byte b = (byte)(((y / 255F) * (1F - keyline)).Clamp(0, 1) * 255); - packed.PackFromBytes(r, g, b, 255); + packed.PackFromRgba32(new Rgba32(r, g, b)); } /// @@ -904,7 +898,7 @@ namespace ImageSharp.Formats byte g = (byte)(((1 - magenta) * (1 - keyline)).Clamp(0, 1) * 255); byte b = (byte)(((1 - yellow) * (1 - keyline)).Clamp(0, 1) * 255); - packed.PackFromBytes(r, g, b, 255); + packed.PackFromRgba32(new Rgba32(r, g, b)); } /// @@ -982,6 +976,7 @@ namespace ImageSharp.Formats byte[] identifier = new byte[Icclength]; this.InputProcessor.ReadFull(identifier, 0, Icclength); + remaining -= Icclength; // we have read it by this point if (identifier[0] == 'I' && identifier[1] == 'C' && @@ -996,7 +991,6 @@ namespace ImageSharp.Formats identifier[10] == 'E' && identifier[11] == '\0') { - remaining -= Icclength; byte[] profile = new byte[remaining]; this.InputProcessor.ReadFull(profile, 0, remaining); @@ -1009,6 +1003,11 @@ namespace ImageSharp.Formats metadata.IccProfile.Extend(profile); } } + else + { + // not an ICC profile we can handle read the remaining so we can carry on and ignore this. + this.InputProcessor.Skip(remaining); + } } /// diff --git a/src/ImageSharp/Formats/Jpeg/itu-t81.pdf b/src/ImageSharp/Formats/Jpeg/itu-t81.pdf new file mode 100644 index 0000000000..83fba254b4 Binary files /dev/null and b/src/ImageSharp/Formats/Jpeg/itu-t81.pdf differ diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index f8be0f6cc7..e0bd153e63 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -178,7 +178,7 @@ namespace ImageSharp.Formats /// /// Thrown if the stream does not contain and end chunk. /// - /// + /// /// Thrown if the image is larger than the maximum allowable size. /// /// The decoded image @@ -189,7 +189,6 @@ namespace ImageSharp.Formats this.currentStream = stream; this.currentStream.Skip(8); Image image = null; - PixelAccessor pixels = null; try { using (var deframeStream = new ZlibInflateStream(this.currentStream)) @@ -211,11 +210,11 @@ namespace ImageSharp.Formats case PngChunkTypes.Data: if (image == null) { - this.InitializeImage(metadata, out image, out pixels); + this.InitializeImage(metadata, out image); } deframeStream.AllocateNewBytes(currentChunk.Length); - this.ReadScanlines(deframeStream.CompressedStream, pixels); + this.ReadScanlines(deframeStream.CompressedStream, image); stream.Read(this.crcBuffer, 0, 4); break; case PngChunkTypes.Palette: @@ -252,7 +251,6 @@ namespace ImageSharp.Formats } finally { - pixels?.Dispose(); this.scanline?.Dispose(); this.previousScanline?.Dispose(); } @@ -324,8 +322,7 @@ namespace ImageSharp.Formats /// 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) + private void InitializeImage(ImageMetaData metadata, out Image image) where TPixel : struct, IPixel { if (this.header.Width > Image.MaxWidth || this.header.Height > Image.MaxHeight) @@ -334,7 +331,6 @@ namespace ImageSharp.Formats } 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; @@ -398,17 +394,17 @@ namespace ImageSharp.Formats /// /// The pixel format. /// The containing data. - /// The pixel data. - private void ReadScanlines(Stream dataStream, PixelAccessor pixels) + /// The pixel data. + private void ReadScanlines(Stream dataStream, Image image) where TPixel : struct, IPixel { if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) { - this.DecodeInterlacedPixelData(dataStream, pixels); + this.DecodeInterlacedPixelData(dataStream, image); } else { - this.DecodePixelData(dataStream, pixels); + this.DecodePixelData(dataStream, image); } } @@ -417,8 +413,8 @@ namespace ImageSharp.Formats /// /// The pixel format. /// The compressed pixel data stream. - /// The image pixel accessor. - private void DecodePixelData(Stream compressedStream, PixelAccessor pixels) + /// The image to decode to. + private void DecodePixelData(Stream compressedStream, Image image) where TPixel : struct, IPixel { while (this.currentRow < this.header.Height) @@ -462,7 +458,7 @@ namespace ImageSharp.Formats throw new ImageFormatException("Unknown filter type."); } - this.ProcessDefilteredScanline(this.scanline.Array, pixels); + this.ProcessDefilteredScanline(this.scanline.Array, image); Swap(ref this.scanline, ref this.previousScanline); this.currentRow++; @@ -475,8 +471,8 @@ namespace ImageSharp.Formats /// /// The pixel format. /// The compressed pixel data stream. - /// The image pixel accessor. - private void DecodeInterlacedPixelData(Stream compressedStream, PixelAccessor pixels) + /// The current image. + private void DecodeInterlacedPixelData(Stream compressedStream, Image image) where TPixel : struct, IPixel { while (true) @@ -537,7 +533,8 @@ namespace ImageSharp.Formats throw new ImageFormatException("Unknown filter type."); } - this.ProcessInterlacedDefilteredScanline(this.scanline.Array, this.currentRow, pixels, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]); + Span rowSpan = image.GetRowSpan(this.currentRow); + this.ProcessInterlacedDefilteredScanline(this.scanline.Array, rowSpan, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]); Swap(ref this.scanline, ref this.previousScanline); @@ -561,12 +558,12 @@ namespace ImageSharp.Formats /// /// The pixel format. /// The de-filtered scanline - /// The image pixels - private void ProcessDefilteredScanline(byte[] defilteredScanline, PixelAccessor pixels) + /// The image + private void ProcessDefilteredScanline(byte[] defilteredScanline, Image pixels) where TPixel : struct, IPixel { var color = default(TPixel); - Span pixelBuffer = pixels.GetRowSpan(this.currentRow); + Span rowSpan = pixels.GetRowSpan(this.currentRow); var scanlineBuffer = new Span(defilteredScanline, 1); switch (this.PngColorType) @@ -577,8 +574,8 @@ namespace ImageSharp.Formats for (int x = 0; x < this.header.Width; x++) { byte intensity = (byte)(newScanline1[x] * factor); - color.PackFromBytes(intensity, intensity, intensity, 255); - pixels[x, this.currentRow] = color; + color.PackFromRgba32(new Rgba32(intensity, intensity, intensity)); + rowSpan[x] = color; } break; @@ -592,27 +589,27 @@ namespace ImageSharp.Formats byte intensity = defilteredScanline[offset]; byte alpha = defilteredScanline[offset + this.bytesPerSample]; - color.PackFromBytes(intensity, intensity, intensity, alpha); - pixels[x, this.currentRow] = color; + color.PackFromRgba32(new Rgba32(intensity, intensity, intensity)); + rowSpan[x] = color; } break; case PngColorType.Palette: - this.ProcessScanlineFromPalette(defilteredScanline, pixels); + this.ProcessScanlineFromPalette(defilteredScanline, rowSpan); break; case PngColorType.Rgb: - PixelOperations.Instance.PackFromXyzBytes(scanlineBuffer, pixelBuffer, this.header.Width); + PixelOperations.Instance.PackFromRgb24Bytes(scanlineBuffer, rowSpan, this.header.Width); break; case PngColorType.RgbWithAlpha: - PixelOperations.Instance.PackFromXyzwBytes(scanlineBuffer, pixelBuffer, this.header.Width); + PixelOperations.Instance.PackFromRgba32Bytes(scanlineBuffer, rowSpan, this.header.Width); break; } @@ -623,14 +620,16 @@ namespace ImageSharp.Formats /// /// The type of pixel we are expanding to /// The scanline - /// The output pixels - private void ProcessScanlineFromPalette(byte[] defilteredScanline, PixelAccessor pixels) + /// Thecurrent output image row + private void ProcessScanlineFromPalette(byte[] defilteredScanline, Span row) where TPixel : struct, IPixel { byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); byte[] palette = this.palette; var color = default(TPixel); + Rgba32 rgba = default(Rgba32); + 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 @@ -640,36 +639,34 @@ namespace ImageSharp.Formats int index = newScanline[x + 1]; int pixelOffset = index * 3; - byte a = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255; + rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255; - if (a > 0) + if (rgba.A > 0) { - byte r = palette[pixelOffset]; - byte g = palette[pixelOffset + 1]; - byte b = palette[pixelOffset + 2]; - color.PackFromBytes(r, g, b, a); + rgba.Rgb = palette.GetRgb24(pixelOffset); } else { - color.PackFromBytes(0, 0, 0, 0); + rgba = default(Rgba32); } - pixels[x, this.currentRow] = color; + color.PackFromRgba32(rgba); + row[x] = color; } } else { + rgba.A = 255; + for (int x = 0; x < this.header.Width; x++) { int index = newScanline[x + 1]; int pixelOffset = index * 3; - byte r = palette[pixelOffset]; - byte g = palette[pixelOffset + 1]; - byte b = palette[pixelOffset + 2]; + rgba.Rgb = palette.GetRgb24(pixelOffset); - color.PackFromBytes(r, g, b, 255); - pixels[x, this.currentRow] = color; + color.PackFromRgba32(rgba); + row[x] = color; } } } @@ -679,11 +676,10 @@ namespace ImageSharp.Formats /// /// The pixel format. /// The de-filtered scanline - /// The current image row. - /// The image pixels + /// The current image row. /// 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) + private void ProcessInterlacedDefilteredScanline(byte[] defilteredScanline, Span rowSpan, int pixelOffset = 0, int increment = 1) where TPixel : struct, IPixel { var color = default(TPixel); @@ -696,8 +692,8 @@ namespace ImageSharp.Formats for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o++) { byte intensity = (byte)(newScanline1[o] * factor); - color.PackFromBytes(intensity, intensity, intensity, 255); - pixels[x, row] = color; + color.PackFromRgba32(new Rgba32(intensity, intensity, intensity)); + rowSpan[x] = color; } break; @@ -708,9 +704,8 @@ namespace ImageSharp.Formats { byte intensity = defilteredScanline[o]; byte alpha = defilteredScanline[o + this.bytesPerSample]; - - color.PackFromBytes(intensity, intensity, intensity, alpha); - pixels[x, row] = color; + color.PackFromRgba32(new Rgba32(intensity, intensity, intensity, alpha)); + rowSpan[x] = color; } break; @@ -718,6 +713,7 @@ namespace ImageSharp.Formats case PngColorType.Palette: byte[] newScanline = ToArrayByBitsLength(defilteredScanline, this.bytesPerScanline, this.header.BitDepth); + Rgba32 rgba = default(Rgba32); if (this.paletteAlpha != null && this.paletteAlpha.Length > 0) { @@ -728,36 +724,34 @@ namespace ImageSharp.Formats int index = newScanline[o]; int offset = index * 3; - byte a = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255; + rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : (byte)255; - if (a > 0) + if (rgba.A > 0) { - byte r = this.palette[offset]; - byte g = this.palette[offset + 1]; - byte b = this.palette[offset + 2]; - color.PackFromBytes(r, g, b, a); + rgba.Rgb = this.palette.GetRgb24(offset); } else { - color.PackFromBytes(0, 0, 0, 0); + rgba = default(Rgba32); } - pixels[x, row] = color; + color.PackFromRgba32(rgba); + rowSpan[x] = color; } } else { + rgba.A = 255; + for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o++) { int index = newScanline[o]; int offset = index * 3; - byte r = this.palette[offset]; - byte g = this.palette[offset + 1]; - byte b = this.palette[offset + 2]; + rgba.Rgb = this.palette.GetRgb24(offset); - color.PackFromBytes(r, g, b, 255); - pixels[x, row] = color; + color.PackFromRgba32(rgba); + rowSpan[x] = color; } } @@ -765,14 +759,15 @@ namespace ImageSharp.Formats case PngColorType.Rgb: + rgba.A = 255; for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel) { - byte r = defilteredScanline[o]; - byte g = defilteredScanline[o + this.bytesPerSample]; - byte b = defilteredScanline[o + (2 * this.bytesPerSample)]; + rgba.R = defilteredScanline[o]; + rgba.G = defilteredScanline[o + this.bytesPerSample]; + rgba.B = defilteredScanline[o + (2 * this.bytesPerSample)]; - color.PackFromBytes(r, g, b, 255); - pixels[x, row] = color; + color.PackFromRgba32(rgba); + rowSpan[x] = color; } break; @@ -781,13 +776,13 @@ namespace ImageSharp.Formats for (int x = pixelOffset, o = 1; x < this.header.Width; x += increment, o += this.bytesPerPixel) { - byte r = defilteredScanline[o]; - byte g = defilteredScanline[o + this.bytesPerSample]; - byte b = defilteredScanline[o + (2 * this.bytesPerSample)]; - byte a = defilteredScanline[o + (3 * this.bytesPerSample)]; + rgba.R = defilteredScanline[o]; + rgba.G = defilteredScanline[o + this.bytesPerSample]; + rgba.B = defilteredScanline[o + (2 * this.bytesPerSample)]; + rgba.A = defilteredScanline[o + (3 * this.bytesPerSample)]; - color.PackFromBytes(r, g, b, a); - pixels[x, row] = color; + color.PackFromRgba32(rgba); + rowSpan[x] = color; } break; diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 0482b2691c..645df05481 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -220,11 +220,7 @@ namespace ImageSharp.Formats this.WritePhysicalChunk(stream, image); this.WriteGammaChunk(stream); - using (PixelAccessor pixels = image.Lock()) - { - this.WriteDataChunks(pixels, stream); - } - + this.WriteDataChunks(image, stream); this.WriteEndChunk(stream); stream.Flush(); } @@ -302,9 +298,8 @@ namespace ImageSharp.Formats /// Collects a row of grayscale pixels. /// /// The pixel format. - /// The image pixels accessor. - /// The row index. - private void CollectGrayscaleBytes(PixelAccessor pixels, int row) + /// The image row span. + private void CollectGrayscaleBytes(Span rowSpan) where TPixel : struct, IPixel { byte[] rawScanlineArray = this.rawScanline.Array; @@ -316,7 +311,7 @@ namespace ImageSharp.Formats // Convert the color to YCbCr and store the luminance // Optionally store the original color alpha. int offset = x * this.bytesPerPixel; - pixels[x, row].ToXyzwBytes(this.chunkTypeBuffer, 0); + rowSpan[x].ToXyzwBytes(this.chunkTypeBuffer, 0); byte luminance = (byte)((0.299F * this.chunkTypeBuffer[0]) + (0.587F * this.chunkTypeBuffer[1]) + (0.114F * this.chunkTypeBuffer[2])); for (int i = 0; i < this.bytesPerPixel; i++) @@ -337,20 +332,17 @@ namespace ImageSharp.Formats /// Collects a row of true color pixel data. /// /// The pixel format. - /// The image pixel accessor. - /// The row index. - private void CollecTPixelBytes(PixelAccessor pixels, int row) + /// The row span. + private void CollecTPixelBytes(Span rowSpan) where TPixel : struct, IPixel { - Span rowSpan = pixels.GetRowSpan(row); - if (this.bytesPerPixel == 4) { - PixelOperations.Instance.ToXyzwBytes(rowSpan, this.rawScanline, this.width); + PixelOperations.Instance.ToRgba32Bytes(rowSpan, this.rawScanline, this.width); } else { - PixelOperations.Instance.ToXyzBytes(rowSpan, this.rawScanline, this.width); + PixelOperations.Instance.ToRgb24Bytes(rowSpan, this.rawScanline, this.width); } } @@ -359,10 +351,10 @@ namespace ImageSharp.Formats /// Each scanline is encoded in the most optimal manner to improve compression. /// /// The pixel format. - /// The image pixel accessor. + /// The row span. /// The row. /// The - private Buffer EncodePixelRow(PixelAccessor pixels, int row) + private Buffer EncodePixelRow(Span rowSpan, int row) where TPixel : struct, IPixel { switch (this.pngColorType) @@ -372,10 +364,10 @@ namespace ImageSharp.Formats break; case PngColorType.Grayscale: case PngColorType.GrayscaleWithAlpha: - this.CollectGrayscaleBytes(pixels, row); + this.CollectGrayscaleBytes(rowSpan); break; default: - this.CollecTPixelBytes(pixels, row); + this.CollecTPixelBytes(rowSpan); break; } @@ -637,17 +629,17 @@ namespace ImageSharp.Formats /// Writes the pixel information to the stream. /// /// The pixel format. - /// The pixel accessor. + /// The image. /// The stream. - private void WriteDataChunks(PixelAccessor pixels, Stream stream) + private void WriteDataChunks(Image pixels, Stream stream) where TPixel : struct, IPixel { 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); + this.previousScanline = Buffer.CreateClean(this.bytesPerScanline); + this.rawScanline = Buffer.CreateClean(this.bytesPerScanline); + this.result = Buffer.CreateClean(resultLength); if (this.pngColorType != PngColorType.Palette) { @@ -667,7 +659,7 @@ namespace ImageSharp.Formats { for (int y = 0; y < this.height; y++) { - Buffer r = this.EncodePixelRow(pixels, y); + Buffer r = this.EncodePixelRow(pixels.GetRowSpan(y), y); deflateStream.Write(r.Array, 0, resultLength); Swap(ref this.rawScanline, ref this.previousScanline); diff --git a/src/ImageSharp/Image/ImageBase{TPixel}.cs b/src/ImageSharp/Image/ImageBase{TPixel}.cs index 508c73eb2b..647d60075d 100644 --- a/src/ImageSharp/Image/ImageBase{TPixel}.cs +++ b/src/ImageSharp/Image/ImageBase{TPixel}.cs @@ -11,7 +11,7 @@ namespace ImageSharp using ImageSharp.Memory; using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; /// /// The base class of all images. Encapsulates the basic properties and methods required to manipulate diff --git a/src/ImageSharp/Image/ImageProcessingExtensions.cs b/src/ImageSharp/Image/ImageProcessingExtensions.cs index c9577ac15f..8eed103d10 100644 --- a/src/ImageSharp/Image/ImageProcessingExtensions.cs +++ b/src/ImageSharp/Image/ImageProcessingExtensions.cs @@ -7,7 +7,7 @@ namespace ImageSharp { using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; /// /// Extension methods for the type. diff --git a/src/ImageSharp/Image/Image{TPixel}.cs b/src/ImageSharp/Image/Image{TPixel}.cs index 092706e416..059ccb9a07 100644 --- a/src/ImageSharp/Image/Image{TPixel}.cs +++ b/src/ImageSharp/Image/Image{TPixel}.cs @@ -15,7 +15,7 @@ namespace ImageSharp using Formats; using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; /// /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. diff --git a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs index 4baae86157..3902ba4255 100644 --- a/src/ImageSharp/Image/PixelAccessor{TPixel}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TPixel}.cs @@ -275,7 +275,7 @@ namespace ImageSharp Span source = area.GetRowSpan(y); Span destination = this.GetRowSpan(targetX, targetY + y); - Operations.PackFromZyxBytes(source, destination, width); + Operations.PackFromBgr24Bytes(source, destination, width); } } @@ -295,7 +295,7 @@ namespace ImageSharp Span source = area.GetRowSpan(y); Span destination = this.GetRowSpan(targetX, targetY + y); - Operations.PackFromZyxwBytes(source, destination, width); + Operations.PackFromBgra32Bytes(source, destination, width); } } @@ -315,7 +315,7 @@ namespace ImageSharp Span source = area.GetRowSpan(y); Span destination = this.GetRowSpan(targetX, targetY + y); - Operations.PackFromXyzBytes(source, destination, width); + Operations.PackFromRgb24Bytes(source, destination, width); } } @@ -334,7 +334,7 @@ namespace ImageSharp { Span source = area.GetRowSpan(y); Span destination = this.GetRowSpan(targetX, targetY + y); - Operations.PackFromXyzwBytes(source, destination, width); + Operations.PackFromRgba32Bytes(source, destination, width); } } @@ -353,7 +353,7 @@ namespace ImageSharp { Span source = this.GetRowSpan(sourceX, sourceY + y); Span destination = area.GetRowSpan(y); - Operations.ToZyxBytes(source, destination, width); + Operations.ToBgr24Bytes(source, destination, width); } } @@ -372,7 +372,7 @@ namespace ImageSharp { Span source = this.GetRowSpan(sourceX, sourceY + y); Span destination = area.GetRowSpan(y); - Operations.ToZyxwBytes(source, destination, width); + Operations.ToBgra32Bytes(source, destination, width); } } @@ -391,7 +391,7 @@ namespace ImageSharp { Span source = this.GetRowSpan(sourceX, sourceY + y); Span destination = area.GetRowSpan(y); - Operations.ToXyzBytes(source, destination, width); + Operations.ToRgb24Bytes(source, destination, width); } } @@ -410,7 +410,7 @@ namespace ImageSharp { Span source = this.GetRowSpan(sourceX, sourceY + y); Span destination = area.GetRowSpan(y); - Operations.ToXyzwBytes(source, destination, width); + Operations.ToRgba32Bytes(source, destination, width); } } diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 6194be1bf4..17f7bf58f3 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -53,6 +53,35 @@ TextTemplatingFileGenerator + Block8x8F.Generated.cs + + TextTemplatingFileGenerator + PixelOperations{TPixel}.Generated.cs + + + TextTemplatingFileGenerator + Rgba32.PixelOperations.Generated.cs + + + + + + + + True + True + Block8x8F.Generated.tt + + + True + True + PixelOperations{TPixel}.Generated.tt + + + True + True + Rgba32.PixelOperations.Generated.tt + \ No newline at end of file diff --git a/src/ImageSharp/Numerics/Matrix3x2Extensions.cs b/src/ImageSharp/Numerics/Matrix3x2Extensions.cs new file mode 100644 index 0000000000..f424624020 --- /dev/null +++ b/src/ImageSharp/Numerics/Matrix3x2Extensions.cs @@ -0,0 +1,72 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// Extension methods for the struct + /// + public static class Matrix3x2Extensions + { + /// + /// Creates a rotation matrix for the given rotation in degrees and a center point. + /// + /// The angle in degrees + /// The center point + /// The rotation + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateRotation(float degree, Point centerPoint) + { + float radian = MathF.DegreeToRadian(degree); + return Matrix3x2.CreateRotation(radian, new Vector2(centerPoint.X, centerPoint.Y)); + } + + /// + /// Creates a rotation matrix for the given rotation in degrees and a center point. + /// + /// The angle in degrees + /// The center point + /// The rotation + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateRotation(float degree, PointF centerPoint) + { + float radian = MathF.DegreeToRadian(degree); + return Matrix3x2.CreateRotation(radian, centerPoint); + } + + /// + /// Creates a skew matrix for the given angle in degrees and a center point. + /// + /// The x-angle in degrees + /// The y-angle in degrees + /// The center point + /// The rotation + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateSkew(float degreesX, float degreesY, Point centerPoint) + { + float radiansX = MathF.DegreeToRadian(degreesX); + float radiansY = MathF.DegreeToRadian(degreesY); + return Matrix3x2.CreateSkew(radiansX, radiansY, new Vector2(centerPoint.X, centerPoint.Y)); + } + + /// + /// Creates a skew matrix for the given angle in degrees and a center point. + /// + /// The x-angle in degrees + /// The y-angle in degrees + /// The rotation + /// The center point + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateSkew(float degreesX, float degreesY, PointF centerPoint) + { + float radiansX = MathF.DegreeToRadian(degreesX); + float radiansY = MathF.DegreeToRadian(degreesY); + return Matrix3x2.CreateSkew(radiansX, radiansY, new Vector2(centerPoint.X, centerPoint.Y)); + } + } +} diff --git a/src/ImageSharp/Numerics/Point.cs b/src/ImageSharp/Numerics/Point.cs index 8d523895fb..9bff27d2ac 100644 --- a/src/ImageSharp/Numerics/Point.cs +++ b/src/ImageSharp/Numerics/Point.cs @@ -25,6 +25,17 @@ namespace ImageSharp /// public static readonly Point Empty = default(Point); + /// + /// Initializes a new instance of the struct. + /// + /// The horizontal and vertical position of the point. + public Point(int value) + : this() + { + this.X = LowInt16(value); + this.Y = HighInt16(value); + } + /// /// Initializes a new instance of the struct. /// @@ -38,15 +49,13 @@ namespace ImageSharp } /// - /// Initializes a new instance of the struct. + /// Initializes a new instance of the struct from the given . /// - /// - /// The vector representing the width and height. - /// - public Point(Vector2 vector) + /// The size + public Point(Size size) { - this.X = (int)Math.Round(vector.X); - this.Y = (int)Math.Round(vector.Y); + this.X = size.Width; + this.Y = size.Height; } /// @@ -66,176 +75,160 @@ namespace ImageSharp public bool IsEmpty => this.Equals(Empty); /// - /// Computes the sum of adding two points. + /// Creates a with the coordinates of the specified . /// - /// The point on the left hand of the operand. - /// The point on the right hand of the operand. - /// - /// The - /// + /// The point [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point operator +(Point left, Point right) - { - return new Point(left.X + right.X, left.Y + right.Y); - } + public static implicit operator PointF(Point point) => new PointF(point.X, point.Y); + + /// + /// Creates a with the coordinates of the specified . + /// + /// The point + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Vector2(Point point) => new Vector2(point.X, point.Y); + + /// + /// Creates a with the coordinates of the specified . + /// + /// The point + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Size(Point point) => new Size(point.X, point.Y); /// - /// Computes the difference left by subtracting one point from another. + /// Translates a by a given . /// - /// The point on the left hand of the operand. - /// The point on the right hand of the operand. + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. /// /// The /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Point operator -(Point left, Point right) - { - return new Point(left.X - right.X, left.Y - right.Y); - } + public static Point operator +(Point point, Size size) => Add(point, size); + + /// + /// Translates a by the negative of a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point operator -(Point point, Size size) => Subtract(point, size); /// /// 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 current left is equal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Point left, Point right) - { - return left.Equals(right); - } + public static bool operator ==(Point left, Point right) => left.Equals(right); /// /// Compares two objects for inequality. /// - /// - /// 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 current left is unequal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Point left, Point right) - { - return !left.Equals(right); - } + public static bool operator !=(Point left, Point right) => !left.Equals(right); /// - /// Creates a rotation matrix for the given point and angle. + /// Translates a by the negative of a given . /// - /// The origin point to rotate around - /// Rotation in degrees - /// The rotation - public static Matrix3x2 CreateRotation(Point origin, float degrees) - { - float radians = MathF.DegreeToRadian(degrees); - return Matrix3x2.CreateRotation(radians, new Vector2(origin.X, origin.Y)); - } + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Add(Point point, Size size) => new Point(unchecked(point.X + size.Width), unchecked(point.Y + size.Height)); /// - /// Rotates a point around a given a rotation matrix. + /// Translates a by the negative of a given . /// - /// The point to rotate - /// Rotation matrix used - /// The rotated - public static Point Rotate(Point point, Matrix3x2 rotation) - { - return new Point(Vector2.Transform(new Vector2(point.X, point.Y), rotation)); - } + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Subtract(Point point, Size size) => new Point(unchecked(point.X - size.Width), unchecked(point.Y - size.Height)); /// - /// Rotates a point around a given origin by the specified angle in degrees. + /// Converts a to a by performing a ceiling operation on all the coordinates. /// - /// The point to rotate - /// The center point to rotate around. - /// The angle in degrees. - /// The rotated - public static Point Rotate(Point point, Point origin, float degrees) - { - return new Point(Vector2.Transform(new Vector2(point.X, point.Y), CreateRotation(origin, degrees))); - } + /// The point + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Ceiling(PointF point) => new Point(unchecked((int)MathF.Ceiling(point.X)), unchecked((int)MathF.Ceiling(point.Y))); /// - /// Creates a skew matrix for the given point and angle. + /// Converts a to a by performing a round operation on all the coordinates. /// - /// The origin point to rotate around - /// The x-angle in degrees. - /// The y-angle in degrees. - /// The rotation - public static Matrix3x2 CreateSkew(Point origin, float degreesX, float degreesY) - { - float radiansX = MathF.DegreeToRadian(degreesX); - float radiansY = MathF.DegreeToRadian(degreesY); - return Matrix3x2.CreateSkew(radiansX, radiansY, new Vector2(origin.X, origin.Y)); - } + /// The point + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Round(PointF point) => new Point(unchecked((int)MathF.Round(point.X)), unchecked((int)MathF.Round(point.Y))); /// - /// Skews a point using a given a skew matrix. + /// Converts a to a by performing a truncate operation on all the coordinates. /// - /// The point to rotate - /// Rotation matrix used - /// The rotated - public static Point Skew(Point point, Matrix3x2 skew) - { - return new Point(Vector2.Transform(new Vector2(point.X, point.Y), skew)); - } + /// The point + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Truncate(PointF point) => new Point(unchecked((int)point.X), unchecked((int)point.Y)); /// - /// Skews a point around a given origin by the specified angles in degrees. + /// Converts a to a by performing a round operation on all the coordinates. /// - /// The point to skew. - /// The center point to rotate around. - /// The x-angle in degrees. - /// The y-angle in degrees. - /// The skewed - public static Point Skew(Point point, Point origin, float degreesX, float degreesY) - { - return new Point(Vector2.Transform(new Vector2(point.X, point.Y), CreateSkew(origin, degreesX, degreesY))); - } + /// The vector + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Round(Vector2 vector) => new Point(unchecked((int)MathF.Round(vector.X)), unchecked((int)MathF.Round(vector.Y))); /// - /// Gets a representation for this . + /// Rotates a point around the given rotation matrix. /// - /// A representation for this object. - public Vector2 ToVector2() - { - return new Vector2(this.X, this.Y); - } + /// The point to rotate + /// Rotation matrix used + /// The rotated + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Rotate(Point point, Matrix3x2 rotation) => Round(Vector2.Transform(new Vector2(point.X, point.Y), rotation)); + + /// + /// Skews a point using the given skew matrix. + /// + /// The point to rotate + /// Rotation matrix used + /// The rotated + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Skew(Point point, Matrix3x2 skew) => Round(Vector2.Transform(new Vector2(point.X, point.Y), skew)); /// /// Translates this by the specified amount. /// /// The amount to offset the x-coordinate. /// The amount to offset the y-coordinate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Offset(int dx, int dy) { - this.X += dx; - this.Y += dy; + unchecked + { + this.X += dx; + this.Y += dy; + } } /// /// Translates this by the specified amount. /// - /// The used offset this . - public void Offset(Point p) - { - this.Offset(p.X, p.Y); - } + /// The used offset this . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(Point point) => this.Offset(point.X, point.Y); /// - public override int GetHashCode() - { - return this.GetHashCode(this); - } + public override int GetHashCode() => this.GetHashCode(this); /// public override string ToString() @@ -249,34 +242,16 @@ namespace ImageSharp } /// - public override bool Equals(object obj) - { - if (obj is Point) - { - return this.Equals((Point)obj); - } - - return false; - } + public override bool Equals(object obj) => obj is Point && this.Equals((Point)obj); /// - public bool Equals(Point other) - { - return this.X == other.X && this.Y == other.Y; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Point other) => this.X == other.X && this.Y == other.Y; - /// - /// Returns the hash code for this instance. - /// - /// - /// The instance of to return the hash code for. - /// - /// - /// A 32-bit signed integer that is the hash code for this instance. - /// - private int GetHashCode(Point point) - { - return point.X ^ point.Y; - } + private static short HighInt16(int n) => unchecked((short)((n >> 16) & 0xffff)); + + private static short LowInt16(int n) => unchecked((short)(n & 0xffff)); + + private int GetHashCode(Point point) => point.X ^ point.Y; } } \ No newline at end of file diff --git a/src/ImageSharp/Numerics/PointF.cs b/src/ImageSharp/Numerics/PointF.cs new file mode 100644 index 0000000000..cbe5c7f48b --- /dev/null +++ b/src/ImageSharp/Numerics/PointF.cs @@ -0,0 +1,233 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.ComponentModel; + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// Represents an ordered pair of single precision floating point x- and y-coordinates that defines a point in + /// a two-dimensional plane. + /// + /// + /// 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 PointF : IEquatable + { + /// + /// Represents a that has X and Y values set to zero. + /// + public static readonly PointF Empty = default(PointF); + + /// + /// Initializes a new instance of the struct. + /// + /// The horizontal position of the point. + /// The vertical position of the point. + public PointF(float x, float y) + : this() + { + this.X = x; + this.Y = y; + } + + /// + /// Initializes a new instance of the struct from the given . + /// + /// The size + public PointF(SizeF size) + { + this.X = size.Width; + this.Y = size.Height; + } + + /// + /// Gets or sets the x-coordinate of this . + /// + public float X { get; set; } + + /// + /// Gets or sets the y-coordinate of this . + /// + public float Y { get; set; } + + /// + /// Gets a value indicating whether this is empty. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsEmpty => this.Equals(Empty); + + /// + /// Creates a with the coordinates of the specified . + /// + /// The vector. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator PointF(Vector2 vector) => new PointF(vector.X, vector.Y); + + /// + /// Creates a with the coordinates of the specified . + /// + /// The point. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Vector2(PointF point) => new Vector2(point.X, point.Y); + + /// + /// Creates a with the coordinates of the specified by truncating each of the coordinates. + /// + /// The point. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Point(PointF point) => Point.Truncate(point); + + /// + /// Translates a by a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// The + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF operator +(PointF point, SizeF size) => Add(point, size); + + /// + /// Translates a by the negative of a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF operator -(PointF point, SizeF size) => Subtract(point, size); + + /// + /// Compares two objects for equality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(PointF left, PointF right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(PointF left, PointF right) => !left.Equals(right); + + /// + /// Translates a by the negative of a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Add(PointF point, SizeF size) => new PointF(point.X + size.Width, point.Y + size.Height); + + /// + /// Translates a by the negative of a given . + /// + /// The point on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Subtract(PointF point, SizeF size) => new PointF(point.X - size.Width, point.Y - size.Height); + + /// + /// Rotates a point around the given rotation matrix. + /// + /// The point to rotate + /// Rotation matrix used + /// The rotated + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Rotate(PointF point, Matrix3x2 rotation) => Vector2.Transform(new Vector2(point.X, point.Y), rotation); + + /// + /// Skews a point using the given skew matrix. + /// + /// The point to rotate + /// Rotation matrix used + /// The rotated + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Skew(PointF point, Matrix3x2 skew) => Vector2.Transform(new Vector2(point.X, point.Y), skew); + + /// + /// Translates this by the specified amount. + /// + /// The amount to offset the x-coordinate. + /// The amount to offset the y-coordinate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(float dx, float dy) + { + this.X += dx; + this.Y += dy; + } + + /// + /// Translates this by the specified amount. + /// + /// The used offset this . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(PointF point) => this.Offset(point.X, point.Y); + + /// + public override int GetHashCode() => this.GetHashCode(this); + + /// + public override string ToString() + { + if (this.IsEmpty) + { + return "PointF [ Empty ]"; + } + + return $"PointF [ X={this.X}, Y={this.Y} ]"; + } + + /// + public override bool Equals(object obj) => obj is PointF && this.Equals((PointF)obj); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(PointF other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); + + /// + /// Returns the hash code for this instance. + /// + /// + /// The instance of to return the hash code for. + /// + /// + /// A 32-bit signed integer that is the hash code for this instance. + /// + private int GetHashCode(PointF point) => point.X.GetHashCode() ^ point.Y.GetHashCode(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Numerics/Rectangle.cs b/src/ImageSharp/Numerics/Rectangle.cs index b651eff71a..d16b22920a 100644 --- a/src/ImageSharp/Numerics/Rectangle.cs +++ b/src/ImageSharp/Numerics/Rectangle.cs @@ -8,6 +8,7 @@ namespace ImageSharp using System; using System.ComponentModel; using System.Numerics; + using System.Runtime.CompilerServices; /// /// Stores a set of four integers that represent the location and size of a rectangle. @@ -23,11 +24,6 @@ namespace ImageSharp /// public static readonly Rectangle Empty = default(Rectangle); - /// - /// The backing vector for SIMD support. - /// - private Vector4 backingVector; - /// /// Initializes a new instance of the struct. /// @@ -37,7 +33,10 @@ namespace ImageSharp /// The height of the rectangle. public Rectangle(int x, int y, int width, int height) { - this.backingVector = new Vector4(x, y, width, height); + this.X = x; + this.Y = y; + this.Width = width; + this.Height = height; } /// @@ -51,197 +50,325 @@ namespace ImageSharp /// public Rectangle(Point point, Size size) { - this.backingVector = new Vector4(point.X, point.Y, size.Width, size.Height); + this.X = point.X; + this.Y = point.Y; + this.Width = size.Width; + this.Height = size.Height; } /// - /// Initializes a new instance of the struct. + /// Gets or sets the x-coordinate of this . /// - /// - /// The which specifies the rectangles top left point in a two-dimensional plane. - /// - /// - /// The which specifies the rectangles bottom right point in a two-dimensional plane. - /// - public Rectangle(Point topLeft, Point bottomRight) - { - this.backingVector = new Vector4(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y); - } + public int X { get; set; } /// - /// Initializes a new instance of the struct. + /// Gets or sets the y-coordinate of this . /// - /// The vector. - public Rectangle(Vector4 vector) - { - this.backingVector = vector; - } + public int Y { get; set; } /// - /// Gets or sets the x-coordinate of this . + /// Gets or sets the width of this . + /// + public int Width { get; set; } + + /// + /// Gets or sets the height of this . /// - public int X + public int Height { get; set; } + + /// + /// Gets or sets the coordinates of the upper-left corner of the rectangular region represented by this . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public Point Location { - get - { - return (int)this.backingVector.X; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Point(this.X, this.Y); + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { - this.backingVector.X = value; + this.X = value.X; + this.Y = value.Y; } } /// - /// Gets or sets the y-coordinate of this . + /// Gets or sets the size of this . /// - public int Y + [EditorBrowsable(EditorBrowsableState.Never)] + public Size Size { - get - { - return (int)this.backingVector.Y; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Size(this.Width, this.Height); + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { - this.backingVector.Y = value; + this.Width = value.Width; + this.Height = value.Height; } } /// - /// Gets or sets the width of this . + /// Gets a value indicating whether this is empty. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsEmpty => this.Equals(Empty); + + /// + /// Gets the y-coordinate of the top edge of this . /// - public int Width + public int Top { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - return (int)this.backingVector.Z; + return this.Y; } + } - set + /// + /// Gets the x-coordinate of the right edge of this . + /// + public int Right + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { - this.backingVector.Z = value; + return unchecked(this.X + this.Width); } } /// - /// Gets or sets the height of this . + /// Gets the y-coordinate of the bottom edge of this . /// - public int Height + public int Bottom { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - return (int)this.backingVector.W; + return unchecked(this.Y + this.Height); } + } - set + /// + /// Gets the x-coordinate of the left edge of this . + /// + public int Left + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { - this.backingVector.W = value; + return this.X; } } /// - /// Gets the size of this . + /// Creates a with the coordinates of the specified . /// - public Size Size => new Size(this.Width, this.Height); + /// The rectangle + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator RectangleF(Rectangle rectangle) => new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); /// - /// Gets a value indicating whether this is empty. + /// Creates a with the coordinates of the specified . /// - [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.Equals(Empty); + /// The rectangle + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Vector4(Rectangle rectangle) => new Vector4(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); /// - /// Gets the y-coordinate of the top edge of this . + /// Compares two objects for equality. /// - public int Top => this.Y; + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rectangle left, Rectangle right) => left.Equals(right); /// - /// Gets the x-coordinate of the right edge of this . + /// Compares two objects for inequality. /// - public int Right => this.X + this.Width; + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rectangle left, Rectangle right) => !left.Equals(right); /// - /// Gets the y-coordinate of the bottom edge of this . + /// Creates a new with the specified location and size. + /// The left coordinate of the rectangle + /// The top coordinate of the rectangle + /// The right coordinate of the rectangle + /// The bottom coordinate of the rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + + // ReSharper disable once InconsistentNaming + public static Rectangle FromLTRB(int left, int top, int right, int bottom) => new Rectangle(left, top, unchecked(right - left), unchecked(bottom - top)); + + /// + /// Returns the center point of the given /// - public int Bottom => this.Y + this.Height; + /// The rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Point Center(Rectangle rectangle) => new Point(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2)); /// - /// Gets the x-coordinate of the left edge of this . + /// Creates a rectangle that represents the intersection between and + /// . If there is no intersection, an empty rectangle is returned. /// - public int Left => this.X; + /// The first rectangle + /// The second rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Intersect(Rectangle a, Rectangle b) + { + int x1 = Math.Max(a.X, b.X); + int x2 = Math.Min(a.Right, b.Right); + int y1 = Math.Max(a.Y, b.Y); + int y2 = Math.Min(a.Bottom, b.Bottom); + + if (x2 >= x1 && y2 >= y1) + { + return new Rectangle(x1, y1, x2 - x1, y2 - y1); + } + + return Empty; + } /// - /// Computes the sum of adding two rectangles. + /// Creates a that is inflated by the specified amount. /// - /// The rectangle on the left hand of the operand. - /// The rectangle on the right hand of the operand. - /// - /// The - /// - public static Rectangle operator +(Rectangle left, Rectangle right) + /// The rectangle + /// The amount to inflate the width by + /// The amount to inflate the height by + /// A new + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Inflate(Rectangle rectangle, int x, int y) { - return new Rectangle(left.backingVector + right.backingVector); + Rectangle r = rectangle; + r.Inflate(x, y); + return r; } /// - /// Computes the difference left by subtracting one rectangle from another. + /// Converts a to a by performing a ceiling operation on all the coordinates. /// - /// The rectangle on the left hand of the operand. - /// The rectangle on the right hand of the operand. - /// - /// The - /// - public static Rectangle operator -(Rectangle left, Rectangle right) + /// The rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Ceiling(RectangleF rectangle) { - return new Rectangle(left.backingVector - right.backingVector); + unchecked + { + return new Rectangle( + (int)MathF.Ceiling(rectangle.X), + (int)MathF.Ceiling(rectangle.Y), + (int)MathF.Ceiling(rectangle.Width), + (int)MathF.Ceiling(rectangle.Height)); + } } /// - /// Compares two objects for equality. + /// Converts a to a by performing a truncate operation on all the coordinates. /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - public static bool operator ==(Rectangle left, Rectangle right) + /// The rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Truncate(RectangleF rectangle) { - return left.Equals(right); + unchecked + { + return new Rectangle( + (int)rectangle.X, + (int)rectangle.Y, + (int)rectangle.Width, + (int)rectangle.Height); + } } /// - /// Compares two objects for inequality. + /// Converts a to a by performing a round operation on all the coordinates. /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - public static bool operator !=(Rectangle left, Rectangle right) + /// The rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Round(RectangleF rectangle) { - return !left.Equals(right); + unchecked + { + return new Rectangle( + (int)MathF.Round(rectangle.X), + (int)MathF.Round(rectangle.Y), + (int)MathF.Round(rectangle.Width), + (int)MathF.Round(rectangle.Height)); + } } /// - /// Returns the center point of the given + /// Creates a rectangle that represents the union between and . + /// + /// The first rectangle + /// The second rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rectangle Union(Rectangle a, Rectangle b) + { + int x1 = Math.Min(a.X, b.X); + int x2 = Math.Max(a.Right, b.Right); + int y1 = Math.Min(a.Y, b.Y); + int y2 = Math.Max(a.Bottom, b.Bottom); + + return new Rectangle(x1, y1, x2 - x1, y2 - y1); + } + + /// + /// Creates a Rectangle that represents the intersection between this Rectangle and the . /// /// The rectangle - /// - public static Point Center(Rectangle rectangle) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Intersect(Rectangle rectangle) { - return new Point(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2)); + Rectangle result = Intersect(rectangle, this); + + this.X = result.X; + this.Y = result.Y; + this.Width = result.Width; + this.Height = result.Height; } + /// + /// Inflates this by the specified amount. + /// + /// The width + /// The height + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Inflate(int width, int height) + { + unchecked + { + this.X -= width; + this.Y -= height; + + this.Width += 2 * width; + this.Height += 2 * height; + } + } + + /// + /// Inflates this by the specified amount. + /// + /// The size + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Inflate(Size size) => this.Inflate(size.Width, size.Height); + /// /// Determines if the specfied point is contained within the rectangular region defined by /// this . @@ -249,33 +376,63 @@ namespace ImageSharp /// The x-coordinate of the given point. /// The y-coordinate of the given point. /// The - public bool Contains(int x, int y) - { - // TODO: SIMD? - return this.X <= x - && x < this.Right - && this.Y <= y - && y < this.Bottom; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(int x, int y) => this.X <= x && x < this.Right && this.Y <= y && y < this.Bottom; + + /// + /// Determines if the specified point is contained within the rectangular region defined by this . + /// + /// The point + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(Point point) => this.Contains(point.X, point.Y); + + /// + /// Determines if the rectangular region represented by is entirely contained + /// within the rectangular region represented by this . + /// + /// The rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(Rectangle rectangle) => + (this.X <= rectangle.X) && (rectangle.Right <= this.Right) && + (this.Y <= rectangle.Y) && (rectangle.Bottom <= this.Bottom); /// /// Determines if the specfied intersects the rectangular region defined by /// this . /// - /// The other Rectange + /// The other Rectange /// The - public bool Intersects(Rectangle rect) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IntersectsWith(Rectangle rectangle) => + (rectangle.X < this.Right) && (this.X < rectangle.Right) && + (rectangle.Y < this.Bottom) && (this.Y < rectangle.Bottom); + + /// + /// Adjusts the location of this rectangle by the specified amount. + /// + /// The point + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(Point point) => this.Offset(point.X, point.Y); + + /// + /// Adjusts the location of this rectangle by the specified amount. + /// + /// The amount to offset the x-coordinate. + /// The amount to offset the y-coordinate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(int dx, int dy) { - return rect.Left <= this.Right && rect.Right >= this.Left - && - rect.Top <= this.Bottom && rect.Bottom >= this.Top; + unchecked + { + this.X += dx; + this.Y += dy; + } } /// - public override int GetHashCode() - { - return this.GetHashCode(this); - } + public override int GetHashCode() => this.GetHashCode(this); /// public override string ToString() @@ -285,39 +442,26 @@ namespace ImageSharp return "Rectangle [ Empty ]"; } - return - $"Rectangle [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]"; + return $"Rectangle [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]"; } /// - public override bool Equals(object obj) - { - if (obj is Rectangle) - { - return this.Equals((Rectangle)obj); - } - - return false; - } + public override bool Equals(object obj) => obj is Rectangle && this.Equals((Rectangle)obj); /// - public bool Equals(Rectangle other) - { - return this.backingVector.Equals(other.backingVector); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Rectangle other) => this.X == other.X && this.Y == other.Y && this.Width == other.Width && this.Height == other.Height; - /// - /// Returns the hash code for this instance. - /// - /// - /// The instance of to return the hash code for. - /// - /// - /// A 32-bit signed integer that is the hash code for this instance. - /// private int GetHashCode(Rectangle rectangle) { - return rectangle.backingVector.GetHashCode(); + unchecked + { + int hashCode = rectangle.X; + hashCode = (hashCode * 397) ^ rectangle.Y; + hashCode = (hashCode * 397) ^ rectangle.Width; + hashCode = (hashCode * 397) ^ rectangle.Height; + return hashCode; + } } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Numerics/RectangleF.cs b/src/ImageSharp/Numerics/RectangleF.cs index 2ed57c8414..7611c96029 100644 --- a/src/ImageSharp/Numerics/RectangleF.cs +++ b/src/ImageSharp/Numerics/RectangleF.cs @@ -8,9 +8,10 @@ namespace ImageSharp using System; using System.ComponentModel; using System.Numerics; + using System.Runtime.CompilerServices; /// - /// Stores a set of four integers that represent the location and size of a rectangle. + /// Stores a set of four single precision floating points that represent the location and size of a rectangle. /// /// /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, @@ -19,15 +20,10 @@ namespace ImageSharp public struct RectangleF : IEquatable { /// - /// Represents a that has X, Y, Width, and Height values set to zero. + /// Represents a that has X, Y, Width, and Height values set to zero. /// public static readonly RectangleF Empty = default(RectangleF); - /// - /// The backing vector for SIMD support. - /// - private Vector4 backingVector; - /// /// Initializes a new instance of the struct. /// @@ -37,79 +33,80 @@ namespace ImageSharp /// The height of the rectangle. public RectangleF(float x, float y, float width, float height) { - this.backingVector = new Vector4(x, y, width, height); + this.X = x; + this.Y = y; + this.Width = width; + this.Height = height; } /// /// Initializes a new instance of the struct. /// - /// The vector. - public RectangleF(Vector4 vector) + /// + /// The which specifies the rectangles point in a two-dimensional plane. + /// + /// + /// The which specifies the rectangles height and width. + /// + public RectangleF(PointF point, SizeF size) { - this.backingVector = vector; + this.X = point.X; + this.Y = point.Y; + this.Width = size.Width; + this.Height = size.Height; } /// /// Gets or sets the x-coordinate of this . /// - public float X - { - get - { - return this.backingVector.X; - } - - set - { - this.backingVector.X = value; - } - } + public float X { get; set; } /// /// Gets or sets the y-coordinate of this . /// - public float Y - { - get - { - return this.backingVector.Y; - } - - set - { - this.backingVector.Y = value; - } - } + public float Y { get; set; } /// /// Gets or sets the width of this . /// - public float Width + public float Width { get; set; } + + /// + /// Gets or sets the height of this . + /// + public float Height { get; set; } + + /// + /// Gets or sets the coordinates of the upper-left corner of the rectangular region represented by this . + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public PointF Location { - get - { - return this.backingVector.Z; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new PointF(this.X, this.Y); + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { - this.backingVector.Z = value; + this.X = value.X; + this.Y = value.Y; } } /// - /// Gets or sets the height of this . + /// Gets or sets the size of this . /// - public float Height + [EditorBrowsable(EditorBrowsableState.Never)] + public SizeF Size { - get - { - return this.backingVector.W; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new SizeF(this.Width, this.Height); + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { - this.backingVector.W = value; + this.Width = value.Width; + this.Height = value.Height; } } @@ -117,141 +114,196 @@ namespace ImageSharp /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.Equals(Empty); + public bool IsEmpty => (this.Width <= 0) || (this.Height <= 0); /// /// Gets the y-coordinate of the top edge of this . /// - public float Top => this.Y; + public float Top + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.Y; + } + } /// /// Gets the x-coordinate of the right edge of this . /// - public float Right => this.X + this.Width; + public float Right + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.X + this.Width; + } + } /// /// Gets the y-coordinate of the bottom edge of this . /// - public float Bottom => this.Y + this.Height; + public float Bottom + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.Y + this.Height; + } + } /// /// Gets the x-coordinate of the left edge of this . /// - public float Left => this.X; + public float Left + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return this.X; + } + } /// - /// Performs an implicit conversion from to . + /// Creates a with the coordinates of the specified by truncating each coordinate. /// - /// The d. - /// - /// The result of the conversion. - /// - public static implicit operator RectangleF(Rectangle d) - { - return new RectangleF(d.Left, d.Top, d.Width, d.Height); - } + /// The rectangle + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Rectangle(RectangleF rectangle) => Rectangle.Truncate(rectangle); /// - /// Computes the sum of adding two rectangles. + /// Compares two objects for equality. /// - /// The rectangle on the left hand of the operand. - /// The rectangle on the right hand of the operand. + /// The on the left side of the operand. + /// The on the right side of the operand. /// - /// The + /// True if the current left is equal to the parameter; otherwise, false. /// - public static RectangleF operator +(RectangleF left, RectangleF right) - { - return new RectangleF(left.backingVector + right.backingVector); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(RectangleF left, RectangleF right) => left.Equals(right); /// - /// Computes the difference left by subtracting one rectangle from another. + /// Compares two objects for inequality. /// - /// The rectangle on the left hand of the operand. - /// The rectangle on the right hand of the operand. + /// The on the left side of the operand. + /// The on the right side of the operand. /// - /// The + /// True if the current left is unequal to the parameter; otherwise, false. /// - public static RectangleF operator -(RectangleF left, RectangleF right) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(RectangleF left, RectangleF right) => !left.Equals(right); + + /// + /// Creates a new with the specified location and size. + /// The left coordinate of the rectangle + /// The top coordinate of the rectangle + /// The right coordinate of the rectangle + /// The bottom coordinate of the rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + + // ReSharper disable once InconsistentNaming + public static RectangleF FromLTRB(float left, float top, float right, float bottom) => new RectangleF(left, top, right - left, bottom - top); + + /// + /// Returns the center point of the given + /// + /// The rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static PointF Center(RectangleF rectangle) => new PointF(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2)); + + /// + /// Creates a rectangle that represents the intersection between and + /// . If there is no intersection, an empty rectangle is returned. + /// + /// The first rectangle + /// The second rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RectangleF Intersect(RectangleF a, RectangleF b) { - return new RectangleF(left.backingVector - right.backingVector); + float x1 = MathF.Max(a.X, b.X); + float x2 = MathF.Min(a.Right, b.Right); + float y1 = MathF.Max(a.Y, b.Y); + float y2 = MathF.Min(a.Bottom, b.Bottom); + + if (x2 >= x1 && y2 >= y1) + { + return new RectangleF(x1, y1, x2 - x1, y2 - y1); + } + + return Empty; } /// - /// Compares two objects for equality. + /// Creates a that is inflated by the specified amount. /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - public static bool operator ==(RectangleF left, RectangleF right) + /// The rectangle + /// The amount to inflate the width by + /// The amount to inflate the height by + /// A new + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RectangleF Inflate(RectangleF rectangle, float x, float y) { - return left.Equals(right); + RectangleF r = rectangle; + r.Inflate(x, y); + return r; } /// - /// Compares two objects for inequality. + /// Creates a rectangle that represents the union between and . /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - public static bool operator !=(RectangleF left, RectangleF right) + /// The first rectangle + /// The second rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RectangleF Union(RectangleF a, RectangleF b) { - return !left.Equals(right); + float x1 = MathF.Min(a.X, b.X); + float x2 = MathF.Max(a.Right, b.Right); + float y1 = MathF.Min(a.Y, b.Y); + float y2 = MathF.Max(a.Bottom, b.Bottom); + + return new RectangleF(x1, y1, x2 - x1, y2 - y1); } /// - /// Returns the center point of the given + /// Creates a RectangleF that represents the intersection between this RectangleF and the . /// /// The rectangle - /// - public static Vector2 Center(RectangleF rectangle) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Intersect(RectangleF rectangle) { - return new Vector2(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2)); + RectangleF result = Intersect(rectangle, this); + + this.X = result.X; + this.Y = result.Y; + this.Width = result.Width; + this.Height = result.Height; } /// - /// Rounds the points away from the center this into a - /// by rounding the dimensions to the nerent integer ensuring that the new rectangle is - /// never smaller than the source + /// Inflates this by the specified amount. /// - /// The source area to round out - /// - /// The smallest that the will fit inside. - /// - public static Rectangle Ceiling(RectangleF source) + /// The width + /// The height + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Inflate(float width, float height) { - int y = (int)Math.Floor(source.Y); - int width = (int)Math.Ceiling(source.Width); - int x = (int)Math.Floor(source.X); - int height = (int)Math.Ceiling(source.Height); - return new Rectangle(x, y, width, height); + this.X -= width; + this.Y -= height; + + this.Width += 2 * width; + this.Height += 2 * height; } /// - /// Outsets the specified region. + /// Inflates this by the specified amount. /// - /// The region. - /// The width. - /// - /// The with all dimensions move away from the center by the offset. - /// - public static RectangleF Outset(RectangleF region, float width) - { - float dblWidth = width * 2; - return new RectangleF(region.X - width, region.Y - width, region.Width + dblWidth, region.Height + dblWidth); - } + /// The size + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Inflate(SizeF size) => this.Inflate(size.Width, size.Height); /// /// Determines if the specfied point is contained within the rectangular region defined by @@ -260,75 +312,89 @@ namespace ImageSharp /// The x-coordinate of the given point. /// The y-coordinate of the given point. /// The - public bool Contains(float x, float y) - { - // TODO: SIMD? - return this.X <= x - && x < this.Right - && this.Y <= y - && y < this.Bottom; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(float x, float y) => this.X <= x && x < this.Right && this.Y <= y && y < this.Bottom; + + /// + /// Determines if the specified point is contained within the rectangular region defined by this . + /// + /// The point + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(PointF point) => this.Contains(point.X, point.Y); + + /// + /// Determines if the rectangular region represented by is entirely contained + /// within the rectangular region represented by this . + /// + /// The rectangle + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Contains(RectangleF rectangle) => + (this.X <= rectangle.X) && (rectangle.Right <= this.Right) && + (this.Y <= rectangle.Y) && (rectangle.Bottom <= this.Bottom); /// - /// Determines if the specfied intersects the rectangular region defined by - /// this . + /// Determines if the specfied intersects the rectangular region defined by + /// this . /// - /// The other Rectange + /// The other Rectange /// The - public bool Intersects(RectangleF rect) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IntersectsWith(RectangleF rectangle) => + (rectangle.X < this.Right) && (this.X < rectangle.Right) && + (rectangle.Y < this.Bottom) && (this.Y < rectangle.Bottom); + + /// + /// Adjusts the location of this rectangle by the specified amount. + /// + /// The point + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(PointF point) => this.Offset(point.X, point.Y); + + /// + /// Adjusts the location of this rectangle by the specified amount. + /// + /// The amount to offset the x-coordinate. + /// The amount to offset the y-coordinate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Offset(float dx, float dy) { - return rect.Left <= this.Right && rect.Right >= this.Left - && - rect.Top <= this.Bottom && rect.Bottom >= this.Top; + this.X += dx; + this.Y += dy; } /// - public override int GetHashCode() - { - return this.GetHashCode(this); - } + public override int GetHashCode() => this.GetHashCode(this); /// public override string ToString() { if (this.IsEmpty) { - return "Rectangle [ Empty ]"; + return "RectangleF [ Empty ]"; } - return - $"Rectangle [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]"; + return $"RectangleF [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]"; } /// - public override bool Equals(object obj) - { - if (obj is RectangleF) - { - return this.Equals((RectangleF)obj); - } - - return false; - } + public override bool Equals(object obj) => obj is RectangleF && this.Equals((RectangleF)obj); /// - public bool Equals(RectangleF other) - { - return this.backingVector.Equals(other.backingVector); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(RectangleF other) => this.X.Equals(other.X) && this.Y.Equals(other.Y) && this.Width.Equals(other.Width) && this.Height.Equals(other.Height); - /// - /// Returns the hash code for this instance. - /// - /// - /// The instance of to return the hash code for. - /// - /// - /// A 32-bit signed integer that is the hash code for this instance. - /// private int GetHashCode(RectangleF rectangle) { - return rectangle.backingVector.GetHashCode(); + unchecked + { + int hashCode = rectangle.X.GetHashCode(); + hashCode = (hashCode * 397) ^ rectangle.Y.GetHashCode(); + hashCode = (hashCode * 397) ^ rectangle.Width.GetHashCode(); + hashCode = (hashCode * 397) ^ rectangle.Height.GetHashCode(); + return hashCode; + } } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Numerics/Size.cs b/src/ImageSharp/Numerics/Size.cs index bae645ac8e..79ee1ddead 100644 --- a/src/ImageSharp/Numerics/Size.cs +++ b/src/ImageSharp/Numerics/Size.cs @@ -23,6 +23,17 @@ namespace ImageSharp /// public static readonly Size Empty = default(Size); + /// + /// Initializes a new instance of the struct. + /// + /// The width and height of the size + public Size(int value) + : this() + { + this.Width = value; + this.Height = value; + } + /// /// Initializes a new instance of the struct. /// @@ -34,6 +45,27 @@ namespace ImageSharp this.Height = height; } + /// + /// Initializes a new instance of the struct. + /// + /// The size + public Size(Size size) + : this() + { + this.Width = size.Width; + this.Height = size.Height; + } + + /// + /// Initializes a new instance of the struct from the given . + /// + /// The point + public Size(Point point) + { + this.Width = point.X; + this.Height = point.Y; + } + /// /// Gets or sets the width of this . /// @@ -50,6 +82,20 @@ namespace ImageSharp [EditorBrowsable(EditorBrowsableState.Never)] public bool IsEmpty => this.Equals(Empty); + /// + /// Creates a with the dimensions of the specified . + /// + /// The point + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator SizeF(Size size) => new SizeF(size.Width, size.Height); + + /// + /// Converts the given into a . + /// + /// The size + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Point(Size size) => new Point(size.Width, size.Height); + /// /// Computes the sum of adding two sizes. /// @@ -59,10 +105,7 @@ namespace ImageSharp /// The /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Size operator +(Size left, Size right) - { - return new Size(left.Width + right.Width, left.Height + right.Height); - } + public static Size operator +(Size left, Size right) => Add(left, right); /// /// Computes the difference left by subtracting one size from another. @@ -73,10 +116,7 @@ namespace ImageSharp /// The /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Size operator -(Size left, Size right) - { - return new Size(left.Width - right.Width, left.Height - right.Height); - } + public static Size operator -(Size left, Size right) => Subtract(left, right); /// /// Compares two objects for equality. @@ -91,10 +131,7 @@ namespace ImageSharp /// True if the current left is equal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Size left, Size right) - { - return left.Equals(right); - } + public static bool operator ==(Size left, Size right) => left.Equals(right); /// /// Compares two objects for inequality. @@ -109,16 +146,52 @@ namespace ImageSharp /// True if the current left is unequal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Size left, Size right) - { - return !left.Equals(right); - } + public static bool operator !=(Size left, Size right) => !left.Equals(right); + + /// + /// Performs vector addition of two objects. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size Add(Size left, Size right) => new Size(unchecked(left.Width + right.Width), unchecked(left.Height + right.Height)); + + /// + /// Contracts a by another + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size Subtract(Size left, Size right) => new Size(unchecked(left.Width - right.Width), unchecked(left.Height - right.Height)); + + /// + /// Converts a to a by performing a ceiling operation on all the dimensions. + /// + /// The size + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size Ceiling(SizeF size) => new Size(unchecked((int)MathF.Ceiling(size.Width)), unchecked((int)MathF.Ceiling(size.Height))); + + /// + /// Converts a to a by performing a round operation on all the dimensions. + /// + /// The size + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size Round(SizeF size) => new Size(unchecked((int)MathF.Round(size.Width)), unchecked((int)MathF.Round(size.Height))); + + /// + /// Converts a to a by performing a round operation on all the dimensions. + /// + /// The size + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Size Truncate(SizeF size) => new Size(unchecked((int)size.Width), unchecked((int)size.Height)); /// - public override int GetHashCode() - { - return this.GetHashCode(this); - } + public override int GetHashCode() => this.GetHashCode(this); /// public override string ToString() @@ -132,22 +205,11 @@ namespace ImageSharp } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) - { - if (obj is Size) - { - return this.Equals((Size)obj); - } - - return false; - } + public override bool Equals(object obj) => obj is Size && this.Equals((Size)obj); /// - public bool Equals(Size other) - { - return this.Width == other.Width && this.Height == other.Height; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Size other) => this.Width == other.Width && this.Height == other.Height; /// /// Returns the hash code for this instance. @@ -158,9 +220,6 @@ namespace ImageSharp /// /// A 32-bit signed integer that is the hash code for this instance. /// - private int GetHashCode(Size size) - { - return size.Width ^ size.Height; - } + private int GetHashCode(Size size) => size.Width ^ size.Height; } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Numerics/SizeF.cs b/src/ImageSharp/Numerics/SizeF.cs new file mode 100644 index 0000000000..78078fd01f --- /dev/null +++ b/src/ImageSharp/Numerics/SizeF.cs @@ -0,0 +1,179 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.ComponentModel; + using System.Runtime.CompilerServices; + + /// + /// Stores an ordered pair of single precision floating points, which specify a height and width. + /// + /// + /// 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 SizeF : IEquatable + { + /// + /// Represents a that has Width and Height values set to zero. + /// + public static readonly SizeF Empty = default(SizeF); + + /// + /// Initializes a new instance of the struct. + /// + /// The width of the size. + /// The height of the size. + public SizeF(float width, float height) + { + this.Width = width; + this.Height = height; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The size + public SizeF(SizeF size) + : this() + { + this.Width = size.Width; + this.Height = size.Height; + } + + /// + /// Initializes a new instance of the struct from the given . + /// + /// The point + public SizeF(PointF point) + { + this.Width = point.X; + this.Height = point.Y; + } + + /// + /// Gets or sets the width of this . + /// + public float Width { get; set; } + + /// + /// Gets or sets the height of this . + /// + public float Height { get; set; } + + /// + /// Gets a value indicating whether this is empty. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IsEmpty => this.Equals(Empty); + + /// + /// Creates a with the dimensions of the specified by truncating each of the dimensions. + /// + /// The size. + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator Size(SizeF size) => new Size(unchecked((int)size.Width), unchecked((int)size.Height)); + + /// + /// Converts the given into a . + /// + /// The size + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator PointF(SizeF size) => new PointF(size.Width, size.Height); + + /// + /// Computes the sum of adding two sizes. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// The + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SizeF operator +(SizeF left, SizeF right) => Add(left, right); + + /// + /// Computes the difference left by subtracting one size from another. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// The + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SizeF operator -(SizeF left, SizeF right) => Subtract(left, right); + + /// + /// Compares two objects for equality. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(SizeF left, SizeF right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(SizeF left, SizeF right) => !left.Equals(right); + + /// + /// Performs vector addition of two objects. + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SizeF Add(SizeF left, SizeF right) => new SizeF(left.Width + right.Width, left.Height + right.Height); + + /// + /// Contracts a by another + /// + /// The size on the left hand of the operand. + /// The size on the right hand of the operand. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SizeF Subtract(SizeF left, SizeF right) => new SizeF(left.Width - right.Width, left.Height - right.Height); + + /// + public override int GetHashCode() + { + return this.GetHashCode(this); + } + + /// + public override string ToString() + { + if (this.IsEmpty) + { + return "SizeF [ Empty ]"; + } + + return $"SizeF [ Width={this.Width}, Height={this.Height} ]"; + } + + /// + public override bool Equals(object obj) => obj is SizeF && this.Equals((SizeF)obj); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(SizeF other) => this.Width.Equals(other.Width) && this.Height.Equals(other.Height); + + private int GetHashCode(SizeF size) => size.Width.GetHashCode() ^ size.Height.GetHashCode(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Alpha8.cs b/src/ImageSharp/PixelFormats/Alpha8.cs index c184ed9cf6..59934fdc64 100644 --- a/src/ImageSharp/PixelFormats/Alpha8.cs +++ b/src/ImageSharp/PixelFormats/Alpha8.cs @@ -62,7 +62,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -80,47 +80,43 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - this.PackedValue = w; + this.PackedValue = source.A; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { - bytes[startIndex] = 0; - bytes[startIndex + 1] = 0; - bytes[startIndex + 2] = 0; + dest = default(Rgb24); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { - bytes[startIndex] = 0; - bytes[startIndex + 1] = 0; - bytes[startIndex + 2] = 0; - bytes[startIndex + 3] = this.PackedValue; + dest.R = 0; + dest.G = 0; + dest.B = 0; + dest.A = this.PackedValue; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { - bytes[startIndex] = 0; - bytes[startIndex + 1] = 0; - bytes[startIndex + 2] = 0; + dest = default(Bgr24); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { - bytes[startIndex] = 0; - bytes[startIndex + 1] = 0; - bytes[startIndex + 2] = 0; - bytes[startIndex + 3] = this.PackedValue; + dest.R = 0; + dest.G = 0; + dest.B = 0; + dest.A = this.PackedValue; } /// diff --git a/src/ImageSharp/PixelFormats/Argb32.cs b/src/ImageSharp/PixelFormats/Argb32.cs index bd47f72f93..f389723dc3 100644 --- a/src/ImageSharp/PixelFormats/Argb32.cs +++ b/src/ImageSharp/PixelFormats/Argb32.cs @@ -59,11 +59,24 @@ namespace ImageSharp.PixelFormats /// The green component. /// The blue component. /// The alpha component. - public Argb32(byte r, byte g, byte b, byte a = 255) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Argb32(byte r, byte g, byte b, byte a) { this.PackedValue = Pack(r, g, b, a); } + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Argb32(byte r, byte g, byte b) + { + this.PackedValue = Pack(r, g, b, 255); + } + /// /// Initializes a new instance of the struct. /// @@ -224,7 +237,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -235,47 +248,47 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - this.PackedValue = Pack(x, y, z, w); + this.PackedValue = Pack(source.R, source.G, source.B, source.A); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { - bytes[startIndex] = this.R; - bytes[startIndex + 1] = this.G; - bytes[startIndex + 2] = this.B; + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { - bytes[startIndex] = this.R; - bytes[startIndex + 1] = this.G; - bytes[startIndex + 2] = this.B; - bytes[startIndex + 3] = this.A; + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; + dest.A = this.A; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { - bytes[startIndex] = this.B; - bytes[startIndex + 1] = this.G; - bytes[startIndex + 2] = this.R; + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { - bytes[startIndex] = this.B; - bytes[startIndex + 1] = this.G; - bytes[startIndex + 2] = this.R; - bytes[startIndex + 3] = this.A; + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; + dest.A = this.A; } /// diff --git a/src/ImageSharp/PixelFormats/Bgr24.cs b/src/ImageSharp/PixelFormats/Bgr24.cs new file mode 100644 index 0000000000..aaed5c3852 --- /dev/null +++ b/src/ImageSharp/PixelFormats/Bgr24.cs @@ -0,0 +1,135 @@ +// +// 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; + using System.Runtime.InteropServices; + + /// + /// Pixel type containing three 8-bit unsigned normalized values ranging from 0 to 255. + /// The color components are stored in blue, green, red order. + /// + [StructLayout(LayoutKind.Sequential)] + public struct Bgr24 : IPixel + { + /// + /// The blue component. + /// + public byte B; + + /// + /// The green component. + /// + public byte G; + + /// + /// The red component. + /// + public byte R; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Bgr24(byte r, byte g, byte b) + { + this.R = r; + this.G = g; + this.B = b; + } + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Bgr24 other) + { + return this.R == other.R && this.G == other.G && this.B == other.B; + } + + /// + public override bool Equals(object obj) + { + return obj?.GetType() == typeof(Bgr24) && this.Equals((Bgr24)obj); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() + { + unchecked + { + int hashCode = this.B; + hashCode = (hashCode * 397) ^ this.G; + hashCode = (hashCode * 397) ^ this.R; + return hashCode; + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PackFromRgba32(Rgba32 source) + { + this = source.Bgr; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PackFromVector4(Vector4 vector) + { + var rgba = default(Rgba32); + rgba.PackFromVector4(vector); + this.PackFromRgba32(rgba); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToVector4() + { + return new Rgba32(this.R, this.G, this.B, 255).ToVector4(); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgb24(ref Rgb24 dest) + { + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) + { + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; + dest.A = 255; + } + + /// + public void ToBgr24(ref Bgr24 dest) + { + dest = this; + } + + /// + public void ToBgra32(ref Bgra32 dest) + { + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; + dest.A = 255; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Bgr565.cs b/src/ImageSharp/PixelFormats/Bgr565.cs index 92bbac14cc..af22b14a09 100644 --- a/src/ImageSharp/PixelFormats/Bgr565.cs +++ b/src/ImageSharp/PixelFormats/Bgr565.cs @@ -71,7 +71,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// /// Expands the packed representation into a . @@ -103,51 +103,51 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - this.PackFromVector4(new Vector4(x, y, z, w) / 255F); + this.PackFromVector4(source.ToVector4()); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { Vector4 vector = this.ToVector4() * 255F; - bytes[startIndex] = (byte)MathF.Round(vector.X); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.Z); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { Vector4 vector = this.ToVector4() * 255F; - bytes[startIndex] = (byte)MathF.Round(vector.X); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.Z); - bytes[startIndex + 3] = (byte)MathF.Round(vector.W); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); + dest.A = (byte)MathF.Round(vector.W); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { Vector4 vector = this.ToVector4() * 255F; - bytes[startIndex] = (byte)MathF.Round(vector.Z); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.X); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { Vector4 vector = this.ToVector4() * 255F; - bytes[startIndex] = (byte)MathF.Round(vector.Z); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.X); - bytes[startIndex + 3] = (byte)MathF.Round(vector.W); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); + dest.A = (byte)MathF.Round(vector.W); } /// diff --git a/src/ImageSharp/PixelFormats/Bgra32.cs b/src/ImageSharp/PixelFormats/Bgra32.cs new file mode 100644 index 0000000000..f1ac20b567 --- /dev/null +++ b/src/ImageSharp/PixelFormats/Bgra32.cs @@ -0,0 +1,187 @@ +// +// 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; + using System.Runtime.InteropServices; + + /// + /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255. + /// The color components are stored in blue, green, red, and alpha order. + /// + [StructLayout(LayoutKind.Sequential)] + public struct Bgra32 : IPixel, IPackedVector + { + /// + /// Gets or sets the blue component. + /// + public byte B; + + /// + /// Gets or sets the green component. + /// + public byte G; + + /// + /// Gets or sets the red component. + /// + public byte R; + + /// + /// Gets or sets the alpha component. + /// + public byte A; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Bgra32(byte r, byte g, byte b) + { + this.R = r; + this.G = g; + this.B = b; + this.A = 255; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The alpha component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Bgra32(byte r, byte g, byte b, byte a) + { + this.R = r; + this.G = g; + this.B = b; + this.A = a; + } + + /// + /// Gets or sets the packed representation of the Bgra32 struct. + /// + public uint Bgra + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return Unsafe.As(ref this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + Unsafe.As(ref this) = value; + } + } + + /// + public uint PackedValue + { + get => this.Bgra; + set => this.Bgra = value; + } + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + public bool Equals(Bgra32 other) + { + return this.R == other.R && this.G == other.G && this.B == other.B && this.A == other.A; + } + + /// + public override bool Equals(object obj) => obj?.GetType() == typeof(Bgra32) && this.Equals((Bgra32)obj); + + /// + public override int GetHashCode() + { + unchecked + { + int hashCode = this.B; + hashCode = (hashCode * 397) ^ this.G; + hashCode = (hashCode * 397) ^ this.R; + hashCode = (hashCode * 397) ^ this.A; + return hashCode; + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PackFromVector4(Vector4 vector) + { + var rgba = default(Rgba32); + rgba.PackFromVector4(vector); + this.PackFromRgba32(rgba); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToVector4() + { + return this.ToRgba32().ToVector4(); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PackFromRgba32(Rgba32 source) + { + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = source.A; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgb24(ref Rgb24 dest) + { + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgba32(ref Rgba32 dest) + { + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; + dest.A = this.A; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToBgr24(ref Bgr24 dest) + { + dest = Unsafe.As(ref this); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToBgra32(ref Bgra32 dest) + { + dest = this; + } + + /// + /// Converts the pixel to format. + /// + /// The RGBA value + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgba32 ToRgba32() => new Rgba32(this.R, this.G, this.B, this.A); + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Bgra4444.cs b/src/ImageSharp/PixelFormats/Bgra4444.cs index 0bac00dfe3..746e1062ba 100644 --- a/src/ImageSharp/PixelFormats/Bgra4444.cs +++ b/src/ImageSharp/PixelFormats/Bgra4444.cs @@ -70,7 +70,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -79,10 +79,10 @@ namespace ImageSharp.PixelFormats const float Max = 1 / 15F; return new Vector4( - ((this.PackedValue >> 8) & 0x0F) * Max, - ((this.PackedValue >> 4) & 0x0F) * Max, - (this.PackedValue & 0x0F) * Max, - ((this.PackedValue >> 12) & 0x0F) * Max); + ((this.PackedValue >> 8) & 0x0F) * Max, + ((this.PackedValue >> 4) & 0x0F) * Max, + (this.PackedValue & 0x0F) * Max, + ((this.PackedValue >> 12) & 0x0F) * Max); } /// @@ -94,51 +94,51 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - this.PackFromVector4(new Vector4(x, y, z, w) / 255F); + this.PackFromVector4(source.ToVector4()); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { Vector4 vector = this.ToVector4() * 255F; - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.Z; + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { Vector4 vector = this.ToVector4() * 255F; - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.Z; - bytes[startIndex + 3] = (byte)vector.W; + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; + dest.A = (byte)vector.W; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { Vector4 vector = this.ToVector4() * 255F; - bytes[startIndex] = (byte)vector.Z; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { Vector4 vector = this.ToVector4() * 255F; - bytes[startIndex] = (byte)vector.Z; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; - bytes[startIndex + 3] = (byte)vector.W; + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; + dest.A = (byte)vector.W; } /// @@ -179,9 +179,9 @@ namespace ImageSharp.PixelFormats private static ushort Pack(float x, float y, float z, float w) { return (ushort)((((int)Math.Round(w.Clamp(0, 1) * 15F) & 0x0F) << 12) | - (((int)Math.Round(x.Clamp(0, 1) * 15F) & 0x0F) << 8) | - (((int)Math.Round(y.Clamp(0, 1) * 15F) & 0x0F) << 4) | - ((int)Math.Round(z.Clamp(0, 1) * 15F) & 0x0F)); + (((int)Math.Round(x.Clamp(0, 1) * 15F) & 0x0F) << 8) | + (((int)Math.Round(y.Clamp(0, 1) * 15F) & 0x0F) << 4) | + ((int)Math.Round(z.Clamp(0, 1) * 15F) & 0x0F)); } } } diff --git a/src/ImageSharp/PixelFormats/Bgra5551.cs b/src/ImageSharp/PixelFormats/Bgra5551.cs index f151db644a..198f911088 100644 --- a/src/ImageSharp/PixelFormats/Bgra5551.cs +++ b/src/ImageSharp/PixelFormats/Bgra5551.cs @@ -72,7 +72,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -94,51 +94,51 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - this.PackFromVector4(new Vector4(x, y, z, w) / 255F); + this.PackFromVector4(source.ToVector4()); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { - Vector4 vector = this.ToVector4() * 255F; - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.Z; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { - Vector4 vector = this.ToVector4() * 255F; - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.Z; - bytes[startIndex + 3] = (byte)vector.W; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; + dest.A = (byte)vector.W; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { - Vector4 vector = this.ToVector4() * 255F; - bytes[startIndex] = (byte)vector.Z; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { - Vector4 vector = this.ToVector4() * 255F; - bytes[startIndex] = (byte)vector.Z; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; - bytes[startIndex + 3] = (byte)vector.W; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; + dest.A = (byte)vector.W; } /// @@ -190,5 +190,8 @@ namespace ImageSharp.PixelFormats (((int)Math.Round(z.Clamp(0, 1) * 31F) & 0x1F) << 0) | (((int)Math.Round(w.Clamp(0, 1)) & 0x1) << 15)); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Vector4 ToScaledVector4() => this.ToVector4() * 255f; } } diff --git a/src/ImageSharp/PixelFormats/Byte4.cs b/src/ImageSharp/PixelFormats/Byte4.cs index 264bc74972..14053ba122 100644 --- a/src/ImageSharp/PixelFormats/Byte4.cs +++ b/src/ImageSharp/PixelFormats/Byte4.cs @@ -73,7 +73,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -95,51 +95,51 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - this.PackFromVector4(new Vector4(x, y, z, w)); + this.PackFromVector4(source.ToUnscaledVector4()); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { Vector4 vector = this.ToVector4(); - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.Z; + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { Vector4 vector = this.ToVector4(); - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.Z; - bytes[startIndex + 3] = (byte)vector.W; + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; + dest.A = (byte)vector.W; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { Vector4 vector = this.ToVector4(); - bytes[startIndex] = (byte)vector.Z; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { Vector4 vector = this.ToVector4(); - bytes[startIndex] = (byte)vector.Z; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; - bytes[startIndex + 3] = (byte)vector.W; + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; + dest.A = (byte)vector.W; } /// diff --git a/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs b/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs index 4b21130c0d..92fb006abf 100644 --- a/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs @@ -35,12 +35,13 @@ namespace ImageSharp.PixelFormats } TPixel result = default(TPixel); - - result.PackFromBytes( + Rgba32 rgba = new Rgba32( (byte)(packedValue >> 24), (byte)(packedValue >> 16), (byte)(packedValue >> 8), (byte)(packedValue >> 0)); + + result.PackFromRgba32(rgba); return result; } @@ -51,12 +52,7 @@ namespace ImageSharp.PixelFormats /// The green intensity. /// The blue intensity. /// Returns a that represents the color defined by the provided RGB values with 100% opacity. - public static TPixel FromRGB(byte red, byte green, byte blue) - { - TPixel color = default(TPixel); - color.PackFromBytes(red, green, blue, 255); - return color; - } + public static TPixel FromRGB(byte red, byte green, byte blue) => FromRGBA(red, green, blue, 255); /// /// Creates a new representation from standard RGBA bytes. @@ -69,7 +65,7 @@ namespace ImageSharp.PixelFormats public static TPixel FromRGBA(byte red, byte green, byte blue, byte alpha) { TPixel color = default(TPixel); - color.PackFromBytes(red, green, blue, alpha); + color.PackFromRgba32(new Rgba32(red, green, blue, alpha)); return color; } diff --git a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs new file mode 100644 index 0000000000..c042d76786 --- /dev/null +++ b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs @@ -0,0 +1,302 @@ +// + +// +// 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; + + public partial class PixelOperations + { + + /// + /// Converts 'count' elements in 'source` span of data to a span of -s. + /// + /// The source of data. + /// The to the destination pixels. + /// The number of pixels to convert. + internal virtual void PackFromRgba32(Span source, Span destPixels, int count) + { + GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); + + ref Rgba32 sourceRef = ref source.DangerousGetPinnableReference(); + ref TPixel destRef = ref destPixels.DangerousGetPinnableReference(); + + Rgba32 rgba = new Rgba32(0, 0, 0, 255); + + for (int i = 0; i < count; i++) + { + ref TPixel dp = ref Unsafe.Add(ref destRef, i); + rgba = Unsafe.Add(ref sourceRef, i); + dp.PackFromRgba32(rgba); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void PackFromRgba32Bytes(Span sourceBytes, Span destPixels, int count) + { + this.PackFromRgba32(sourceBytes.NonPortableCast(), destPixels, count); + } + + /// + /// Converts 'count' pixels in 'sourcePixels` span to a span of -s. + /// Bulk version of . + /// + /// The span of source pixels + /// The destination span of data. + /// The number of pixels to convert. + internal virtual void ToRgba32(Span sourcePixels, Span dest, int count) + { + GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); + + ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference(); + ref Rgba32 destBaseRef = ref dest.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destBaseRef, i); + sp.ToRgba32(ref dp); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ToRgba32Bytes(Span sourceColors, Span destBytes, int count) + { + this.ToRgba32(sourceColors, destBytes.NonPortableCast(), count); + } + + /// + /// Converts 'count' elements in 'source` span of data to a span of -s. + /// + /// The source of data. + /// The to the destination pixels. + /// The number of pixels to convert. + internal virtual void PackFromBgra32(Span source, Span destPixels, int count) + { + GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); + + ref Bgra32 sourceRef = ref source.DangerousGetPinnableReference(); + ref TPixel destRef = ref destPixels.DangerousGetPinnableReference(); + + Rgba32 rgba = new Rgba32(0, 0, 0, 255); + + for (int i = 0; i < count; i++) + { + ref TPixel dp = ref Unsafe.Add(ref destRef, i); + rgba = Unsafe.Add(ref sourceRef, i).ToRgba32(); + dp.PackFromRgba32(rgba); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void PackFromBgra32Bytes(Span sourceBytes, Span destPixels, int count) + { + this.PackFromBgra32(sourceBytes.NonPortableCast(), destPixels, count); + } + + /// + /// Converts 'count' pixels in 'sourcePixels` span to a span of -s. + /// Bulk version of . + /// + /// The span of source pixels + /// The destination span of data. + /// The number of pixels to convert. + internal virtual void ToBgra32(Span sourcePixels, Span dest, int count) + { + GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); + + ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference(); + ref Bgra32 destBaseRef = ref dest.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Bgra32 dp = ref Unsafe.Add(ref destBaseRef, i); + sp.ToBgra32(ref dp); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ToBgra32Bytes(Span sourceColors, Span destBytes, int count) + { + this.ToBgra32(sourceColors, destBytes.NonPortableCast(), count); + } + + /// + /// Converts 'count' elements in 'source` span of data to a span of -s. + /// + /// The source of data. + /// The to the destination pixels. + /// The number of pixels to convert. + internal virtual void PackFromRgb24(Span source, Span destPixels, int count) + { + GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); + + ref Rgb24 sourceRef = ref source.DangerousGetPinnableReference(); + ref TPixel destRef = ref destPixels.DangerousGetPinnableReference(); + + Rgba32 rgba = new Rgba32(0, 0, 0, 255); + + for (int i = 0; i < count; i++) + { + ref TPixel dp = ref Unsafe.Add(ref destRef, i); + rgba.Rgb = Unsafe.Add(ref sourceRef, i); + dp.PackFromRgba32(rgba); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void PackFromRgb24Bytes(Span sourceBytes, Span destPixels, int count) + { + this.PackFromRgb24(sourceBytes.NonPortableCast(), destPixels, count); + } + + /// + /// Converts 'count' pixels in 'sourcePixels` span to a span of -s. + /// Bulk version of . + /// + /// The span of source pixels + /// The destination span of data. + /// The number of pixels to convert. + internal virtual void ToRgb24(Span sourcePixels, Span dest, int count) + { + GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); + + ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference(); + ref Rgb24 destBaseRef = ref dest.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Rgb24 dp = ref Unsafe.Add(ref destBaseRef, i); + sp.ToRgb24(ref dp); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ToRgb24Bytes(Span sourceColors, Span destBytes, int count) + { + this.ToRgb24(sourceColors, destBytes.NonPortableCast(), count); + } + + /// + /// Converts 'count' elements in 'source` span of data to a span of -s. + /// + /// The source of data. + /// The to the destination pixels. + /// The number of pixels to convert. + internal virtual void PackFromBgr24(Span source, Span destPixels, int count) + { + GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); + + ref Bgr24 sourceRef = ref source.DangerousGetPinnableReference(); + ref TPixel destRef = ref destPixels.DangerousGetPinnableReference(); + + Rgba32 rgba = new Rgba32(0, 0, 0, 255); + + for (int i = 0; i < count; i++) + { + ref TPixel dp = ref Unsafe.Add(ref destRef, i); + rgba.Bgr = Unsafe.Add(ref sourceRef, i); + dp.PackFromRgba32(rgba); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void PackFromBgr24Bytes(Span sourceBytes, Span destPixels, int count) + { + this.PackFromBgr24(sourceBytes.NonPortableCast(), destPixels, count); + } + + /// + /// Converts 'count' pixels in 'sourcePixels` span to a span of -s. + /// Bulk version of . + /// + /// The span of source pixels + /// The destination span of data. + /// The number of pixels to convert. + internal virtual void ToBgr24(Span sourcePixels, Span dest, int count) + { + GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); + + ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference(); + ref Bgr24 destBaseRef = ref dest.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Bgr24 dp = ref Unsafe.Add(ref destBaseRef, i); + sp.ToBgr24(ref dp); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ToBgr24Bytes(Span sourceColors, Span destBytes, int count) + { + this.ToBgr24(sourceColors, destBytes.NonPortableCast(), count); + } + + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt new file mode 100644 index 0000000000..16292489fc --- /dev/null +++ b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt @@ -0,0 +1,130 @@ +<# +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// +#> +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +<# + void GenerateToDestFormatMethods(string pixelType) + { + #> + + /// + /// Converts 'count' pixels in 'sourcePixels` span to a span of -s. + /// Bulk version of . + /// + /// The span of source pixels + /// The destination span of data. + /// The number of pixels to convert. + internal virtual void To<#=pixelType#>(Span sourcePixels, Span<<#=pixelType#>> dest, int count) + { + GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); + + ref TPixel sourceBaseRef = ref sourcePixels.DangerousGetPinnableReference(); + ref <#=pixelType#> destBaseRef = ref dest.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref <#=pixelType#> dp = ref Unsafe.Add(ref destBaseRef, i); + sp.To<#=pixelType#>(ref dp); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void To<#=pixelType#>Bytes(Span sourceColors, Span destBytes, int count) + { + this.To<#=pixelType#>(sourceColors, destBytes.NonPortableCast>(), count); + } + <# + } + + void GeneratePackFromMethodUsingPackFromRgba32(string pixelType, string rgbaOperationCode) + { + #> + + /// + /// Converts 'count' elements in 'source` span of data to a span of -s. + /// + /// The source of data. + /// The to the destination pixels. + /// The number of pixels to convert. + internal virtual void PackFrom<#=pixelType#>(Span<<#=pixelType#>> source, Span destPixels, int count) + { + GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); + + ref <#=pixelType#> sourceRef = ref source.DangerousGetPinnableReference(); + ref TPixel destRef = ref destPixels.DangerousGetPinnableReference(); + + Rgba32 rgba = new Rgba32(0, 0, 0, 255); + + for (int i = 0; i < count; i++) + { + ref TPixel dp = ref Unsafe.Add(ref destRef, i); + <#=rgbaOperationCode#> + dp.PackFromRgba32(rgba); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void PackFrom<#=pixelType#>Bytes(Span sourceBytes, Span destPixels, int count) + { + this.PackFrom<#=pixelType#>(sourceBytes.NonPortableCast>(), destPixels, count); + } + <# + } + +#> +// + +// +// 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; + + public partial class PixelOperations + { + <# + + GeneratePackFromMethodUsingPackFromRgba32("Rgba32", "rgba = Unsafe.Add(ref sourceRef, i);"); + GenerateToDestFormatMethods("Rgba32"); + + GeneratePackFromMethodUsingPackFromRgba32("Bgra32", "rgba = Unsafe.Add(ref sourceRef, i).ToRgba32();"); + GenerateToDestFormatMethods("Bgra32"); + + GeneratePackFromMethodUsingPackFromRgba32("Rgb24", "rgba.Rgb = Unsafe.Add(ref sourceRef, i);"); + GenerateToDestFormatMethods("Rgb24"); + + GeneratePackFromMethodUsingPackFromRgba32("Bgr24", "rgba.Bgr = Unsafe.Add(ref sourceRef, i);"); + GenerateToDestFormatMethods("Bgr24"); + + #> + + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs new file mode 100644 index 0000000000..e42c575d89 --- /dev/null +++ b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs @@ -0,0 +1,130 @@ +// + +// +// 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 + { + internal partial class PixelOperations : PixelOperations + { + + /// + internal override void PackFromRgb24(Span source, Span destPixels, int count) + { + GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); + + ref Rgb24 sourceRef = ref source.DangerousGetPinnableReference(); + ref Rgba32 destRef = ref destPixels.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 ToRgb24(Span sourcePixels, Span dest, int count) + { + GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); + + ref Rgba32 sourceRef = ref sourcePixels.DangerousGetPinnableReference(); + ref Rgb24 destRef = ref dest.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 void PackFromBgr24(Span source, Span destPixels, int count) + { + GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); + + ref Bgr24 sourceRef = ref source.DangerousGetPinnableReference(); + ref Rgba32 destRef = ref destPixels.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); + dp.Bgr = sp; dp.A = 255; + } + } + + + /// + internal override void ToBgr24(Span sourcePixels, Span dest, int count) + { + GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); + + ref Rgba32 sourceRef = ref sourcePixels.DangerousGetPinnableReference(); + ref Bgr24 destRef = ref dest.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); + dp = sp.Bgr; + } + } + + + /// + internal override void PackFromBgra32(Span source, Span destPixels, int count) + { + GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); + + ref Bgra32 sourceRef = ref source.DangerousGetPinnableReference(); + ref Rgba32 destRef = ref destPixels.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); + dp = sp.ToRgba32(); + } + } + + + /// + internal override void ToBgra32(Span sourcePixels, Span dest, int count) + { + GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); + + ref Rgba32 sourceRef = ref sourcePixels.DangerousGetPinnableReference(); + ref Bgra32 destRef = ref dest.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); + dp = sp.ToBgra32(); + } + } + + + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt new file mode 100644 index 0000000000..9c01fa9157 --- /dev/null +++ b/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt @@ -0,0 +1,99 @@ +<# +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// +#> +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ output extension=".cs" #> +<# + + void GeneratePackFromMethod(string pixelType, string converterCode) + { + #> + + /// + internal override void PackFrom<#=pixelType#>(Span<<#=pixelType#>> source, Span destPixels, int count) + { + GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); + + ref <#=pixelType#> sourceRef = ref source.DangerousGetPinnableReference(); + ref Rgba32 destRef = ref destPixels.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref <#=pixelType#> sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); + <#=converterCode#> + } + } + + <# + } + + void GenerateConvertToMethod(string pixelType, string converterCode) + { + #> + + /// + internal override void To<#=pixelType#>(Span sourcePixels, Span<<#=pixelType#>> dest, int count) + { + GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); + + ref Rgba32 sourceRef = ref sourcePixels.DangerousGetPinnableReference(); + ref <#=pixelType#> destRef = ref dest.DangerousGetPinnableReference(); + + for (int i = 0; i < count; i++) + { + ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); + ref <#=pixelType#> dp = ref Unsafe.Add(ref destRef, i); + <#=converterCode#> + } + } + + <# + } + +#> +// + +// +// 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 + { + internal partial class PixelOperations : PixelOperations + { + <# + GeneratePackFromMethod("Rgb24", "Unsafe.As(ref dp) = sp; dp.A = 255;"); + GenerateConvertToMethod("Rgb24", "dp = Unsafe.As(ref sp);"); + + GeneratePackFromMethod("Bgr24", "dp.Bgr = sp; dp.A = 255;"); + GenerateConvertToMethod("Bgr24", "dp = sp.Bgr;"); + + GeneratePackFromMethod("Bgra32", "dp = sp.ToRgba32();"); + GenerateConvertToMethod("Bgra32", "dp = sp.ToBgra32();"); + #> + + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/HalfSingle.cs b/src/ImageSharp/PixelFormats/HalfSingle.cs index 4cc9acc222..3bdfc9f1cf 100644 --- a/src/ImageSharp/PixelFormats/HalfSingle.cs +++ b/src/ImageSharp/PixelFormats/HalfSingle.cs @@ -76,7 +76,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// /// Expands the packed representation into a . @@ -104,67 +104,51 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - this.PackFromVector4(new Vector4(x, y, z, w) / MaxBytes); + this.PackFromVector4(source.ToVector4()); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { - Vector4 vector = this.ToVector4(); - vector *= MaxBytes; - vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.Z; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { - Vector4 vector = this.ToVector4(); - vector *= MaxBytes; - vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.Z; - bytes[startIndex + 3] = (byte)vector.W; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; + dest.A = (byte)vector.W; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { - Vector4 vector = this.ToVector4(); - vector *= MaxBytes; - vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.Z; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { - Vector4 vector = this.ToVector4(); - vector *= MaxBytes; - vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.Z; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; - bytes[startIndex + 3] = (byte)vector.W; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; + dest.A = (byte)vector.W; } /// @@ -192,5 +176,15 @@ namespace ImageSharp.PixelFormats { return this.PackedValue.GetHashCode(); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Vector4 ToScaledVector4() + { + Vector4 vector = this.ToVector4(); + vector *= MaxBytes; + vector += Half; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + return vector; + } } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/HalfVector2.cs b/src/ImageSharp/PixelFormats/HalfVector2.cs index f490f71690..7f1fe4ebdd 100644 --- a/src/ImageSharp/PixelFormats/HalfVector2.cs +++ b/src/ImageSharp/PixelFormats/HalfVector2.cs @@ -86,7 +86,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// /// Expands the packed representation into a . @@ -118,67 +118,51 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - this.PackFromVector4(new Vector4(x, y, z, w) / MaxBytes); + this.PackFromVector4(source.ToVector4()); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { - Vector4 vector = this.ToVector4(); - vector *= MaxBytes; - vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.Z; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = 0; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { - Vector4 vector = this.ToVector4(); - vector *= MaxBytes; - vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.Z; - bytes[startIndex + 3] = (byte)vector.W; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = 0; + dest.A = 255; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { - Vector4 vector = this.ToVector4(); - vector *= MaxBytes; - vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.Z; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = 0; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { - Vector4 vector = this.ToVector4(); - vector *= MaxBytes; - vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.Z; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; - bytes[startIndex + 3] = (byte)vector.W; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = 0; + dest.A = 255; } /// @@ -220,5 +204,15 @@ namespace ImageSharp.PixelFormats uint num = (uint)(HalfTypeHelper.Pack(y) << 0x10); return num2 | num; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Vector4 ToScaledVector4() + { + Vector4 vector = this.ToVector4(); + vector *= MaxBytes; + vector += Half; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + return vector; + } } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/HalfVector4.cs b/src/ImageSharp/PixelFormats/HalfVector4.cs index 7c496c161b..062287dbe6 100644 --- a/src/ImageSharp/PixelFormats/HalfVector4.cs +++ b/src/ImageSharp/PixelFormats/HalfVector4.cs @@ -89,7 +89,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -111,67 +111,51 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - this.PackFromVector4(new Vector4(x, y, z, w) / MaxBytes); + this.PackFromVector4(source.ToVector4()); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { - Vector4 vector = this.ToVector4(); - vector *= MaxBytes; - vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.Z; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { - Vector4 vector = this.ToVector4(); - vector *= MaxBytes; - vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.Z; - bytes[startIndex + 3] = (byte)vector.W; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; + dest.A = (byte)vector.W; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { - Vector4 vector = this.ToVector4(); - vector *= MaxBytes; - vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.Z; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { - Vector4 vector = this.ToVector4(); - vector *= MaxBytes; - vector += Half; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.Z; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; - bytes[startIndex + 3] = (byte)vector.W; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; + dest.A = (byte)vector.W; } /// @@ -214,5 +198,15 @@ namespace ImageSharp.PixelFormats ulong num1 = (ulong)HalfTypeHelper.Pack(vector.W) << 0x30; return num4 | num3 | num2 | num1; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Vector4 ToScaledVector4() + { + Vector4 vector = this.ToVector4(); + vector *= MaxBytes; + vector += Half; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + return vector; + } } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/IPixel.cs b/src/ImageSharp/PixelFormats/IPixel.cs index 030cb93f46..9090e1210c 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -20,7 +20,7 @@ namespace ImageSharp.PixelFormats /// This method is not intended to be consumed directly. Use instead. /// /// The instance. - PixelOperations CreateBulkOperations(); + PixelOperations CreatePixelOperations(); } /// @@ -42,44 +42,33 @@ namespace ImageSharp.PixelFormats Vector4 ToVector4(); /// - /// Sets the packed representation from the given byte array. + /// Packs the pixel from an value. /// - /// The x-component. - /// The y-component. - /// The z-component. - /// The w-component. - void PackFromBytes(byte x, byte y, byte z, byte w); + /// The value. + void PackFromRgba32(Rgba32 source); /// - /// Expands the packed representation into a given byte array. - /// Output is expanded to X-> Y-> Z order. Equivalent to R-> G-> B in + /// Converts the pixel to format. /// - /// The bytes to set the color in. - /// The starting index of the . - void ToXyzBytes(Span bytes, int startIndex); + /// The destination pixel to write to + void ToRgb24(ref Rgb24 dest); /// - /// 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 + /// Converts the pixel to format. /// - /// The bytes to set the color in. - /// The starting index of the . - void ToXyzwBytes(Span bytes, int startIndex); + /// The destination pixel to write to + void ToRgba32(ref Rgba32 dest); /// - /// Expands the packed representation into a given byte array. - /// Output is expanded to Z-> Y-> X order. Equivalent to B-> G-> R in + /// Converts the pixel to format. /// - /// The bytes to set the color in. - /// The starting index of the . - void ToZyxBytes(Span bytes, int startIndex); + /// The destination pixel to write to + void ToBgr24(ref Bgr24 dest); /// - /// 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 + /// Converts the pixel to format. /// - /// The bytes to set the color in. - /// The starting index of the . - void ToZyxwBytes(Span bytes, int startIndex); + /// The destination pixel to write to + void ToBgra32(ref Bgra32 dest); } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/NormalizedByte2.cs index 47a4f30059..992986f92b 100644 --- a/src/ImageSharp/PixelFormats/NormalizedByte2.cs +++ b/src/ImageSharp/PixelFormats/NormalizedByte2.cs @@ -91,7 +91,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// /// Expands the packed representation into a . @@ -122,9 +122,9 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - Vector4 vector = new Vector4(x, y, z, w); + Vector4 vector = source.ToUnscaledVector4(); vector -= Round; vector -= Half; vector -= Round; @@ -134,68 +134,44 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { - Vector4 vector = this.ToVector4(); - vector *= Half; - vector += Round; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = 0; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = 0; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { - Vector4 vector = this.ToVector4(); - vector *= Half; - vector += Round; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = 0; - bytes[startIndex + 3] = 255; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = 0; + dest.A = 255; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { - Vector4 vector = this.ToVector4(); - vector *= Half; - vector += Round; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = 0; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = 0; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { - Vector4 vector = this.ToVector4(); - vector *= Half; - vector += Round; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = 0; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; - bytes[startIndex + 3] = 255; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = 0; + dest.A = 255; } /// @@ -238,5 +214,17 @@ namespace ImageSharp.PixelFormats return (ushort)(byte2 | byte1); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Vector4 ToScaledVector4() + { + Vector4 vector = this.ToVector4(); + vector *= Half; + vector += Round; + vector += Half; + vector += Round; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + return vector; + } } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/NormalizedByte4.cs index 4559bd082f..99f603f690 100644 --- a/src/ImageSharp/PixelFormats/NormalizedByte4.cs +++ b/src/ImageSharp/PixelFormats/NormalizedByte4.cs @@ -93,7 +93,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -115,9 +115,9 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - Vector4 vector = new Vector4(x, y, z, w); + Vector4 vector = source.ToUnscaledVector4(); vector -= Round; vector -= Half; vector -= Round; @@ -127,68 +127,44 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { - Vector4 vector = this.ToVector4(); - vector *= Half; - vector += Round; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.Z; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { - Vector4 vector = this.ToVector4(); - vector *= Half; - vector += Round; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.Z; - bytes[startIndex + 3] = (byte)vector.W; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; + dest.A = (byte)vector.W; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { - Vector4 vector = this.ToVector4(); - vector *= Half; - vector += Round; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.Z; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { - Vector4 vector = this.ToVector4(); - vector *= Half; - vector += Round; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)vector.Z; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; - bytes[startIndex + 3] = (byte)vector.W; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; + dest.A = (byte)vector.W; } /// @@ -235,5 +211,17 @@ namespace ImageSharp.PixelFormats return byte4 | byte3 | byte2 | byte1; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Vector4 ToScaledVector4() + { + Vector4 vector = this.ToVector4(); + vector *= Half; + vector += Round; + vector += Half; + vector += Round; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + return vector; + } } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/NormalizedShort2.cs index 648b68905a..a0615563f0 100644 --- a/src/ImageSharp/PixelFormats/NormalizedShort2.cs +++ b/src/ImageSharp/PixelFormats/NormalizedShort2.cs @@ -91,7 +91,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -109,9 +109,9 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - Vector4 vector = new Vector4(x, y, z, w); + Vector4 vector = source.ToUnscaledVector4(); vector -= Round; vector -= Half; vector -= Round; @@ -121,68 +121,44 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { - Vector4 vector = this.ToVector4(); - vector *= Half; - vector += Round; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)MathF.Round(vector.X); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = 0; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = 0; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { - Vector4 vector = this.ToVector4(); - vector *= Half; - vector += Round; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)MathF.Round(vector.X); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = 0; - bytes[startIndex + 3] = 255; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = 0; + dest.A = 255; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { - Vector4 vector = this.ToVector4(); - vector *= Half; - vector += Round; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = 0; - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.X); + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = 0; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { - Vector4 vector = this.ToVector4(); - vector *= Half; - vector += Round; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = 0; - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.X); - bytes[startIndex + 3] = 255; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = 0; + dest.A = 255; } /// @@ -245,5 +221,17 @@ namespace ImageSharp.PixelFormats return word2 | word1; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Vector4 ToScaledVector4() + { + Vector4 vector = this.ToVector4(); + vector *= Half; + vector += Round; + vector += Half; + vector += Round; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + return vector; + } } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/NormalizedShort4.cs index 7b520aacef..f35fb63684 100644 --- a/src/ImageSharp/PixelFormats/NormalizedShort4.cs +++ b/src/ImageSharp/PixelFormats/NormalizedShort4.cs @@ -93,7 +93,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -117,9 +117,9 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - Vector4 vector = new Vector4(x, y, z, w); + Vector4 vector = source.ToUnscaledVector4(); vector -= Round; vector -= Half; vector -= Round; @@ -129,68 +129,44 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { - Vector4 vector = this.ToVector4(); - vector *= Half; - vector += Round; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)MathF.Round(vector.X); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.Z); + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { - Vector4 vector = this.ToVector4(); - vector *= Half; - vector += Round; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)MathF.Round(vector.X); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.Z); - bytes[startIndex + 3] = (byte)MathF.Round(vector.W); + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); + dest.A = (byte)MathF.Round(vector.W); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { - Vector4 vector = this.ToVector4(); - vector *= Half; - vector += Round; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)MathF.Round(vector.Z); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.X); + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { - Vector4 vector = this.ToVector4(); - vector *= Half; - vector += Round; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)MathF.Round(vector.Z); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.X); - bytes[startIndex + 3] = (byte)MathF.Round(vector.W); + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); + dest.A = (byte)MathF.Round(vector.W); } /// @@ -241,5 +217,17 @@ namespace ImageSharp.PixelFormats return word4 | word3 | word2 | word1; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Vector4 ToScaledVector4() + { + Vector4 vector = this.ToVector4(); + vector *= Half; + vector += Round; + vector += Half; + vector += Round; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + return vector; + } } } diff --git a/src/ImageSharp/PixelFormats/PixelConversionExtensions.cs b/src/ImageSharp/PixelFormats/PixelConversionExtensions.cs new file mode 100644 index 0000000000..1ea2628951 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelConversionExtensions.cs @@ -0,0 +1,76 @@ +namespace ImageSharp.PixelFormats +{ + using System; + using System.Runtime.CompilerServices; + + /// + /// Extension methods for copying single pixel data into byte Spans. + /// TODO: This utility class exists for legacy reasons. Need to do a lot of chore work to remove it (mostly in test classes). + /// + internal static class PixelConversionExtensions + { + /// + /// Expands the packed representation into a given byte array. + /// Output is expanded to X-> Y-> Z order. Equivalent to R-> G-> B in + /// + /// The pixel type. + /// The pixel to copy the data from. + /// The bytes to set the color in. + /// The starting index of the . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ToXyzBytes(this TPixel pixel, Span bytes, int startIndex) + where TPixel : struct, IPixel + { + ref Rgb24 dest = ref bytes.GetRgb24(startIndex); + pixel.ToRgb24(ref dest); + } + + /// + /// 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 + /// + /// The pixel type. + /// The pixel to copy the data from. + /// The bytes to set the color in. + /// The starting index of the . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ToXyzwBytes(this TPixel pixel, Span bytes, int startIndex) + where TPixel : struct, IPixel + { + ref Rgba32 dest = ref Unsafe.As(ref bytes[startIndex]); + pixel.ToRgba32(ref dest); + } + + /// + /// Expands the packed representation into a given byte array. + /// Output is expanded to Z-> Y-> X order. Equivalent to B-> G-> R in + /// + /// The pixel type. + /// The pixel to copy the data from. + /// The bytes to set the color in. + /// The starting index of the . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ToZyxBytes(this TPixel pixel, Span bytes, int startIndex) + where TPixel : struct, IPixel + { + ref Bgr24 dest = ref Unsafe.As(ref bytes[startIndex]); + pixel.ToBgr24(ref dest); + } + + /// + /// 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 + /// + /// The pixel type. + /// The pixel to copy the data from. + /// The bytes to set the color in. + /// The starting index of the . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ToZyxwBytes(this TPixel pixel, Span bytes, int startIndex) + where TPixel : struct, IPixel + { + ref Bgra32 dest = ref Unsafe.As(ref bytes[startIndex]); + pixel.ToBgra32(ref dest); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index 993a11232a..a62d14527a 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -20,7 +20,7 @@ namespace ImageSharp.PixelFormats /// /// Gets the global instance for the pixel type /// - public static PixelOperations Instance { get; } = default(TPixel).CreateBulkOperations(); + public static PixelOperations Instance { get; } = default(TPixel).CreatePixelOperations(); /// /// Bulk version of @@ -30,8 +30,7 @@ namespace ImageSharp.PixelFormats /// 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)); + GuardSpans(sourceVectors, nameof(sourceVectors), destColors, nameof(destColors), count); ref Vector4 sourceRef = ref sourceVectors.DangerousGetPinnableReference(); ref TPixel destRef = ref destColors.DangerousGetPinnableReference(); @@ -52,8 +51,7 @@ namespace ImageSharp.PixelFormats /// 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)); + GuardSpans(sourceColors, nameof(sourceColors), destVectors, nameof(destVectors), count); ref TPixel sourceRef = ref sourceColors.DangerousGetPinnableReference(); ref Vector4 destRef = ref destVectors.DangerousGetPinnableReference(); @@ -67,187 +65,25 @@ namespace ImageSharp.PixelFormats } /// - /// Bulk version of that converts data in . + /// Verifies that the given 'source' and 'dest' spans are at least of 'minLength' size. + /// Throwing an if the condition is not met. /// - /// 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) + /// The source element type + /// The destination element type + /// The source span + /// The source parameter name + /// The destination span + /// The destination parameter name + /// The minimum length + protected internal static void GuardSpans( + Span source, + string sourceParamName, + Span dest, + string destParamName, + int minLength) { - 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); - } + Guard.MustBeSizedAtLeast(source, minLength, sourceParamName); + Guard.MustBeSizedAtLeast(dest, minLength, destParamName); } } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Rg32.cs b/src/ImageSharp/PixelFormats/Rg32.cs index ea7d8729b4..0575689a77 100644 --- a/src/ImageSharp/PixelFormats/Rg32.cs +++ b/src/ImageSharp/PixelFormats/Rg32.cs @@ -76,7 +76,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// /// Expands the packed representation into a . @@ -107,55 +107,51 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - this.PackFromVector4(new Vector4(x, y, z, w) / 255F); + this.PackFromVector4(source.ToVector4()); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { - Vector4 vector = this.ToVector4() * 255F; - - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.Z; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { - Vector4 vector = this.ToVector4() * 255F; - - bytes[startIndex] = (byte)vector.X; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.Z; - bytes[startIndex + 3] = (byte)vector.W; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; + dest.A = (byte)vector.W; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { - Vector4 vector = this.ToVector4() * 255F; - - bytes[startIndex] = (byte)vector.Z; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { - Vector4 vector = this.ToVector4() * 255F; - - bytes[startIndex] = (byte)vector.Z; - bytes[startIndex + 1] = (byte)vector.Y; - bytes[startIndex + 2] = (byte)vector.X; - bytes[startIndex + 3] = (byte)vector.W; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)vector.X; + dest.G = (byte)vector.Y; + dest.B = (byte)vector.Z; + dest.A = (byte)vector.W; } /// @@ -197,5 +193,8 @@ namespace ImageSharp.PixelFormats ((int)Math.Round(x.Clamp(0, 1) * 65535F) & 0xFFFF) | (((int)Math.Round(y.Clamp(0, 1) * 65535F) & 0xFFFF) << 16)); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Vector4 ToScaledVector4() => this.ToVector4() * 255f; } } diff --git a/src/ImageSharp/PixelFormats/Rgb24.cs b/src/ImageSharp/PixelFormats/Rgb24.cs new file mode 100644 index 0000000000..b8cc8dc24f --- /dev/null +++ b/src/ImageSharp/PixelFormats/Rgb24.cs @@ -0,0 +1,132 @@ +// +// 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; + using System.Runtime.InteropServices; + + /// + /// Pixel type containing three 8-bit unsigned normalized values ranging from 0 to 255. + /// The color components are stored in red, green, blue order. + /// + [StructLayout(LayoutKind.Sequential)] + public struct Rgb24 : IPixel + { + /// + /// The red component. + /// + public byte R; + + /// + /// The green component. + /// + public byte G; + + /// + /// The blue component. + /// + public byte B; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgb24(byte r, byte g, byte b) + { + this.R = r; + this.G = g; + this.B = b; + } + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Rgb24 other) + { + return this.R == other.R && this.G == other.G && this.B == other.B; + } + + /// + public override bool Equals(object obj) + { + return obj?.GetType() == typeof(Rgb24) && this.Equals((Rgb24)obj); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() + { + unchecked + { + int hashCode = this.R; + hashCode = (hashCode * 397) ^ this.G; + hashCode = (hashCode * 397) ^ this.B; + return hashCode; + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PackFromRgba32(Rgba32 source) + { + this = Unsafe.As(ref source); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PackFromVector4(Vector4 vector) + { + var rgba = default(Rgba32); + rgba.PackFromVector4(vector); + this.PackFromRgba32(rgba); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToVector4() + { + return new Rgba32(this.R, this.G, this.B, 255).ToVector4(); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ToRgb24(ref Rgb24 dest) + { + dest = this; + } + + /// + public void ToRgba32(ref Rgba32 dest) + { + dest.Rgb = this; + dest.A = 255; + } + + /// + public void ToBgr24(ref Bgr24 dest) + { + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; + } + + /// + public void ToBgra32(ref Bgra32 dest) + { + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; + dest.A = 255; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Rgba1010102.cs b/src/ImageSharp/PixelFormats/Rgba1010102.cs index ca7b74fbbd..e682aa4772 100644 --- a/src/ImageSharp/PixelFormats/Rgba1010102.cs +++ b/src/ImageSharp/PixelFormats/Rgba1010102.cs @@ -79,7 +79,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -101,55 +101,51 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - this.PackFromVector4(new Vector4(x, y, z, w) / 255F); + this.PackFromVector4(source.ToVector4()); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { Vector4 vector = this.ToVector4() * 255F; - - bytes[startIndex] = (byte)MathF.Round(vector.X); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.Z); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { Vector4 vector = this.ToVector4() * 255F; - - bytes[startIndex] = (byte)MathF.Round(vector.X); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.Z); - bytes[startIndex + 3] = (byte)MathF.Round(vector.W); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); + dest.A = (byte)MathF.Round(vector.W); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { Vector4 vector = this.ToVector4() * 255F; - - bytes[startIndex] = (byte)MathF.Round(vector.Z); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.X); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { Vector4 vector = this.ToVector4() * 255F; - - bytes[startIndex] = (byte)MathF.Round(vector.Z); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.X); - bytes[startIndex + 3] = (byte)MathF.Round(vector.W); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); + dest.A = (byte)MathF.Round(vector.W); } /// diff --git a/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs index 2ba663603e..63e40e9cf7 100644 --- a/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs @@ -21,7 +21,7 @@ namespace ImageSharp /// /// implementation optimized for . /// - internal class PixelOperations : PixelOperations + internal partial class PixelOperations : PixelOperations { /// /// SIMD optimized bulk implementation of @@ -121,164 +121,19 @@ namespace ImageSharp } /// - internal override void PackFromXyzBytes(Span sourceBytes, Span destColors, int count) + internal override void PackFromRgba32(Span source, Span destPixels, int count) { - Guard.MustBeSizedAtLeast(sourceBytes, count * 3, nameof(sourceBytes)); - Guard.MustBeSizedAtLeast(destColors, count, nameof(destColors)); + GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - 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)); + SpanHelper.Copy(source, destPixels, count); } /// - internal override unsafe void ToXyzwBytes(Span sourceColors, Span destBytes, int count) + internal override void ToRgba32(Span sourcePixels, Span dest, 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); + GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - 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 }; + SpanHelper.Copy(sourcePixels, dest, count); } /// diff --git a/src/ImageSharp/PixelFormats/Rgba32.cs b/src/ImageSharp/PixelFormats/Rgba32.cs index 9b82a37010..85322c7c5c 100644 --- a/src/ImageSharp/PixelFormats/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/Rgba32.cs @@ -23,39 +23,29 @@ namespace ImageSharp /// 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. /// - [StructLayout(LayoutKind.Explicit)] + [StructLayout(LayoutKind.Sequential)] 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 /// @@ -86,6 +76,21 @@ namespace ImageSharp /// private static readonly Vector4 Half = new Vector4(0.5F); + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgba32(byte r, byte g, byte b) + { + this.R = r; + this.G = g; + this.B = b; + this.A = 255; + } + /// /// Initializes a new instance of the struct. /// @@ -94,8 +99,7 @@ namespace ImageSharp /// The blue component. /// The alpha component. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgba32(byte r, byte g, byte b, byte a = 255) - : this() + public Rgba32(byte r, byte g, byte b, byte a) { this.R = r; this.G = g; @@ -156,8 +160,68 @@ namespace ImageSharp this.Rgba = packed; } + /// + /// Gets or sets the packed representation of the Rgba32 struct. + /// + public uint Rgba + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return Unsafe.As(ref this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + Unsafe.As(ref this) = value; + } + } + + /// + /// Gets or sets the RGB components of this struct as + /// + public Rgb24 Rgb + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return Unsafe.As(ref this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + Unsafe.As(ref this) = value; + } + } + + /// + /// Gets or sets the RGB components of this struct as reverting the component order. + /// + public Bgr24 Bgr + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return new Bgr24(this.R, this.G, this.B); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + this.R = value.R; + this.G = value.G; + this.B = value.B; + } + } + /// - public uint PackedValue { get => this.Rgba; set => this.Rgba = value; } + public uint PackedValue + { + get => this.Rgba; + set => this.Rgba = value; + } /// /// Compares two objects for equality. @@ -207,16 +271,13 @@ namespace ImageSharp } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - this.R = x; - this.G = y; - this.B = z; - this.A = w; + this = source; } /// @@ -229,42 +290,37 @@ namespace ImageSharp return hexOrder.ToString("X8"); } - /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { - bytes[startIndex] = this.R; - bytes[startIndex + 1] = this.G; - bytes[startIndex + 2] = this.B; + dest = Unsafe.As(ref this); } - /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { - bytes[startIndex] = this.R; - bytes[startIndex + 1] = this.G; - bytes[startIndex + 2] = this.B; - bytes[startIndex + 3] = this.A; + dest = this; } - /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { - bytes[startIndex] = this.B; - bytes[startIndex + 1] = this.G; - bytes[startIndex + 2] = this.R; + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; } - /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { - bytes[startIndex] = this.B; - bytes[startIndex + 1] = this.G; - bytes[startIndex + 2] = this.R; - bytes[startIndex + 3] = this.A; + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; + dest.A = this.A; } /// @@ -281,6 +337,17 @@ namespace ImageSharp return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; } + /// + /// Gets the value of this struct as . + /// Useful for changing the component order. + /// + /// A value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Bgra32 ToBgra32() + { + return new Bgra32(this.R, this.G, this.B, this.A); + } + /// public override bool Equals(object obj) { @@ -308,14 +375,24 @@ namespace ImageSharp { unchecked { - int hashCode = this.R.GetHashCode(); - hashCode = (hashCode * 397) ^ this.G.GetHashCode(); - hashCode = (hashCode * 397) ^ this.B.GetHashCode(); - hashCode = (hashCode * 397) ^ this.A.GetHashCode(); + int hashCode = this.R; + hashCode = (hashCode * 397) ^ this.G; + hashCode = (hashCode * 397) ^ this.B; + hashCode = (hashCode * 397) ^ this.A; return hashCode; } } + /// + /// Gets the representation without normalizing to [0, 1] + /// + /// A of values in [0, 255] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Vector4 ToUnscaledVector4() + { + return new Vector4(this.R, this.G, this.B, this.A); + } + /// /// Packs the four floats into a . /// diff --git a/src/ImageSharp/PixelFormats/Rgba64.cs b/src/ImageSharp/PixelFormats/Rgba64.cs index 4178283686..bdcf13763d 100644 --- a/src/ImageSharp/PixelFormats/Rgba64.cs +++ b/src/ImageSharp/PixelFormats/Rgba64.cs @@ -78,7 +78,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -100,55 +100,51 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - this.PackFromVector4(new Vector4(x, y, z, w) / 255F); + this.PackFromVector4(source.ToVector4()); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { Vector4 vector = this.ToVector4() * 255F; - - bytes[startIndex] = (byte)MathF.Round(vector.X); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.Z); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { Vector4 vector = this.ToVector4() * 255F; - - bytes[startIndex] = (byte)MathF.Round(vector.X); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.Z); - bytes[startIndex + 3] = (byte)MathF.Round(vector.W); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); + dest.A = (byte)MathF.Round(vector.W); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { Vector4 vector = this.ToVector4() * 255F; - - bytes[startIndex] = (byte)MathF.Round(vector.Z); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.X); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { Vector4 vector = this.ToVector4() * 255F; - - bytes[startIndex] = (byte)MathF.Round(vector.Z); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.X); - bytes[startIndex + 3] = (byte)MathF.Round(vector.W); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); + dest.A = (byte)MathF.Round(vector.W); } /// diff --git a/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs index ac25b7f149..00b7461661 100644 --- a/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs +++ b/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs @@ -23,10 +23,9 @@ namespace ImageSharp.PixelFormats /// internal override unsafe void ToVector4(Span sourceColors, Span destVectors, int count) { - Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors)); - Guard.MustBeSizedAtLeast(destVectors, count, nameof(destVectors)); + GuardSpans(sourceColors, nameof(sourceColors), destVectors, nameof(destVectors), count); - SpanHelper.Copy(sourceColors.AsBytes(), destVectors.AsBytes(), count * sizeof(Vector4)); + SpanHelper.Copy(sourceColors.NonPortableCast(), destVectors, count); } } } diff --git a/src/ImageSharp/PixelFormats/RgbaVector.cs b/src/ImageSharp/PixelFormats/RgbaVector.cs index 5332f4a8e7..c6eed1122e 100644 --- a/src/ImageSharp/PixelFormats/RgbaVector.cs +++ b/src/ImageSharp/PixelFormats/RgbaVector.cs @@ -211,13 +211,13 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new RgbaVector.PixelOperations(); + public PixelOperations CreatePixelOperations() => new RgbaVector.PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - this.backingVector = new Vector4(x, y, z, w) / MaxBytes; + this.backingVector = source.ToVector4(); } /// @@ -233,50 +233,46 @@ namespace ImageSharp.PixelFormats return hexOrder.ToString("X8"); } - /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { - 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; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); } - /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { - 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; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); + dest.A = (byte)MathF.Round(vector.W); } - /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { - 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; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); } - /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { - 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; + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); + dest.A = (byte)MathF.Round(vector.W); } /// @@ -320,5 +316,8 @@ namespace ImageSharp.PixelFormats { return this.backingVector.GetHashCode(); } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Vector4 ToScaledVector4() => Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes; } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Short2.cs b/src/ImageSharp/PixelFormats/Short2.cs index b848b55053..0b3f4be934 100644 --- a/src/ImageSharp/PixelFormats/Short2.cs +++ b/src/ImageSharp/PixelFormats/Short2.cs @@ -91,7 +91,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -109,9 +109,9 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - Vector2 vector = new Vector2(x, y) / 255; + Vector2 vector = new Vector2(source.R, source.G) / 255; vector *= 65534; vector -= new Vector2(32767); this.PackedValue = Pack(vector.X, vector.Y); @@ -119,68 +119,44 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { - Vector2 vector = this.ToVector2(); - vector /= 65534; - vector *= 255; - vector += Half; - vector += Round; - vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes); - - bytes[startIndex] = (byte)MathF.Round(vector.X); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = 0; + Vector2 vector = this.ToScaledVector2(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = 0; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { - Vector2 vector = this.ToVector2(); - vector /= 65534; - vector *= 255; - vector += Half; - vector += Round; - vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes); - - bytes[startIndex] = (byte)MathF.Round(vector.X); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = 0; - bytes[startIndex + 3] = 255; + Vector2 vector = this.ToScaledVector2(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = 0; + dest.A = 255; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { - Vector2 vector = this.ToVector2(); - vector /= 65534; - vector *= 255; - vector += Half; - vector += Round; - vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes); - - bytes[startIndex] = 0; - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.X); + Vector2 vector = this.ToScaledVector2(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = 0; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { - Vector2 vector = this.ToVector2(); - vector /= 65534; - vector *= 255; - vector += Half; - vector += Round; - vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes); - - bytes[startIndex] = 0; - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.X); - bytes[startIndex + 3] = 255; + Vector2 vector = this.ToScaledVector2(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = 0; + dest.A = 255; } /// @@ -239,5 +215,17 @@ namespace ImageSharp.PixelFormats return word2 | word1; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Vector2 ToScaledVector2() + { + Vector2 vector = this.ToVector2(); + vector /= 65534; + vector *= 255; + vector += Half; + vector += Round; + vector = Vector2.Clamp(vector, Vector2.Zero, MaxBytes); + return vector; + } } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Short4.cs b/src/ImageSharp/PixelFormats/Short4.cs index 763de19bc3..9583009293 100644 --- a/src/ImageSharp/PixelFormats/Short4.cs +++ b/src/ImageSharp/PixelFormats/Short4.cs @@ -93,7 +93,7 @@ namespace ImageSharp.PixelFormats } /// - public PixelOperations CreateBulkOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -115,9 +115,9 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBytes(byte x, byte y, byte z, byte w) + public void PackFromRgba32(Rgba32 source) { - Vector4 vector = new Vector4(x, y, z, w) / 255; + Vector4 vector = source.ToVector4(); vector *= 65534; vector -= new Vector4(32767); this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); @@ -125,68 +125,44 @@ namespace ImageSharp.PixelFormats /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzBytes(Span bytes, int startIndex) + public void ToRgb24(ref Rgb24 dest) { - Vector4 vector = this.ToVector4(); - vector /= 65534; - vector *= 255; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)MathF.Round(vector.X); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.Z); + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToXyzwBytes(Span bytes, int startIndex) + public void ToRgba32(ref Rgba32 dest) { - Vector4 vector = this.ToVector4(); - vector /= 65534; - vector *= 255; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)MathF.Round(vector.X); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.Z); - bytes[startIndex + 3] = (byte)MathF.Round(vector.W); + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); + dest.A = (byte)MathF.Round(vector.W); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxBytes(Span bytes, int startIndex) + public void ToBgr24(ref Bgr24 dest) { - Vector4 vector = this.ToVector4(); - vector /= 65534; - vector *= 255; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)MathF.Round(vector.Z); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.X); + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToZyxwBytes(Span bytes, int startIndex) + public void ToBgra32(ref Bgra32 dest) { - Vector4 vector = this.ToVector4(); - vector /= 65534; - vector *= 255; - vector += Half; - vector += Round; - vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - - bytes[startIndex] = (byte)MathF.Round(vector.Z); - bytes[startIndex + 1] = (byte)MathF.Round(vector.Y); - bytes[startIndex + 2] = (byte)MathF.Round(vector.X); - bytes[startIndex + 3] = (byte)MathF.Round(vector.W); + Vector4 vector = this.ToScaledVector4(); + dest.R = (byte)MathF.Round(vector.X); + dest.G = (byte)MathF.Round(vector.Y); + dest.B = (byte)MathF.Round(vector.Z); + dest.A = (byte)MathF.Round(vector.W); } /// @@ -246,5 +222,17 @@ namespace ImageSharp.PixelFormats return word4 | word3 | word2 | word1; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Vector4 ToScaledVector4() + { + Vector4 vector = this.ToVector4(); + vector /= 65534; + vector *= 255; + vector += Half; + vector += Round; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + return vector; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs b/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs index 76977455a4..de543f2cd9 100644 --- a/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs +++ b/src/ImageSharp/Processing/ColorMatrix/BlackWhite.cs @@ -9,7 +9,7 @@ namespace ImageSharp using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; using Processing.Processors; /// diff --git a/src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs b/src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs index d012d6fe2b..c48a862930 100644 --- a/src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs +++ b/src/ImageSharp/Processing/ColorMatrix/ColorBlindness.cs @@ -9,7 +9,7 @@ namespace ImageSharp using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; using Processing.Processors; /// diff --git a/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs b/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs index 8700b63e88..daddf106c3 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Grayscale.cs @@ -5,11 +5,9 @@ namespace ImageSharp { - using System; - using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; using Processing.Processors; /// @@ -17,6 +15,18 @@ namespace ImageSharp /// public static partial class ImageExtensions { + /// + /// Applies Grayscale toning to the image. + /// + /// The pixel format. + /// The image this method extends. + /// The . + public static Image Grayscale(this Image source) + where TPixel : struct, IPixel + { + return Grayscale(source, GrayscaleMode.Bt709); + } + /// /// Applies Grayscale toning to the image. /// @@ -24,23 +34,41 @@ namespace ImageSharp /// 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) + public static Image Grayscale(this Image source, GrayscaleMode mode) where TPixel : struct, IPixel { - return Grayscale(source, source.Bounds, mode); + return Grayscale(source, mode, source.Bounds); } /// - /// Applies Grayscale toning to the image. + /// Applies Grayscale toning to the image. /// /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// + /// The . + public static Image Grayscale(this Image source, Rectangle rectangle) + where TPixel : struct, IPixel + { + IImageProcessor processor = new GrayscaleBt709Processor(); + + source.ApplyProcessor(processor, rectangle); + return source; + } + + /// + /// Applies Grayscale toning to the image. + /// + /// The pixel format. + /// The image this method extends. /// The formula to apply to perform the operation. + /// + /// The structure that specifies the portion of the image object to alter. + /// /// The . - public static Image Grayscale(this Image source, Rectangle rectangle, GrayscaleMode mode = GrayscaleMode.Bt709) + public static Image Grayscale(this Image source, GrayscaleMode mode, Rectangle rectangle) where TPixel : struct, IPixel { IImageProcessor processor = mode == GrayscaleMode.Bt709 diff --git a/src/ImageSharp/Processing/ColorMatrix/Hue.cs b/src/ImageSharp/Processing/ColorMatrix/Hue.cs index 8dbc555307..bcaf68d9a9 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Hue.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Hue.cs @@ -9,7 +9,7 @@ namespace ImageSharp using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; using Processing.Processors; /// diff --git a/src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs b/src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs index 13a71a71ef..a302bb2e90 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Kodachrome.cs @@ -9,7 +9,7 @@ namespace ImageSharp using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; using Processing.Processors; /// diff --git a/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs b/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs index 937dca9ba8..cbfeb7d055 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Lomograph.cs @@ -9,7 +9,7 @@ namespace ImageSharp using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; using Processing.Processors; /// diff --git a/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs b/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs index f1a573c05d..ac9f7391f1 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Polaroid.cs @@ -9,7 +9,7 @@ namespace ImageSharp using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; using Processing.Processors; /// diff --git a/src/ImageSharp/Processing/ColorMatrix/Saturation.cs b/src/ImageSharp/Processing/ColorMatrix/Saturation.cs index c41f304b4e..ffe336c62d 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Saturation.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Saturation.cs @@ -9,7 +9,7 @@ namespace ImageSharp using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; using Processing.Processors; /// diff --git a/src/ImageSharp/Processing/ColorMatrix/Sepia.cs b/src/ImageSharp/Processing/ColorMatrix/Sepia.cs index 39eca4e8ef..8488dcfe5a 100644 --- a/src/ImageSharp/Processing/ColorMatrix/Sepia.cs +++ b/src/ImageSharp/Processing/ColorMatrix/Sepia.cs @@ -9,7 +9,7 @@ namespace ImageSharp using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; using Processing.Processors; /// diff --git a/src/ImageSharp/Processing/Convolution/DetectEdges.cs b/src/ImageSharp/Processing/Convolution/DetectEdges.cs index 3aa1d0b513..368688f624 100644 --- a/src/ImageSharp/Processing/Convolution/DetectEdges.cs +++ b/src/ImageSharp/Processing/Convolution/DetectEdges.cs @@ -9,7 +9,7 @@ namespace ImageSharp using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; using Processing.Processors; /// diff --git a/src/ImageSharp/Processing/Convolution/GaussianBlur.cs b/src/ImageSharp/Processing/Convolution/GaussianBlur.cs index 72abec6df9..0b8c5383c8 100644 --- a/src/ImageSharp/Processing/Convolution/GaussianBlur.cs +++ b/src/ImageSharp/Processing/Convolution/GaussianBlur.cs @@ -9,7 +9,7 @@ namespace ImageSharp using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; using Processing.Processors; /// diff --git a/src/ImageSharp/Processing/Convolution/GaussianSharpen.cs b/src/ImageSharp/Processing/Convolution/GaussianSharpen.cs index 2ed99ea260..1a5d3c2df5 100644 --- a/src/ImageSharp/Processing/Convolution/GaussianSharpen.cs +++ b/src/ImageSharp/Processing/Convolution/GaussianSharpen.cs @@ -9,7 +9,7 @@ namespace ImageSharp using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; using Processing.Processors; /// diff --git a/src/ImageSharp/Processing/Effects/OilPainting.cs b/src/ImageSharp/Processing/Effects/OilPainting.cs index d4528b55ba..fd65542e04 100644 --- a/src/ImageSharp/Processing/Effects/OilPainting.cs +++ b/src/ImageSharp/Processing/Effects/OilPainting.cs @@ -16,6 +16,35 @@ namespace ImageSharp /// public static partial class ImageExtensions { + /// + /// Alters the colors of the image recreating an oil painting effect with levels and brushSize + /// set to 10 and 15 respectively. + /// + /// The pixel format. + /// The image this method extends. + /// The . + public static Image OilPaint(this Image source) + where TPixel : struct, IPixel + { + return OilPaint(source, 10, 15); + } + + /// + /// Alters the colors of the image recreating an oil painting effect with levels and brushSize + /// set to 10 and 15 respectively. + /// + /// The pixel format. + /// The image this method extends. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static Image OilPaint(this Image source, Rectangle rectangle) + where TPixel : struct, IPixel + { + return OilPaint(source, 10, 15, rectangle); + } + /// /// Alters the colors of the image recreating an oil painting effect. /// @@ -24,8 +53,8 @@ namespace ImageSharp /// 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 TPixel : struct, IPixel + public static Image OilPaint(this Image source, int levels, int brushSize) + where TPixel : struct, IPixel { return OilPaint(source, levels, brushSize, source.Bounds); } diff --git a/src/ImageSharp/Processing/Overlays/Glow.cs b/src/ImageSharp/Processing/Overlays/Glow.cs index 587bbe6104..04c85e00c7 100644 --- a/src/ImageSharp/Processing/Overlays/Glow.cs +++ b/src/ImageSharp/Processing/Overlays/Glow.cs @@ -5,8 +5,6 @@ namespace ImageSharp { - using System; - using ImageSharp.PixelFormats; using Processing.Processors; @@ -158,7 +156,7 @@ namespace ImageSharp public static Image Glow(this Image source, TPixel color, float radius, Rectangle rectangle, GraphicsOptions options) where TPixel : struct, IPixel { - GlowProcessor processor = new GlowProcessor(color, options) { Radius = radius, }; + var 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 2eaf2e1efa..c04e887182 100644 --- a/src/ImageSharp/Processing/Overlays/Vignette.cs +++ b/src/ImageSharp/Processing/Overlays/Vignette.cs @@ -5,8 +5,6 @@ namespace ImageSharp { - using System; - using ImageSharp.PixelFormats; using Processing.Processors; @@ -162,7 +160,7 @@ namespace ImageSharp 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, options) { RadiusX = radiusX, RadiusY = radiusY }; + var processor = new VignetteProcessor(color, options) { RadiusX = radiusX, RadiusY = radiusY }; source.ApplyProcessor(processor, rectangle); return source; } diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs index a43f77a1c6..37cc8a9d9c 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs @@ -97,17 +97,7 @@ namespace ImageSharp.Processing.Processors int fyr = fy - radius; int offsetY = y + fyr; - // Skip the current row - if (offsetY < minY) - { - continue; - } - - // Outwith the current bounds so break. - if (offsetY >= maxY) - { - break; - } + offsetY = offsetY.Clamp(0, maxY); Span sourceOffsetRow = source.GetRowSpan(offsetY); @@ -115,34 +105,25 @@ namespace ImageSharp.Processing.Processors { int fxr = fx - radius; int offsetX = x + fxr; + offsetX = offsetX.Clamp(0, maxX); - // Skip the column - if (offsetX < 0) - { - continue; - } + var vector = sourceOffsetRow[offsetX].ToVector4(); - if (offsetX < maxX) - { - // ReSharper disable once AccessToDisposedClosure - var vector = sourceOffsetRow[offsetX].ToVector4(); + float sourceRed = vector.X; + float sourceBlue = vector.Z; + float sourceGreen = vector.Y; - float sourceRed = vector.X; - float sourceBlue = vector.Z; - float sourceGreen = vector.Y; + int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); - int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (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; } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index 23fea94e93..4e4a36a380 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -52,7 +52,7 @@ namespace ImageSharp.Processing.Processors int startX = sourceRectangle.X; int endX = sourceRectangle.Right; TPixel glowColor = this.GlowColor; - var centre = Rectangle.Center(sourceRectangle).ToVector2(); + Vector2 centre = Rectangle.Center(sourceRectangle); float maxDistance = this.Radius > 0 ? MathF.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; // Align start/end positions. diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index 4dfa41989b..23ba5afe0f 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -58,7 +58,7 @@ namespace ImageSharp.Processing.Processors int startX = sourceRectangle.X; int endX = sourceRectangle.Right; TPixel vignetteColor = this.VignetteColor; - var centre = Rectangle.Center(sourceRectangle).ToVector2(); + Vector2 centre = Rectangle.Center(sourceRectangle); 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; float maxDistance = MathF.Sqrt((rX * rX) + (rY * rY)); diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs index b5266c9bd8..8aef87ec85 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs @@ -27,31 +27,52 @@ namespace ImageSharp.Processing.Processors public int Left; /// - /// The span of weights pointing to . + /// The length of the weights window /// - public Span Span; + public int Length; + + /// + /// The index in the destination buffer + /// + private readonly int flatStartIndex; + + /// + /// The buffer containing the weights values. + /// + private readonly Buffer buffer; /// /// Initializes a new instance of the struct. /// + /// The destination index in the buffer /// The local left index - /// The span + /// The span + /// The length of the window [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal WeightsWindow(int left, Span span) + internal WeightsWindow(int index, int left, Buffer2D buffer, int length) { + this.flatStartIndex = (index * buffer.Width) + left; this.Left = left; - this.Span = span; + this.buffer = buffer; + this.Length = length; } /// - /// Gets an unsafe float* pointer to the beginning of . + /// Gets a reference to the first item of the window. /// - public ref float Ptr => ref this.Span.DangerousGetPinnableReference(); + /// The reference to the first item of the window + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref float GetStartReference() + { + return ref this.buffer[this.flatStartIndex]; + } /// - /// Gets the lenghth of the weights window + /// Gets the span representing the portion of the that this window covers /// - public int Length => this.Span.Length; + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span GetWindowSpan() => this.buffer.Slice(this.flatStartIndex, this.Length); /// /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance. @@ -62,7 +83,7 @@ namespace ImageSharp.Processing.Processors [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ComputeWeightedRowSum(Span rowSpan, int sourceX) { - ref float horizontalValues = ref this.Ptr; + ref float horizontalValues = ref this.GetStartReference(); int left = this.Left; ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX); @@ -89,7 +110,7 @@ namespace ImageSharp.Processing.Processors [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ComputeExpandedWeightedRowSum(Span rowSpan, int sourceX) { - ref float horizontalValues = ref this.Ptr; + ref float horizontalValues = ref this.GetStartReference(); int left = this.Left; ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX); @@ -117,7 +138,7 @@ namespace ImageSharp.Processing.Processors [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ComputeWeightedColumnSum(Buffer2D firstPassPixels, int x, int sourceY) { - ref float verticalValues = ref this.Ptr; + ref float verticalValues = ref this.GetStartReference(); int left = this.Left; // Destination color components @@ -139,7 +160,7 @@ namespace ImageSharp.Processing.Processors /// internal class WeightsBuffer : IDisposable { - private Buffer2D dataBuffer; + private readonly Buffer2D dataBuffer; /// /// Initializes a new instance of the class. @@ -174,8 +195,7 @@ namespace ImageSharp.Processing.Processors /// The weights public WeightsWindow GetWeightsWindow(int destIdx, int leftIdx, int rightIdx) { - Span span = this.dataBuffer.GetRowSpan(destIdx).Slice(leftIdx, rightIdx - leftIdx + 1); - return new WeightsWindow(leftIdx, span); + return new WeightsWindow(destIdx, leftIdx, this.dataBuffer, rightIdx - leftIdx + 1); } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs index 757b0889a5..7245b961f8 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs @@ -112,7 +112,7 @@ namespace ImageSharp.Processing.Processors WeightsWindow ws = result.GetWeightsWindow(i, left, right); result.Weights[i] = ws; - ref float weights = ref ws.Ptr; + ref float weightsBaseRef = ref ws.GetStartReference(); for (int j = left; j <= right; j++) { @@ -120,7 +120,7 @@ namespace ImageSharp.Processing.Processors sum += weight; // weights[j - left] = weight: - Unsafe.Add(ref weights, j - left) = weight; + Unsafe.Add(ref weightsBaseRef, j - left) = weight; } // Normalise, best to do it here rather than in the pixel loop later on. @@ -129,7 +129,7 @@ namespace ImageSharp.Processing.Processors for (int w = 0; w < ws.Length; w++) { // weights[w] = weights[w] / sum: - ref float wRef = ref Unsafe.Add(ref weights, w); + ref float wRef = ref Unsafe.Add(ref weightsBaseRef, w); wRef = wRef / sum; } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index 43dbb53eac..15d7ca641a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -78,7 +78,7 @@ namespace ImageSharp.Processing.Processors return; } - this.processMatrix = Point.CreateRotation(new Point(0, 0), -this.Angle); + this.processMatrix = Matrix3x2Extensions.CreateRotation(-this.Angle, new Point(0, 0)); if (this.Expand) { this.CreateNewCanvas(sourceRectangle, this.processMatrix); diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index 7807d0dfcc..264cf4a531 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -73,7 +73,7 @@ namespace ImageSharp.Processing.Processors /// protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) { - this.processMatrix = Point.CreateSkew(new Point(0, 0), -this.AngleX, -this.AngleY); + this.processMatrix = Matrix3x2Extensions.CreateSkew(-this.AngleX, -this.AngleY, new Point(0, 0)); if (this.Expand) { this.CreateNewCanvas(sourceRectangle, this.processMatrix); diff --git a/src/ImageSharp/Processing/Transforms/AutoOrient.cs b/src/ImageSharp/Processing/Transforms/AutoOrient.cs index f9d3a60aa1..07e5d5bc9a 100644 --- a/src/ImageSharp/Processing/Transforms/AutoOrient.cs +++ b/src/ImageSharp/Processing/Transforms/AutoOrient.cs @@ -5,12 +5,9 @@ namespace ImageSharp { - using System; - using ImageSharp.PixelFormats; - using Processing; - using Processing.Processors; + using ImageSharp.Processing; /// /// Extension methods for the type. @@ -80,7 +77,7 @@ namespace ImageSharp return Orientation.Unknown; } - Orientation orientation = (Orientation)value.Value; + var orientation = (Orientation)value.Value; source.MetaData.ExifProfile.SetValue(ExifTag.Orientation, (ushort)Orientation.TopLeft); diff --git a/src/ImageSharp/Processing/Transforms/Flip.cs b/src/ImageSharp/Processing/Transforms/Flip.cs index 342b4dd46e..1c8baebf13 100644 --- a/src/ImageSharp/Processing/Transforms/Flip.cs +++ b/src/ImageSharp/Processing/Transforms/Flip.cs @@ -9,7 +9,7 @@ namespace ImageSharp using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; using Processing.Processors; /// diff --git a/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs b/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs index c876882662..712efec121 100644 --- a/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs +++ b/src/ImageSharp/Processing/Transforms/Options/ResizeHelper.cs @@ -5,7 +5,6 @@ namespace ImageSharp.Processing { - using System; using System.Linq; using ImageSharp.PixelFormats; @@ -86,17 +85,17 @@ namespace ImageSharp.Processing if (options.CenterCoordinates.Any()) { - float center = -(ratio * sourceHeight) * options.CenterCoordinates.First(); - destinationY = (int)center + (height / 2); + float center = -(ratio * sourceHeight) * options.CenterCoordinates.ToArray()[1]; + destinationY = (int)MathF.Round(center + (height / 2F)); if (destinationY > 0) { destinationY = 0; } - if (destinationY < (int)(height - (sourceHeight * ratio))) + if (destinationY < (int)MathF.Round(height - (sourceHeight * ratio))) { - destinationY = (int)(height - (sourceHeight * ratio)); + destinationY = (int)MathF.Round(height - (sourceHeight * ratio)); } } else @@ -111,10 +110,10 @@ namespace ImageSharp.Processing case AnchorPosition.Bottom: case AnchorPosition.BottomLeft: case AnchorPosition.BottomRight: - destinationY = (int)(height - (sourceHeight * ratio)); + destinationY = (int)MathF.Round(height - (sourceHeight * ratio)); break; default: - destinationY = (int)((height - (sourceHeight * ratio)) / 2); + destinationY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F); break; } } @@ -127,17 +126,17 @@ namespace ImageSharp.Processing if (options.CenterCoordinates.Any()) { - float center = -(ratio * sourceWidth) * options.CenterCoordinates.ToArray()[1]; - destinationX = (int)center + (width / 2); + float center = -(ratio * sourceWidth) * options.CenterCoordinates.First(); + destinationX = (int)MathF.Round(center + (width / 2F)); if (destinationX > 0) { destinationX = 0; } - if (destinationX < (int)(width - (sourceWidth * ratio))) + if (destinationX < (int)MathF.Round(width - (sourceWidth * ratio))) { - destinationX = (int)(width - (sourceWidth * ratio)); + destinationX = (int)MathF.Round(width - (sourceWidth * ratio)); } } else @@ -152,10 +151,10 @@ namespace ImageSharp.Processing case AnchorPosition.Right: case AnchorPosition.TopRight: case AnchorPosition.BottomRight: - destinationX = (int)(width - (sourceWidth * ratio)); + destinationX = (int)MathF.Round(width - (sourceWidth * ratio)); break; default: - destinationX = (int)((width - (sourceWidth * ratio)) / 2); + destinationX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F); break; } } @@ -202,7 +201,7 @@ namespace ImageSharp.Processing if (percentHeight < percentWidth) { ratio = percentHeight; - destinationWidth = Convert.ToInt32(sourceWidth * percentHeight); + destinationWidth = (int)MathF.Round(sourceWidth * percentHeight); switch (options.Position) { @@ -214,17 +213,17 @@ namespace ImageSharp.Processing case AnchorPosition.Right: case AnchorPosition.TopRight: case AnchorPosition.BottomRight: - destinationX = (int)(width - (sourceWidth * ratio)); + destinationX = (int)MathF.Round(width - (sourceWidth * ratio)); break; default: - destinationX = Convert.ToInt32((width - (sourceWidth * ratio)) / 2); + destinationX = (int)MathF.Round((width - (sourceWidth * ratio)) / 2F); break; } } else { ratio = percentWidth; - destinationHeight = Convert.ToInt32(sourceHeight * percentWidth); + destinationHeight = (int)MathF.Round(sourceHeight * percentWidth); switch (options.Position) { @@ -236,10 +235,10 @@ namespace ImageSharp.Processing case AnchorPosition.Bottom: case AnchorPosition.BottomLeft: case AnchorPosition.BottomRight: - destinationY = (int)(height - (sourceHeight * ratio)); + destinationY = (int)MathF.Round(height - (sourceHeight * ratio)); break; default: - destinationY = (int)((height - (sourceHeight * ratio)) / 2); + destinationY = (int)MathF.Round((height - (sourceHeight * ratio)) / 2F); break; } } @@ -274,8 +273,8 @@ namespace ImageSharp.Processing float percentHeight = MathF.Abs(height / (float)sourceHeight); float percentWidth = MathF.Abs(width / (float)sourceWidth); - int boxPadHeight = height > 0 ? height : Convert.ToInt32(sourceHeight * percentWidth); - int boxPadWidth = width > 0 ? width : Convert.ToInt32(sourceWidth * percentHeight); + int boxPadHeight = height > 0 ? height : (int)MathF.Round(sourceHeight * percentWidth); + int boxPadWidth = width > 0 ? width : (int)MathF.Round(sourceWidth * percentHeight); // Only calculate if upscaling. if (sourceWidth < boxPadWidth && sourceHeight < boxPadHeight) @@ -356,17 +355,17 @@ namespace ImageSharp.Processing float percentWidth = MathF.Abs(width / (float)source.Width); // Integers must be cast to floats to get needed precision - float ratio = (float)options.Size.Height / options.Size.Width; - float sourceRatio = (float)source.Height / source.Width; + float ratio = options.Size.Height / (float)options.Size.Width; + float sourceRatio = source.Height / (float)source.Width; if (sourceRatio < ratio) { - destinationHeight = Convert.ToInt32(source.Height * percentWidth); + destinationHeight = (int)MathF.Round(source.Height * percentWidth); height = destinationHeight; } else { - destinationWidth = Convert.ToInt32(source.Width * percentHeight); + destinationWidth = (int)MathF.Round(source.Width * percentHeight); width = destinationWidth; } @@ -389,38 +388,54 @@ namespace ImageSharp.Processing { int width = options.Size.Width; int height = options.Size.Height; + int sourceWidth = source.Width; + int sourceHeight = source.Height; int destinationWidth; int destinationHeight; // Don't upscale - if (width > source.Width || height > source.Height) + if (width > sourceWidth || height > sourceHeight) { - options.Size = new Size(source.Width, source.Height); - return new Rectangle(0, 0, source.Width, source.Height); + options.Size = new Size(sourceWidth, sourceHeight); + return new Rectangle(0, 0, sourceWidth, sourceHeight); } - float sourceRatio = (float)source.Height / source.Width; + // Fractional variants for preserving aspect ratio. + float percentHeight = MathF.Abs(height / (float)sourceHeight); + float percentWidth = MathF.Abs(width / (float)sourceWidth); + + float sourceRatio = (float)sourceHeight / sourceWidth; // Find the shortest distance to go. - int widthDiff = source.Width - width; - int heightDiff = source.Height - height; + int widthDiff = sourceWidth - width; + int heightDiff = sourceHeight - height; if (widthDiff < heightDiff) { - destinationHeight = Convert.ToInt32(width * sourceRatio); + destinationHeight = (int)MathF.Round(width * sourceRatio); height = destinationHeight; destinationWidth = width; } else if (widthDiff > heightDiff) { - destinationWidth = Convert.ToInt32(height / sourceRatio); + destinationWidth = (int)MathF.Round(height / sourceRatio); destinationHeight = height; width = destinationWidth; } else { - destinationWidth = width; - destinationHeight = height; + if (height > width) + { + destinationWidth = width; + destinationHeight = (int)MathF.Round(sourceHeight * percentWidth); + height = destinationHeight; + } + else + { + destinationHeight = height; + destinationWidth = (int)MathF.Round(sourceWidth * percentHeight); + width = destinationWidth; + } } // Replace the size to match the rectangle. @@ -428,4 +443,4 @@ namespace ImageSharp.Processing return new Rectangle(0, 0, destinationWidth, destinationHeight); } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Pad.cs b/src/ImageSharp/Processing/Transforms/Pad.cs index 42851e205d..4bded07724 100644 --- a/src/ImageSharp/Processing/Transforms/Pad.cs +++ b/src/ImageSharp/Processing/Transforms/Pad.cs @@ -9,7 +9,7 @@ namespace ImageSharp using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; using Processing.Processors; /// diff --git a/src/ImageSharp/Processing/Transforms/Resize.cs b/src/ImageSharp/Processing/Transforms/Resize.cs index 543b982976..3399ff0e37 100644 --- a/src/ImageSharp/Processing/Transforms/Resize.cs +++ b/src/ImageSharp/Processing/Transforms/Resize.cs @@ -5,11 +5,9 @@ namespace ImageSharp { - using System; - using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; using Processing.Processors; /// @@ -31,12 +29,12 @@ namespace ImageSharp // Ensure size is populated across both dimensions. if (options.Size.Width == 0 && options.Size.Height > 0) { - options.Size = new Size(source.Width * options.Size.Height / source.Height, options.Size.Height); + options.Size = new Size((int)MathF.Round(source.Width * options.Size.Height / (float)source.Height), options.Size.Height); } if (options.Size.Height == 0 && options.Size.Width > 0) { - options.Size = new Size(options.Size.Width, source.Height * options.Size.Width / source.Width); + options.Size = new Size(options.Size.Width, (int)MathF.Round(source.Height * options.Size.Width / (float)source.Width)); } Rectangle targetRectangle = ResizeHelper.CalculateTargetLocationAndBounds(source, options); @@ -58,6 +56,21 @@ namespace ImageSharp return Resize(source, size.Width, size.Height, new BicubicResampler(), false); } + /// + /// Resizes an image to the given . + /// + /// The pixel format. + /// The image to resize. + /// The target image size. + /// Whether to compress and expand the image color-space to gamma correct the image during processing. + /// 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, bool compand) + where TPixel : struct, IPixel + { + return Resize(source, size.Width, size.Height, new BicubicResampler(), compand); + } + /// /// Resizes an image to the given width and height. /// @@ -140,26 +153,25 @@ namespace ImageSharp /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image - public static Image Resize(this Image source, int width, int height, IResampler sampler, Rectangle sourceRectangle, Rectangle targetRectangle, bool compand = false) + public static Image Resize(this Image source, int width, int height, IResampler sampler, Rectangle sourceRectangle, Rectangle targetRectangle, bool compand) where TPixel : struct, IPixel { if (width == 0 && height > 0) { - width = source.Width * height / source.Height; + width = (int)MathF.Round(source.Width * height / (float)source.Height); targetRectangle.Width = width; } if (height == 0 && width > 0) { - height = source.Height * width / source.Width; + height = (int)MathF.Round(source.Height * width / (float)source.Width); targetRectangle.Height = height; } Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - ResizeProcessor processor = - new ResizeProcessor(sampler, width, height, targetRectangle) { Compand = compand }; + var 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 de9dbdc3bc..af7c06a27e 100644 --- a/src/ImageSharp/Processing/Transforms/Rotate.cs +++ b/src/ImageSharp/Processing/Transforms/Rotate.cs @@ -9,7 +9,7 @@ namespace ImageSharp using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; using Processing.Processors; /// diff --git a/src/ImageSharp/Processing/Transforms/RotateFlip.cs b/src/ImageSharp/Processing/Transforms/RotateFlip.cs index 460d004cbc..805deb8d16 100644 --- a/src/ImageSharp/Processing/Transforms/RotateFlip.cs +++ b/src/ImageSharp/Processing/Transforms/RotateFlip.cs @@ -9,7 +9,7 @@ namespace ImageSharp using ImageSharp.PixelFormats; - using Processing; + using ImageSharp.Processing; /// /// Extension methods for the type. diff --git a/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs index e19df4cfaa..8ebea85334 100644 --- a/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/OctreeQuantizer{TPixel}.cs @@ -499,7 +499,7 @@ namespace ImageSharp.Quantizers // And set the color of the palette entry var pixel = default(TPixel); - pixel.PackFromBytes(r, g, b, 255); + pixel.PackFromRgba32(new Rgba32(r, g, b, 255)); palette[index] = pixel; // Consume the next palette index diff --git a/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs index cf3ff94eec..d121dc6ae7 100644 --- a/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/PaletteQuantizer{TPixel}.cs @@ -18,11 +18,6 @@ namespace ImageSharp.Quantizers public sealed class PaletteQuantizer : Quantizer where TPixel : struct, IPixel { - /// - /// The pixel buffer, used to reduce allocations. - /// - private readonly byte[] pixelBuffer = new byte[4]; - /// /// A lookup table for colors /// @@ -48,14 +43,9 @@ namespace ImageSharp.Quantizers 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); - var packed = default(TPixel); - packed.PackFromBytes(this.pixelBuffer[0], this.pixelBuffer[1], this.pixelBuffer[2], this.pixelBuffer[3]); - safe[i] = packed; - } + Span constantsBytes = constants.AsSpan().NonPortableCast(); + PixelOperations.Instance.PackFromRgba32Bytes(constantsBytes, safe, constants.Length); this.colors = safe; } else diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs index 30d93e3d94..78bdfd1599 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs @@ -22,7 +22,7 @@ [Params(16, 128, 1024)] public int Count { get; set; } - [Setup] + [GlobalSetup] public void Setup() { this.destination = new Buffer(this.Count); @@ -31,7 +31,7 @@ this.destination.Pin(); } - [Cleanup] + [GlobalCleanup] public void Cleanup() { this.source.Dispose(); diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs index 501ae79497..7b151989ea 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs @@ -16,14 +16,14 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Params(16, 128, 1024)] public int Count { get; set; } - [Setup] + [GlobalSetup] public void Setup() { this.destination = new Buffer(this.Count); this.source = new Buffer(this.Count * 4); } - [Cleanup] + [GlobalCleanup] public void Cleanup() { this.destination.Dispose(); @@ -40,7 +40,7 @@ namespace ImageSharp.Benchmarks.Color.Bulk { int i4 = i * 4; TPixel c = default(TPixel); - c.PackFromBytes(s[i4], s[i4 + 1], s[i4 + 2], s[i4 + 3]); + c.PackFromRgba32(new Rgba32(s[i4], s[i4 + 1], s[i4 + 2], s[i4 + 3])); d[i] = c; } } @@ -48,13 +48,13 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Benchmark] public void CommonBulk() { - new PixelOperations().PackFromXyzwBytes(this.source, this.destination, this.Count); + new PixelOperations().PackFromRgba32Bytes(this.source, this.destination, this.Count); } [Benchmark] public void OptimizedBulk() { - PixelOperations.Instance.PackFromXyzwBytes(this.source, this.destination, this.Count); + PixelOperations.Instance.PackFromRgba32Bytes(this.source, this.destination, this.Count); } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs index 65a2988b98..9976faa8c3 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs @@ -18,14 +18,14 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Params(64, 300, 1024)] public int Count { get; set; } - [Setup] + [GlobalSetup] public void Setup() { this.source = new Buffer(this.Count); this.destination = new Buffer(this.Count); } - [Cleanup] + [GlobalCleanup] public void Cleanup() { this.source.Dispose(); diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs index 3c75fc2d19..061a5bd37b 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs @@ -16,14 +16,14 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Params(16, 128, 1024)] public int Count { get; set; } - [Setup] + [GlobalSetup] public void Setup() { this.source = new Buffer(this.Count); this.destination = new Buffer(this.Count * 3); } - [Cleanup] + [GlobalCleanup] public void Cleanup() { this.source.Dispose(); @@ -46,13 +46,13 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Benchmark] public void CommonBulk() { - new PixelOperations().ToXyzBytes(this.source, this.destination, this.Count); + new PixelOperations().ToRgb24Bytes(this.source, this.destination, this.Count); } [Benchmark] public void OptimizedBulk() { - PixelOperations.Instance.ToXyzBytes(this.source, this.destination, this.Count); + PixelOperations.Instance.ToRgb24Bytes(this.source, this.destination, this.Count); } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs index f64bf561b0..f4bcce1152 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs @@ -21,14 +21,14 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Params(16, 128, 1024)] public int Count { get; set; } - [Setup] + [GlobalSetup] public void Setup() { this.source = new Buffer(this.Count); this.destination = new Buffer(this.Count * 4); } - [Cleanup] + [GlobalCleanup] public void Cleanup() { this.source.Dispose(); @@ -51,13 +51,13 @@ namespace ImageSharp.Benchmarks.Color.Bulk [Benchmark] public void CommonBulk() { - new PixelOperations().ToXyzwBytes(this.source, this.destination, this.Count); + new PixelOperations().ToRgba32Bytes(this.source, this.destination, this.Count); } [Benchmark] public void OptimizedBulk() { - PixelOperations.Instance.ToXyzwBytes(this.source, this.destination, this.Count); + PixelOperations.Instance.ToRgba32Bytes(this.source, this.destination, this.Count); } } diff --git a/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs b/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs index 0c777b86bb..2efd9bc429 100644 --- a/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs +++ b/tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs @@ -116,7 +116,7 @@ private int[] inputSourceRGBAsInteger = null; - [Setup] + [GlobalSetup] public void Setup() { // Console.WriteLine("Vector.Count: " + Vector.Count); diff --git a/tests/ImageSharp.Benchmarks/General/Array2D.cs b/tests/ImageSharp.Benchmarks/General/Array2D.cs index 9d44fc93de..b7ac2cd867 100644 --- a/tests/ImageSharp.Benchmarks/General/Array2D.cs +++ b/tests/ImageSharp.Benchmarks/General/Array2D.cs @@ -27,7 +27,7 @@ namespace ImageSharp.Benchmarks.General public int Min { get; private set; } public int Max { get; private set; } - [Setup] + [GlobalSetup] public void SetUp() { this.flatArray = new float[this.Count * this.Count]; diff --git a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs index 31e9cc0e3c..dd0ebd413e 100644 --- a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs +++ b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs @@ -20,7 +20,7 @@ namespace ImageSharp.Benchmarks.General byte[] destination; - [Setup] + [GlobalSetup] public void SetUp() { this.source = new byte[this.Count]; diff --git a/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs b/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs index 970f6b347c..27341b146f 100644 --- a/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs +++ b/tests/ImageSharp.Benchmarks/General/ArrayReverse.cs @@ -18,7 +18,7 @@ namespace ImageSharp.Benchmarks.General byte[] destination; - [Setup] + [GlobalSetup] public void SetUp() { this.source = new byte[this.Count]; diff --git a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs index fc1b46a91a..c3a309bb8a 100644 --- a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs +++ b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs @@ -15,13 +15,13 @@ namespace ImageSharp.Benchmarks.General [Params(32, 128, 512)] public int Count { get; set; } - [Setup] + [GlobalSetup] public void Setup() { this.buffer = new Buffer(this.Count); } - [Cleanup] + [GlobalCleanup] public void Cleanup() { this.buffer.Dispose(); diff --git a/tests/ImageSharp.Benchmarks/General/IterateArray.cs b/tests/ImageSharp.Benchmarks/General/IterateArray.cs index b9e2f7acdc..cc8d99b418 100644 --- a/tests/ImageSharp.Benchmarks/General/IterateArray.cs +++ b/tests/ImageSharp.Benchmarks/General/IterateArray.cs @@ -18,7 +18,7 @@ namespace ImageSharp.Benchmarks.General [Params(64, 1024)] public int Length { get; set; } - [Setup] + [GlobalSetup] public void Setup() { this.buffer = new Buffer(this.Length); diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromRgba32.cs new file mode 100644 index 0000000000..e096fd828c --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromRgba32.cs @@ -0,0 +1,206 @@ +// ReSharper disable InconsistentNaming +namespace ImageSharp.Benchmarks.General +{ + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + using BenchmarkDotNet.Attributes; + + public class PixelConversion_ConvertFromRgba32 + { + interface ITestPixel + where T : struct, ITestPixel + { + void FromRgba32(Rgba32 source); + + void FromRgba32(ref Rgba32 source); + + void FromBytes(byte r, byte g, byte b, byte a); + } + + [StructLayout(LayoutKind.Sequential)] + struct TestArgb : ITestPixel + { + private byte a, r, g, b; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 p) + { + this.r = p.R; + this.g = p.G; + this.b = p.B; + this.a = p.A; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(ref Rgba32 p) + { + this.r = p.R; + this.g = p.G; + this.b = p.B; + this.a = p.A; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBytes(byte r, byte g, byte b, byte a) + { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + } + + [StructLayout(LayoutKind.Sequential)] + struct TestRgba : ITestPixel + { + private byte r, g, b, a; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(Rgba32 source) + { + this = Unsafe.As(ref source); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromRgba32(ref Rgba32 source) + { + this = Unsafe.As(ref source); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromBytes(byte r, byte g, byte b, byte a) + { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + } + + struct ConversionRunner + where T : struct, ITestPixel + { + private T[] dest; + + private Rgba32[] source; + + public ConversionRunner(int count) + { + this.dest = new T[count]; + this.source = new Rgba32[count]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunByRefConversion() + { + int count = this.dest.Length; + + ref T destBaseRef = ref this.dest[0]; + ref Rgba32 sourceBaseRef = ref this.source[0]; + + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref destBaseRef, i).FromRgba32(ref Unsafe.Add(ref sourceBaseRef, i)); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunByValConversion() + { + int count = this.dest.Length; + + ref T destBaseRef = ref this.dest[0]; + ref Rgba32 sourceBaseRef = ref this.source[0]; + + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref destBaseRef, i).FromRgba32(Unsafe.Add(ref sourceBaseRef, i)); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunFromBytesConversion() + { + int count = this.dest.Length; + + ref T destBaseRef = ref this.dest[0]; + ref Rgba32 sourceBaseRef = ref this.source[0]; + + for (int i = 0; i < count; i++) + { + ref Rgba32 s = ref Unsafe.Add(ref sourceBaseRef, i); + Unsafe.Add(ref destBaseRef, i).FromBytes(s.R, s.G, s.B, s.A); + } + } + } + + private ConversionRunner compatibleMemLayoutRunner; + + private ConversionRunner permutedRunner; + + [Params(32)] + public int Count { get; set; } + + [Setup] + public void Setup() + { + this.compatibleMemLayoutRunner = new ConversionRunner(this.Count); + this.permutedRunner = new ConversionRunner(this.Count); + } + + [Benchmark(Baseline = true)] + public void CompatibleByRef() + { + this.compatibleMemLayoutRunner.RunByRefConversion(); + } + + [Benchmark] + public void CompatibleByVal() + { + this.compatibleMemLayoutRunner.RunByValConversion(); + } + + [Benchmark] + public void CompatibleFromBytes() + { + this.compatibleMemLayoutRunner.RunFromBytesConversion(); + } + + + [Benchmark] + public void PermutedByRef() + { + this.permutedRunner.RunByRefConversion(); + } + + [Benchmark] + public void PermutedByVal() + { + this.permutedRunner.RunByValConversion(); + } + + [Benchmark] + public void PermutedFromBytes() + { + this.permutedRunner.RunFromBytesConversion(); + } + } + + /* + * Results: + * Method | Count | Mean | StdDev | Scaled | Scaled-StdDev | + * ------------------ |------ |----------- |---------- |------- |-------------- | + * CompatibleByRef | 32 | 20.6339 ns | 0.0742 ns | 1.00 | 0.00 | + * CompatibleByVal | 32 | 23.7425 ns | 0.0997 ns | 1.15 | 0.01 | + * CompatibleFromBytes | 32 | 38.7017 ns | 0.1103 ns | 1.88 | 0.01 | + * PermutedByRef | 32 | 39.2892 ns | 0.1366 ns | 1.90 | 0.01 | + * PermutedByVal | 32 | 38.5178 ns | 0.1946 ns | 1.87 | 0.01 | + * PermutedFromBytes | 32 | 38.6683 ns | 0.0801 ns | 1.87 | 0.01 | + * + * !!! Conclusion !!! + * All memory-incompatible (permuted) variants are equivalent with the the "FromBytes" solution. + * In memory compatible cases we should use the optimized Bulk-copying variant anyways, + * so there is no benefit introducing non-bulk API-s other than PackFromBytes() OR PackFromRgba32(). + */ +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromVector4.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromVector4.cs new file mode 100644 index 0000000000..721ac121a3 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromVector4.cs @@ -0,0 +1,156 @@ +// ReSharper disable InconsistentNaming +namespace ImageSharp.Benchmarks.General +{ + using System.Numerics; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + using BenchmarkDotNet.Attributes; + + public class PixelConversion_ConvertFromVector4 + { + interface ITestPixel + where T : struct, ITestPixel + { + void FromVector4(Vector4 source); + + void FromVector4(ref Vector4 source); + } + + [StructLayout(LayoutKind.Sequential)] + struct TestArgb : ITestPixel + { + private byte a, r, g, b; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 p) + { + this.r = (byte)p.X; + this.g = (byte)p.Y; + this.b = (byte)p.Z; + this.a = (byte)p.W; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(ref Vector4 p) + { + this.r = (byte)p.X; + this.g = (byte)p.Y; + this.b = (byte)p.Z; + this.a = (byte)p.W; + } + } + + [StructLayout(LayoutKind.Sequential)] + struct TestRgbaVector : ITestPixel + { + private Vector4 v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(Vector4 p) + { + this.v = p; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void FromVector4(ref Vector4 p) + { + this.v = p; + } + } + + struct ConversionRunner + where T : struct, ITestPixel + { + private T[] dest; + + private Vector4[] source; + + public ConversionRunner(int count) + { + this.dest = new T[count]; + this.source = new Vector4[count]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunByRefConversion() + { + int count = this.dest.Length; + + ref T destBaseRef = ref this.dest[0]; + ref Vector4 sourceBaseRef = ref this.source[0]; + + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref destBaseRef, i).FromVector4(ref Unsafe.Add(ref sourceBaseRef, i)); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunByValConversion() + { + int count = this.dest.Length; + + ref T destBaseRef = ref this.dest[0]; + ref Vector4 sourceBaseRef = ref this.source[0]; + + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref destBaseRef, i).FromVector4(Unsafe.Add(ref sourceBaseRef, i)); + } + } + } + + private ConversionRunner nonVectorRunner; + + private ConversionRunner vectorRunner; + + [Params(32)] + public int Count { get; set; } + + [Setup] + public void Setup() + { + this.nonVectorRunner = new ConversionRunner(this.Count); + this.vectorRunner = new ConversionRunner(this.Count); + } + + [Benchmark(Baseline = true)] + public void VectorByRef() + { + this.vectorRunner.RunByRefConversion(); + } + + [Benchmark] + public void VectorByVal() + { + this.vectorRunner.RunByValConversion(); + } + + [Benchmark] + public void NonVectorByRef() + { + this.nonVectorRunner.RunByRefConversion(); + } + + [Benchmark] + public void NonVectorByVal() + { + this.nonVectorRunner.RunByValConversion(); + } + + } + + /* + * Results: + * Method | Count | Mean | StdDev | Scaled | Scaled-StdDev | + * --------------- |------ |----------- |---------- |------- |-------------- | + * VectorByRef | 32 | 23.6678 ns | 0.1141 ns | 1.00 | 0.00 | + * VectorByVal | 32 | 24.5347 ns | 0.0771 ns | 1.04 | 0.01 | + * NonVectorByRef | 32 | 59.0187 ns | 0.2114 ns | 2.49 | 0.01 | + * NonVectorByVal | 32 | 58.7529 ns | 0.2545 ns | 2.48 | 0.02 | + * + * !!! Conclusion !!! + * We do not need by-ref version of ConvertFromVector4() stuff + */ +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertToRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertToRgba32.cs new file mode 100644 index 0000000000..0c9a8af3fd --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertToRgba32.cs @@ -0,0 +1,156 @@ +// ReSharper disable InconsistentNaming +namespace ImageSharp.Benchmarks.General +{ + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + using BenchmarkDotNet.Attributes; + + /// + /// When implementing TPixel --> Rgba32 style conversions on IPixel, should which API should we prefer? + /// 1. Rgba32 ToRgba32(); + /// OR + /// 2. void CopyToRgba32(ref Rgba32 dest); + /// ? + /// + public class PixelConversion_ConvertToRgba32 + { + interface ITestPixel + where T : struct, ITestPixel + { + Rgba32 ToRgba32(); + + void CopyToRgba32(ref Rgba32 dest); + } + + [StructLayout(LayoutKind.Sequential)] + struct TestArgb : ITestPixel + { + private byte a, r, g, b; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgba32 ToRgba32() + { + return new Rgba32(this.r, this.g, this.b, this.a); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyToRgba32(ref Rgba32 dest) + { + dest.R = this.r; + dest.G = this.g; + dest.B = this.b; + dest.A = this.a; + } + } + + [StructLayout(LayoutKind.Sequential)] + struct TestRgba : ITestPixel + { + private byte r, g, b, a; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgba32 ToRgba32() + { + return Unsafe.As(ref this); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyToRgba32(ref Rgba32 dest) + { + dest = Unsafe.As(ref this); + } + } + + struct ConversionRunner + where T : struct, ITestPixel + { + private T[] source; + + private Rgba32[] dest; + + public ConversionRunner(int count) + { + this.source = new T[count]; + this.dest = new Rgba32[count]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunRetvalConversion() + { + int count = this.source.Length; + + ref T sourceBaseRef = ref this.source[0]; + ref Rgba32 destBaseRef = ref this.dest[0]; + + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref destBaseRef, i) = Unsafe.Add(ref sourceBaseRef, i).ToRgba32(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunCopyToConversion() + { + int count = this.source.Length; + + ref T sourceBaseRef = ref this.source[0]; + ref Rgba32 destBaseRef = ref this.dest[0]; + + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref sourceBaseRef, i).CopyToRgba32(ref Unsafe.Add(ref destBaseRef, i)); + } + } + } + + private ConversionRunner compatibleMemoryLayoutRunner; + + private ConversionRunner permutedRunner; + + [Params(128)] + public int Count { get; set; } + + [Setup] + public void Setup() + { + this.compatibleMemoryLayoutRunner = new ConversionRunner(this.Count); + this.permutedRunner = new ConversionRunner(this.Count); + } + + [Benchmark(Baseline = true)] + public void CompatibleRetval() + { + this.compatibleMemoryLayoutRunner.RunRetvalConversion(); + } + + [Benchmark] + public void CompatibleCopyTo() + { + this.compatibleMemoryLayoutRunner.RunCopyToConversion(); + } + + [Benchmark] + public void PermutedRetval() + { + this.permutedRunner.RunRetvalConversion(); + } + + [Benchmark] + public void PermutedCopyTo() + { + this.permutedRunner.RunCopyToConversion(); + } + } + + /* + * Results: + * + * Method | Count | Mean | StdDev | Scaled | Scaled-StdDev | + * --------------- |------ |------------ |---------- |------- |-------------- | + * CompatibleRetval | 128 | 89.7358 ns | 2.2389 ns | 1.00 | 0.00 | + * CompatibleCopyTo | 128 | 89.4112 ns | 2.2901 ns | 1.00 | 0.03 | + * PermutedRetval | 128 | 845.4038 ns | 5.6154 ns | 9.43 | 0.23 | + * PermutedCopyTo | 128 | 155.6004 ns | 3.8870 ns | 1.73 | 0.06 | + */ +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs index 18c99a744f..bd2bf599c4 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelIndexing.cs @@ -145,7 +145,7 @@ // [Params(1024)] public int Count { get; set; } = 1024; - [Setup] + [GlobalSetup] public void Setup() { this.width = 2048; @@ -158,7 +158,7 @@ this.endIndex = 2048 / 2 + (this.Count / 2); } - [Cleanup] + [GlobalCleanup] public void Cleanup() { this.buffer.Dispose(); diff --git a/tests/ImageSharp.Benchmarks/General/RoundSinglePrecisionBlocks.cs b/tests/ImageSharp.Benchmarks/General/RoundSinglePrecisionBlocks.cs index 52880168d8..f998290340 100644 --- a/tests/ImageSharp.Benchmarks/General/RoundSinglePrecisionBlocks.cs +++ b/tests/ImageSharp.Benchmarks/General/RoundSinglePrecisionBlocks.cs @@ -22,7 +22,7 @@ private Block8x8F inputDividend = default(Block8x8F); private Block8x8F inputDivisior = default(Block8x8F); - [Setup] + [GlobalSetup] public void Setup() { for (int i = 0; i < Block8x8F.ScalarCount; i++) diff --git a/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs b/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs index a7afa336e2..820789ee7c 100644 --- a/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs +++ b/tests/ImageSharp.Benchmarks/General/Vector4Constants.cs @@ -20,7 +20,7 @@ namespace ImageSharp.Benchmarks.General private Vector4 parameter; - [Setup] + [GlobalSetup] public void Setup() { this.random = new Random(42); diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs index dd20e85d5b..b882403c85 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Benchmarks.General.Vectorization private uint testValue; - [Setup] + [GlobalSetup] public void Setup() { this.input = new uint[this.InputSize]; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs index 61582b7dc5..caadaaa347 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Benchmarks.General.Vectorization private float testValue; - [Setup] + [GlobalSetup] public void Setup() { this.input = new float[this.InputSize]; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs index 75fa03c04b..6b3edb192b 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Benchmarks.General.Vectorization private uint testValue; - [Setup] + [GlobalSetup] public void Setup() { this.input = new uint[this.InputSize]; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs index ee33cf210a..e5ae49b2a6 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Benchmarks.General.Vectorization private float testValue; - [Setup] + [GlobalSetup] public void Setup() { this.input = new float[this.InputSize]; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs index f7d6cf9b9c..532acc02d5 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Benchmarks.General.Vectorization private uint testValue; - [Setup] + [GlobalSetup] public void Setup() { this.input = new uint[this.InputSize]; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs index b0ca181cd6..30443fad9d 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Benchmarks.General.Vectorization } - [Setup] + [GlobalSetup] public void Setup() { this.input = new uint[this.InputSize]; diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs index 018f113349..73fc886487 100644 --- a/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/VectorFetching.cs @@ -20,7 +20,7 @@ namespace ImageSharp.Benchmarks.General.Vectorization [Params(64)] public int InputSize { get; set; } - [Setup] + [GlobalSetup] public void Setup() { this.data = new float[this.InputSize]; diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs b/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs index 87baa8b7ee..3c327bd562 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeBmp.cs @@ -18,7 +18,7 @@ namespace ImageSharp.Benchmarks.Image { private byte[] bmpBytes; - [Setup] + [GlobalSetup] public void ReadImages() { if (this.bmpBytes == null) diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs b/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs index a1fddc5025..8f79386a67 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeFilteredPng.cs @@ -20,7 +20,7 @@ namespace ImageSharp.Benchmarks.Image private MemoryStream filter3; private MemoryStream filter4; - [Setup] + [GlobalSetup] public void ReadImages() { this.filter0 = new MemoryStream(File.ReadAllBytes("../ImageSharp.Tests/TestImages/Formats/Png/filter0.png")); diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs b/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs index 02620fe744..b1e56c6bd2 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeGif.cs @@ -18,7 +18,7 @@ namespace ImageSharp.Benchmarks.Image { private byte[] gifBytes; - [Setup] + [GlobalSetup] public void ReadImages() { if (this.gifBytes == null) diff --git a/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs index ab45f95f43..455af48ad2 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodeJpeg.cs @@ -18,7 +18,7 @@ namespace ImageSharp.Benchmarks.Image { private byte[] jpegBytes; - [Setup] + [GlobalSetup] public void ReadImages() { if (this.jpegBytes == null) diff --git a/tests/ImageSharp.Benchmarks/Image/DecodePng.cs b/tests/ImageSharp.Benchmarks/Image/DecodePng.cs index 9b71fd0583..dd26a2cf73 100644 --- a/tests/ImageSharp.Benchmarks/Image/DecodePng.cs +++ b/tests/ImageSharp.Benchmarks/Image/DecodePng.cs @@ -18,7 +18,7 @@ namespace ImageSharp.Benchmarks.Image { private byte[] pngBytes; - [Setup] + [GlobalSetup] public void ReadImages() { if (this.pngBytes == null) diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs b/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs index 52a0399124..4941e17cba 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeBmp.cs @@ -20,7 +20,7 @@ namespace ImageSharp.Benchmarks.Image private Image bmpDrawing; private Image bmpCore; - [Setup] + [GlobalSetup] public void ReadImages() { if (this.bmpStream == null) @@ -32,7 +32,7 @@ namespace ImageSharp.Benchmarks.Image } } - [Cleanup] + [GlobalCleanup] public void Cleanup() { this.bmpStream.Dispose(); diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs b/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs index 5eaa8940b4..e6600594f0 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeGif.cs @@ -20,7 +20,7 @@ namespace ImageSharp.Benchmarks.Image private Image bmpDrawing; private Image bmpCore; - [Setup] + [GlobalSetup] public void ReadImages() { if (this.bmpStream == null) @@ -32,7 +32,7 @@ namespace ImageSharp.Benchmarks.Image } } - [Cleanup] + [GlobalCleanup] public void Cleanup() { this.bmpStream.Dispose(); diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs b/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs index 4d25c82efd..88e82f0796 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeIndexedPng.cs @@ -27,7 +27,7 @@ namespace ImageSharp.Benchmarks.Image [Params(false)] public bool LargeImage { get; set; } - [Setup] + [GlobalSetup] public void ReadImages() { if (this.bmpStream == null) @@ -41,7 +41,7 @@ namespace ImageSharp.Benchmarks.Image } } - [Cleanup] + [GlobalCleanup] public void Cleanup() { this.bmpStream.Dispose(); diff --git a/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs b/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs index efd4e8ac51..07c2560fd8 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodeJpeg.cs @@ -20,7 +20,7 @@ namespace ImageSharp.Benchmarks.Image private Image bmpDrawing; private Image bmpCore; - [Setup] + [GlobalSetup] public void ReadImages() { if (this.bmpStream == null) @@ -32,7 +32,7 @@ namespace ImageSharp.Benchmarks.Image } } - [Cleanup] + [GlobalCleanup] public void Cleanup() { this.bmpStream.Dispose(); diff --git a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs index 11182ac485..3ec83aa7cf 100644 --- a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs +++ b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs @@ -29,7 +29,7 @@ namespace ImageSharp.Benchmarks.Image [Params(false)] public bool UseOctreeQuantizer { get; set; } - [Setup] + [GlobalSetup] public void ReadImages() { if (this.bmpStream == null) @@ -44,7 +44,7 @@ namespace ImageSharp.Benchmarks.Image } } - [Cleanup] + [GlobalCleanup] public void Cleanup() { this.bmpStream.Dispose(); diff --git a/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs index dfee978ccb..e01a5951ef 100644 --- a/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/Image/MultiImageBenchmarkBase.cs @@ -83,7 +83,7 @@ namespace ImageSharp.Benchmarks.Image protected abstract IEnumerable InputImageSubfoldersOrFiles { get; } - [Setup] + [GlobalSetup] public void ReadImages() { if (!Vector.IsHardwareAccelerated) diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 763ede5217..72593a0da4 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -1,16 +1,26 @@  + netcoreapp1.1;net461 Exe - net461 - win7-x64 True + + + win7-x64 false - + + + + + + + + + diff --git a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs index d4920ff081..1f69030a00 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/DetectEdges.cs @@ -9,7 +9,7 @@ namespace ImageSharp.Benchmarks using BenchmarkDotNet.Attributes; - using Processing; + using ImageSharp.Processing; using CoreImage = ImageSharp.Image; @@ -17,7 +17,7 @@ namespace ImageSharp.Benchmarks { private Image image; - [Setup] + [GlobalSetup] public void ReadImage() { if (this.image == null) @@ -29,7 +29,7 @@ namespace ImageSharp.Benchmarks } } - [Cleanup] + [GlobalCleanup] public void Cleanup() { this.image.Dispose(); diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs index 7608d30659..049a00c1ca 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs @@ -22,7 +22,7 @@ namespace ImageSharp.Benchmarks private GlowProcessor bulk; private GlowProcessorParallel parallel; - [Setup] + [GlobalSetup] public void Setup() { this.bulk = new GlowProcessor(NamedColors.Beige, GraphicsOptions.Default) { Radius = 800 * .5f, }; @@ -79,7 +79,7 @@ namespace ImageSharp.Benchmarks int startX = sourceRectangle.X; int endX = sourceRectangle.Right; TPixel glowColor = this.GlowColor; - Vector2 centre = Rectangle.Center(sourceRectangle).ToVector2(); + Vector2 centre = Rectangle.Center(sourceRectangle); float maxDistance = this.Radius > 0 ? MathF.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; // Align start/end positions. diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index 53cdffaeaf..ae17f3698d 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -17,8 +17,8 @@ - + diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs index 7ea459aaee..6f93df16e5 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Sandbox46/Program.cs @@ -10,6 +10,8 @@ namespace ImageSharp.Sandbox46 using ImageSharp.Tests; using ImageSharp.Tests.Colors; + using ImageSharp.Tests.PixelFormats; + using ImageSharp.Tests.Processing.Transforms; using Xunit.Abstractions; diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs b/tests/ImageSharp.Sandbox46/Tests/PixelFormats/PixelBlenderTests.cs similarity index 96% rename from tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs rename to tests/ImageSharp.Sandbox46/Tests/PixelFormats/PixelBlenderTests.cs index a9108692ed..7efd3a6fcb 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations.cs +++ b/tests/ImageSharp.Sandbox46/Tests/PixelFormats/PixelBlenderTests.cs @@ -15,7 +15,7 @@ namespace ImageSharp.Tests.PixelFormats public class PixelOperations { - public static TheoryData blenderMappings = new TheoryData() + public static TheoryData BlenderMappings = new TheoryData() { { new TestPixel(), typeof(DefaultNormalPixelBlender), PixelBlenderMode.Normal }, { new TestPixel(), typeof(DefaultScreenPixelBlender), PixelBlenderMode.Screen }, @@ -39,7 +39,7 @@ namespace ImageSharp.Tests.PixelFormats }; [Theory] - [MemberData(nameof(blenderMappings))] + [MemberData(nameof(BlenderMappings))] public void ReturnsCorrectBlender(TestPixel pixel, Type type, PixelBlenderMode mode) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/Common/BufferTests.cs b/tests/ImageSharp.Tests/Common/BufferTests.cs index 010bf40b31..25ef173d4c 100644 --- a/tests/ImageSharp.Tests/Common/BufferTests.cs +++ b/tests/ImageSharp.Tests/Common/BufferTests.cs @@ -163,7 +163,7 @@ namespace ImageSharp.Tests.Common // Assert.Equal(buffer.Array, span.ToArray()); // Assert.Equal(0, span.Start); Assert.SpanPointsTo(span, buffer); - Assert.Equal(span.Length, 42); + Assert.Equal(42, span.Length); } } diff --git a/tests/ImageSharp.Tests/Common/ConstantsTests.cs b/tests/ImageSharp.Tests/Common/ConstantsTests.cs index 0adda0c0f6..be4addf913 100644 --- a/tests/ImageSharp.Tests/Common/ConstantsTests.cs +++ b/tests/ImageSharp.Tests/Common/ConstantsTests.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Tests.Common [Fact] public void Epsilon() { - Assert.Equal(Constants.Epsilon, 0.001f); + Assert.Equal(0.001f, Constants.Epsilon); } } } diff --git a/tests/ImageSharp.Tests/Drawing/BeziersTests.cs b/tests/ImageSharp.Tests/Drawing/BeziersTests.cs index ff68921994..3d49a70b81 100644 --- a/tests/ImageSharp.Tests/Drawing/BeziersTests.cs +++ b/tests/ImageSharp.Tests/Drawing/BeziersTests.cs @@ -43,8 +43,8 @@ namespace ImageSharp.Tests.Drawing Assert.Equal(Rgba32.HotPink, sourcePixels[138, 115]); //start points - Assert.Equal(Rgba32.HotPink, sourcePixels[10, 400]); - Assert.Equal(Rgba32.HotPink, sourcePixels[300, 400]); + Assert.Equal(Rgba32.HotPink, sourcePixels[10, 395]); + Assert.Equal(Rgba32.HotPink, sourcePixels[300, 395]); //curve points should not be never be set Assert.Equal(Rgba32.Blue, sourcePixels[30, 10]); @@ -85,14 +85,14 @@ namespace ImageSharp.Tests.Drawing using (PixelAccessor sourcePixels = image.Lock()) { - //top of curve + // top of curve Assert.Equal(mergedColor, sourcePixels[138, 115]); - //start points - Assert.Equal(mergedColor, sourcePixels[10, 400]); - Assert.Equal(mergedColor, sourcePixels[300, 400]); + // start points + Assert.Equal(mergedColor, sourcePixels[10, 395]); + Assert.Equal(mergedColor, sourcePixels[300, 395]); - //curve points should not be never be set + // curve points should not be never be set Assert.Equal(Rgba32.Blue, sourcePixels[30, 10]); Assert.Equal(Rgba32.Blue, sourcePixels[240, 30]); diff --git a/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs b/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs index 6c742c2e0f..23f0569e75 100644 --- a/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs +++ b/tests/ImageSharp.Tests/Drawing/BlendedShapes.cs @@ -18,7 +18,7 @@ namespace ImageSharp.Tests.Drawing .Select(x=> new object[] { x }); [Theory] - [WithBlankImages(nameof(modes), 100, 100, PixelTypes.StandardImageClass)] + [WithBlankImages(nameof(modes), 100, 100, PixelTypes.Rgba32)] public void DrawBlendedValues(TestImageProvider provider, PixelBlenderMode mode) where TPixel : struct, IPixel { @@ -34,7 +34,7 @@ namespace ImageSharp.Tests.Drawing } [Theory] - [WithBlankImages(nameof(modes), 100, 100, PixelTypes.StandardImageClass)] + [WithBlankImages(nameof(modes), 100, 100, PixelTypes.Rgba32)] public void DrawBlendedValues_transparent(TestImageProvider provider, PixelBlenderMode mode) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs index 030034a8f2..42353a2aab 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs @@ -12,7 +12,7 @@ namespace ImageSharp.Tests public class DrawImageTest : FileTestBase { - private const PixelTypes PixelTypes = Tests.PixelTypes.StandardImageClass; + private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32; public static readonly string[] TestFiles = { TestImages.Jpeg.Baseline.Calliphora, diff --git a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs index 7d54879c36..4ba4a3a835 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawPathTests.cs @@ -13,6 +13,7 @@ namespace ImageSharp.Tests.Drawing using ImageSharp.PixelFormats; using Xunit; + using ImageSharp.Drawing.Pens; public class DrawPathTests : FileTestBase { @@ -43,7 +44,7 @@ namespace ImageSharp.Tests.Drawing using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Rgba32.HotPink, sourcePixels[9, 9]); + Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]); @@ -89,7 +90,7 @@ namespace ImageSharp.Tests.Drawing using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(mergedColor, sourcePixels[9, 9]); + Assert.Equal(mergedColor, sourcePixels[11, 11]); Assert.Equal(mergedColor, sourcePixels[199, 149]); @@ -98,5 +99,32 @@ namespace ImageSharp.Tests.Drawing } } + + [Fact] + public void PathExtendingOffEdgeOfImageShouldNotBeCropped() + { + + string path = this.CreateOutputDirectory("Drawing", "Path"); + using (var image = new Image(256, 256)) + { + image.Fill(Rgba32.Black); + var pen = Pens.Solid(Rgba32.White, 5f); + + for (int i = 0; i < 300; i += 20) + { + image.DrawLines(pen, new Vector2[] { new Vector2(100, 2), new Vector2(-10, i) }); + } + + using (FileStream output = File.OpenWrite($"{path}/ClippedLines.png")) + { + image + .Save(output); + } + using (PixelAccessor sourcePixels = image.Lock()) + { + Assert.Equal(Rgba32.White, sourcePixels[0, 90]); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs index c7b789da0d..58e55c1245 100644 --- a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs @@ -8,6 +8,9 @@ namespace ImageSharp.Tests.Drawing using Moq; using ImageSharp.PixelFormats; + using System; + using ImageSharp.Drawing.Pens; + using System.Numerics; public class FillRegionProcessorTests { @@ -26,14 +29,61 @@ namespace ImageSharp.Tests.Drawing Mock region = new Mock(); region.Setup(x => x.Bounds).Returns(bounds); - GraphicsOptions options = new GraphicsOptions(antialias) { + GraphicsOptions options = new GraphicsOptions(antialias) + { AntialiasSubpixelDepth = 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)); + region.Verify(x => x.Scan(It.IsAny(), It.IsAny>()), Times.Exactly(4)); + } + + [Fact] + public void FillOffCanvas() + { + + ImageSharp.Rectangle bounds = new ImageSharp.Rectangle(-100, -10, 10, 10); + + Mock> brush = new Mock>(); + Mock region = new Mock(); + region.Setup(x => x.Bounds).Returns(bounds); + + region.Setup(x => x.MaxIntersections).Returns(10); + region.Setup(x => x.Scan(It.IsAny(), It.IsAny>())) + .Returns>((y, span) => + { + if (y < 5) + { + span[0] = -10f; + span[1] = 100f; + return 2; + } + return 0; + }); + + GraphicsOptions options = new GraphicsOptions(true) + { + }; + FillRegionProcessor processor = new FillRegionProcessor(brush.Object, region.Object, options); + Image img = new Image(10, 10); + processor.Apply(img, bounds); + } + + + + [Fact] + public void DrawOffCanvas() + { + + using (var img = new Image(10, 10)) + { + img.DrawLines(new Pen(Rgba32.Black, 10), new Vector2[] { + new Vector2(-10, 5), + new Vector2(20, 5), + }); + } } } } diff --git a/tests/ImageSharp.Tests/Drawing/LineTests.cs b/tests/ImageSharp.Tests/Drawing/LineTests.cs index 3396d89c57..e751557b6f 100644 --- a/tests/ImageSharp.Tests/Drawing/LineTests.cs +++ b/tests/ImageSharp.Tests/Drawing/LineTests.cs @@ -38,7 +38,7 @@ namespace ImageSharp.Tests.Drawing using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Rgba32.HotPink, sourcePixels[9, 9]); + Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]); @@ -69,7 +69,7 @@ namespace ImageSharp.Tests.Drawing using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Rgba32.HotPink, sourcePixels[9, 9]); + Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); Assert.Equal(Rgba32.HotPink, sourcePixels[199, 149]); @@ -187,7 +187,7 @@ namespace ImageSharp.Tests.Drawing using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(mergedColor, sourcePixels[9, 9]); + Assert.Equal(mergedColor, sourcePixels[11, 11]); Assert.Equal(mergedColor, sourcePixels[199, 149]); @@ -217,7 +217,7 @@ namespace ImageSharp.Tests.Drawing using (PixelAccessor sourcePixels = image.Lock()) { - Assert.Equal(Rgba32.HotPink, sourcePixels[8, 8]); + Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); Assert.Equal(Rgba32.HotPink, sourcePixels[198, 10]); diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs deleted file mode 100644 index a9b2284e8a..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawBeziersTests.cs +++ /dev/null @@ -1,163 +0,0 @@ - -namespace ImageSharp.Tests.Drawing.Paths -{ - using System; - - using ImageSharp.Drawing.Brushes; - - 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(); - 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), - new Vector2(20,10), - new Vector2(30,10), - }; - private ProcessorWatchingImage img; - - public DrawBeziersTests() - { - this.img = new Paths.ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - img.Dispose(); - } - - [Fact] - public void CorrectlySetsBrushThicknessAndPoints() - { - img.DrawBeziers(brush, thickness, points); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - Assert.NotNull(path.Path); - - SixLabors.Shapes.Path vector = Assert.IsType(path.Path); - - BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(brush, pen.Brush); - Assert.Equal(thickness, pen.Width); - } - - [Fact] - public void CorrectlySetsBrushThicknessPointsAndOptions() - { - img.DrawBeziers(brush, thickness, points, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - SixLabors.Shapes.Path vector = Assert.IsType(path.Path); - BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(brush, pen.Brush); - Assert.Equal(thickness, pen.Width); - } - - [Fact] - public void CorrectlySetsColorThicknessAndPoints() - { - img.DrawBeziers(color, thickness, points); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - SixLabors.Shapes.Path vector = Assert.IsType(path.Path); - BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(thickness, pen.Width); - - SolidBrush brush = Assert.IsType>(pen.Brush); - Assert.Equal(color, brush.Color); - } - - [Fact] - public void CorrectlySetsColorThicknessPointsAndOptions() - { - img.DrawBeziers(color, thickness, points, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - SixLabors.Shapes.Path vector = Assert.IsType(path.Path); - BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(thickness, pen.Width); - - SolidBrush brush = Assert.IsType>(pen.Brush); - Assert.Equal(color, brush.Color); - } - - [Fact] - public void CorrectlySetsPenAndPoints() - { - img.DrawBeziers(pen, points); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - SixLabors.Shapes.Path vector = Assert.IsType(path.Path); - BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Assert.Equal(pen, processor.Pen); - } - - [Fact] - public void CorrectlySetsPenPointsAndOptions() - { - img.DrawBeziers(pen, points, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - SixLabors.Shapes.Path vector = Assert.IsType(path.Path); - BezierLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Assert.Equal(pen, processor.Pen); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs deleted file mode 100644 index 3b7ba303dc..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawLinesTests.cs +++ /dev/null @@ -1,160 +0,0 @@ - -namespace ImageSharp.Tests.Drawing.Paths -{ - using System; - - using ImageSharp.Drawing.Brushes; - 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(); - 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), - new Vector2(20,10), - new Vector2(30,10), - }; - private ProcessorWatchingImage img; - - public DrawLinesTests() - { - this.img = new Paths.ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - img.Dispose(); - } - - [Fact] - public void CorrectlySetsBrushThicknessAndPoints() - { - img.DrawLines(brush, thickness, points); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - SixLabors.Shapes.Path vector = Assert.IsType(path.Path); - LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(brush, pen.Brush); - Assert.Equal(thickness, pen.Width); - } - - [Fact] - public void CorrectlySetsBrushThicknessPointsAndOptions() - { - img.DrawLines(brush, thickness, points, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - SixLabors.Shapes.Path vector = Assert.IsType(path.Path); - LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(brush, pen.Brush); - Assert.Equal(thickness, pen.Width); - } - - [Fact] - public void CorrectlySetsColorThicknessAndPoints() - { - img.DrawLines(color, thickness, points); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - SixLabors.Shapes.Path vector = Assert.IsType(path.Path); - LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(thickness, pen.Width); - - SolidBrush brush = Assert.IsType>(pen.Brush); - Assert.Equal(color, brush.Color); - } - - [Fact] - public void CorrectlySetsColorThicknessPointsAndOptions() - { - img.DrawLines(color, thickness, points, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - SixLabors.Shapes.Path vector = Assert.IsType(path.Path); - LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(thickness, pen.Width); - - SolidBrush brush = Assert.IsType>(pen.Brush); - Assert.Equal(color, brush.Color); - } - - [Fact] - public void CorrectlySetsPenAndPoints() - { - img.DrawLines(pen, points); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - SixLabors.Shapes.Path vector = Assert.IsType(path.Path); - LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Assert.Equal(pen, processor.Pen); - } - - [Fact] - public void CorrectlySetsPenPointsAndOptions() - { - img.DrawLines(pen, points, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - SixLabors.Shapes.Path vector = Assert.IsType(path.Path); - LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Assert.Equal(pen, processor.Pen); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs deleted file mode 100644 index 0bdcbdc082..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawPath.cs +++ /dev/null @@ -1,149 +0,0 @@ - -namespace ImageSharp.Tests.Drawing.Paths -{ - using System; - - using ImageSharp.Drawing.Brushes; - - 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(); - 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), - new Vector2(20,10), - new Vector2(30,10), - })); - private ProcessorWatchingImage img; - - public DrawPath() - { - this.img = new Paths.ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - img.Dispose(); - } - - [Fact] - public void CorrectlySetsBrushThicknessAndPath() - { - img.Draw(brush, thickness, path); - - Assert.NotEmpty(img.ProcessorApplications); - 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); - Assert.Equal(brush, pen.Brush); - Assert.Equal(thickness, pen.Width); - } - - [Fact] - public void CorrectlySetsBrushThicknessPathAndOptions() - { - img.Draw(brush, thickness, path, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - 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); - Assert.Equal(brush, pen.Brush); - Assert.Equal(thickness, pen.Width); - } - - [Fact] - public void CorrectlySetsColorThicknessAndPath() - { - img.Draw(color, thickness, path); - - Assert.NotEmpty(img.ProcessorApplications); - 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); - Assert.Equal(thickness, pen.Width); - - SolidBrush brush = Assert.IsType>(pen.Brush); - Assert.Equal(color, brush.Color); - } - - [Fact] - public void CorrectlySetsColorThicknessPathAndOptions() - { - img.Draw(color, thickness, path, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - 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); - Assert.Equal(thickness, pen.Width); - - SolidBrush brush = Assert.IsType>(pen.Brush); - Assert.Equal(color, brush.Color); - } - - [Fact] - public void CorrectlySetsPenAndPath() - { - img.Draw(pen, path); - - Assert.NotEmpty(img.ProcessorApplications); - 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); - - Assert.Equal(pen, processor.Pen); - } - - [Fact] - public void CorrectlySetsPenPathAndOptions() - { - img.Draw(pen, path, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapePath shapepath = Assert.IsType(processor.Path); - Assert.Equal(path, shapepath.Path); - - Assert.Equal(pen, processor.Pen); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs deleted file mode 100644 index 3474e6f62f..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawPolygon.cs +++ /dev/null @@ -1,161 +0,0 @@ - -namespace ImageSharp.Tests.Drawing.Paths -{ - using System; - - using ImageSharp.Drawing.Brushes; - - 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(); - 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), - new Vector2(20,10), - new Vector2(30,10), - }; - private ProcessorWatchingImage img; - - public DrawPolygon() - { - this.img = new Paths.ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - img.Dispose(); - } - - [Fact] - public void CorrectlySetsBrushThicknessAndPoints() - { - img.DrawPolygon(brush, thickness, points); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - Polygon vector = Assert.IsType(path.Path); - LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(brush, pen.Brush); - Assert.Equal(thickness, pen.Width); - } - - [Fact] - public void CorrectlySetsBrushThicknessPointsAndOptions() - { - img.DrawPolygon(brush, thickness, points, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - Polygon vector = Assert.IsType(path.Path); - LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(brush, pen.Brush); - Assert.Equal(thickness, pen.Width); - } - - [Fact] - public void CorrectlySetsColorThicknessAndPoints() - { - img.DrawPolygon(color, thickness, points); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - Polygon vector = Assert.IsType(path.Path); - LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(thickness, pen.Width); - - SolidBrush brush = Assert.IsType>(pen.Brush); - Assert.Equal(color, brush.Color); - } - - [Fact] - public void CorrectlySetsColorThicknessPointsAndOptions() - { - img.DrawPolygon(color, thickness, points, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - Polygon vector = Assert.IsType(path.Path); - LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(thickness, pen.Width); - - SolidBrush brush = Assert.IsType>(pen.Brush); - Assert.Equal(color, brush.Color); - } - - [Fact] - public void CorrectlySetsPenAndPoints() - { - img.DrawPolygon(pen, points); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - Polygon vector = Assert.IsType(path.Path); - LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Assert.Equal(pen, processor.Pen); - } - - [Fact] - public void CorrectlySetsPenPointsAndOptions() - { - img.DrawPolygon(pen, points, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapePath path = Assert.IsType(processor.Path); - - Polygon vector = Assert.IsType(path.Path); - LinearLineSegment segment = Assert.IsType(vector.LineSegments[0]); - - Assert.Equal(pen, processor.Pen); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs deleted file mode 100644 index e08e702c1d..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Paths/DrawRectangle.cs +++ /dev/null @@ -1,177 +0,0 @@ - -namespace ImageSharp.Tests.Drawing.Paths -{ - using System; - using ImageSharp; - using ImageSharp.Drawing.Brushes; - using Xunit; - using ImageSharp.Drawing; - using ImageSharp.Drawing.Processors; - using ImageSharp.Drawing.Pens; - using ImageSharp.PixelFormats; - - public class DrawRectangle : IDisposable - { - float thickness = 7.2f; - GraphicsOptions noneDefault = new GraphicsOptions(); - 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; - - public DrawRectangle() - { - this.img = new Paths.ProcessorWatchingImage(10, 10); - } - - public void Dispose() - { - img.Dispose(); - } - - [Fact] - public void CorrectlySetsBrushThicknessAndRectangle() - { - img.Draw(brush, thickness, rectangle); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath shapepath = Assert.IsType(processor.Path); - SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Path); - - Assert.Equal(rect.Location.X, rectangle.X); - Assert.Equal(rect.Location.Y, rectangle.Y); - Assert.Equal(rect.Size.Width, rectangle.Width); - Assert.Equal(rect.Size.Height, rectangle.Height); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(brush, pen.Brush); - Assert.Equal(thickness, pen.Width); - } - - [Fact] - public void CorrectlySetsBrushThicknessRectangleAndOptions() - { - img.Draw(brush, thickness, rectangle, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapePath shapepath = Assert.IsType(processor.Path); - - SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Path); - - Assert.Equal(rect.Location.X, rectangle.X); - Assert.Equal(rect.Location.Y, rectangle.Y); - Assert.Equal(rect.Size.Width, rectangle.Width); - Assert.Equal(rect.Size.Height, rectangle.Height); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(brush, pen.Brush); - Assert.Equal(thickness, pen.Width); - } - - [Fact] - public void CorrectlySetsColorThicknessAndRectangle() - { - img.Draw(color, thickness, rectangle); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath shapepath = Assert.IsType(processor.Path); - - SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Path); - - Assert.Equal(rect.Location.X, rectangle.X); - Assert.Equal(rect.Location.Y, rectangle.Y); - Assert.Equal(rect.Size.Width, rectangle.Width); - Assert.Equal(rect.Size.Height, rectangle.Height); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(thickness, pen.Width); - - SolidBrush brush = Assert.IsType>(pen.Brush); - Assert.Equal(color, brush.Color); - } - - [Fact] - public void CorrectlySetsColorThicknessRectangleAndOptions() - { - img.Draw(color, thickness, rectangle, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapePath shapepath = Assert.IsType(processor.Path); - - SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Path); - - Assert.Equal(rect.Location.X, rectangle.X); - Assert.Equal(rect.Location.Y, rectangle.Y); - Assert.Equal(rect.Size.Width, rectangle.Width); - Assert.Equal(rect.Size.Height, rectangle.Height); - - Pen pen = Assert.IsType>(processor.Pen); - Assert.Equal(thickness, pen.Width); - - SolidBrush brush = Assert.IsType>(pen.Brush); - Assert.Equal(color, brush.Color); - } - - [Fact] - public void CorrectlySetsPenAndRectangle() - { - img.Draw(pen, rectangle); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(GraphicsOptions.Default, processor.Options); - - ShapePath shapepath = Assert.IsType(processor.Path); - - SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Path); - - Assert.Equal(rect.Location.X, rectangle.X); - Assert.Equal(rect.Location.Y, rectangle.Y); - Assert.Equal(rect.Size.Width, rectangle.Width); - Assert.Equal(rect.Size.Height, rectangle.Height); - - Assert.Equal(pen, processor.Pen); - } - - [Fact] - public void CorrectlySetsPenRectangleAndOptions() - { - img.Draw(pen, rectangle, noneDefault); - - Assert.NotEmpty(img.ProcessorApplications); - DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[0].processor); - - Assert.Equal(noneDefault, processor.Options); - - ShapePath shapepath = Assert.IsType(processor.Path); - - SixLabors.Shapes.Rectangle rect = Assert.IsType(shapepath.Path); - - Assert.Equal(rect.Location.X, rectangle.X); - Assert.Equal(rect.Location.Y, rectangle.Y); - Assert.Equal(rect.Size.Width, rectangle.Width); - Assert.Equal(rect.Size.Height, rectangle.Height); - - Assert.Equal(pen, processor.Pen); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Paths/Extensions.cs b/tests/ImageSharp.Tests/Drawing/Paths/Extensions.cs index a18dd90bc2..24f2a6bd8b 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/Extensions.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/Extensions.cs @@ -5,7 +5,7 @@ namespace ImageSharp.Tests.Drawing.Paths using System.IO; using ImageSharp; using ImageSharp.Drawing.Brushes; - using Processing; + using ImageSharp.Processing; using System.Collections.Generic; using Xunit; using ImageSharp.Drawing; diff --git a/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs b/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs new file mode 100644 index 0000000000..e60ac0c13d --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Paths/FillPathCollection.cs @@ -0,0 +1,131 @@ + +namespace ImageSharp.Tests.Drawing.Paths +{ + using System; + using ImageSharp; + using ImageSharp.Drawing.Brushes; + using Xunit; + using ImageSharp.Drawing; + using System.Numerics; + using SixLabors.Shapes; + using ImageSharp.Drawing.Processors; + using ImageSharp.PixelFormats; + + public class FillPathCollection : IDisposable + { + GraphicsOptions noneDefault = new GraphicsOptions(); + Rgba32 color = Rgba32.HotPink; + SolidBrush brush = Brushes.Solid(Rgba32.HotPink); + IPath path1 = new SixLabors.Shapes.Path(new LinearLineSegment(new Vector2[] { + new Vector2(10,10), + new Vector2(20,10), + new Vector2(20,10), + new Vector2(30,10), + })); + IPath path2 = new SixLabors.Shapes.Path(new LinearLineSegment(new Vector2[] { + new Vector2(10,10), + new Vector2(20,10), + new Vector2(20,10), + new Vector2(30,10), + })); + + IPathCollection pathCollection; + + private ProcessorWatchingImage img; + + public FillPathCollection() + { + this.pathCollection = new PathCollection(path1, path2); + this.img = new ProcessorWatchingImage(10, 10); + } + + public void Dispose() + { + img.Dispose(); + } + + [Fact] + public void CorrectlySetsBrushAndPath() + { + img.Fill(brush, pathCollection); + + Assert.Equal(2, img.ProcessorApplications.Count); + for (var i = 0; i < 2; i++) + { + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[i].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]); + + Assert.Equal(brush, processor.Brush); + } + } + + [Fact] + public void CorrectlySetsBrushPathOptions() + { + img.Fill(brush, pathCollection, noneDefault); + + Assert.Equal(2, img.ProcessorApplications.Count); + for (var i = 0; i < 2; i++) + { + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor); + + Assert.Equal(noneDefault, processor.Options); + + ShapeRegion region = Assert.IsType(processor.Region); + Polygon polygon = Assert.IsType(region.Shape); + LinearLineSegment segments = Assert.IsType(polygon.LineSegments[0]); + + Assert.Equal(brush, processor.Brush); + } + } + + [Fact] + public void CorrectlySetsColorAndPath() + { + img.Fill(color, pathCollection); + + Assert.Equal(2, img.ProcessorApplications.Count); + for (var i = 0; i < 2; i++) + { + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor); + + Assert.Equal(GraphicsOptions.Default, processor.Options); + + ShapeRegion region = Assert.IsType(processor.Region); + Polygon polygon = Assert.IsType(region.Shape); + LinearLineSegment segments = Assert.IsType(polygon.LineSegments[0]); + + SolidBrush brush = Assert.IsType>(processor.Brush); + Assert.Equal(color, brush.Color); + } + } + + [Fact] + public void CorrectlySetsColorPathAndOptions() + { + img.Fill(color, pathCollection, noneDefault); + + Assert.Equal(2, img.ProcessorApplications.Count); + for (var i = 0; i < 2; i++) + { + FillRegionProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor); + + Assert.Equal(noneDefault, processor.Options); + + ShapeRegion region = Assert.IsType(processor.Region); + Polygon polygon = Assert.IsType(region.Shape); + LinearLineSegment segments = Assert.IsType(polygon.LineSegments[0]); + + 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 c1d34a112a..0392160788 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ProcessorWatchingImage.cs @@ -4,7 +4,7 @@ namespace ImageSharp.Tests.Drawing.Paths using System; using System.IO; using ImageSharp; - using Processing; + using ImageSharp.Processing; using System.Collections.Generic; using ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs index 494e2a6726..e791a2bfb7 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ShapePathTests.cs @@ -5,7 +5,7 @@ namespace ImageSharp.Tests.Drawing.Paths using System.IO; using ImageSharp; using ImageSharp.Drawing.Brushes; - using Processing; + using ImageSharp.Processing; using System.Collections.Generic; using Xunit; using ImageSharp.Drawing; @@ -18,109 +18,6 @@ namespace ImageSharp.Tests.Drawing.Paths public class ShapePathTests { - private readonly Mock pathMock1; - private readonly Mock pathMock2; - private readonly SixLabors.Shapes.Rectangle bounds1; - - public ShapePathTests() - { - this.pathMock2 = new Mock(); - this.pathMock1 = new Mock(); - - this.bounds1 = new SixLabors.Shapes.Rectangle(10.5f, 10, 10, 10); - pathMock1.Setup(x => x.Bounds).Returns(this.bounds1); - pathMock2.Setup(x => x.Bounds).Returns(this.bounds1); - // wire up the 2 mocks to reference eachother - pathMock1.Setup(x => x.AsClosedPath()).Returns(() => pathMock2.Object); - } - - [Fact] - public void ShapePathFromPathConvertsBoundsDoesNotProxyToShape() - { - ShapePath region = new ShapePath(pathMock1.Object); - - Assert.Equal(Math.Floor(bounds1.Left), region.Bounds.Left); - Assert.Equal(Math.Ceiling(bounds1.Right), region.Bounds.Right); - - pathMock1.Verify(x => x.Bounds); - } - - [Fact] - public void ShapePathFromPathMaxIntersectionsProxyToShape() - { - ShapePath region = new ShapePath(pathMock1.Object); - - int i = region.MaxIntersections; - pathMock1.Verify(x => x.MaxIntersections); - } - - [Fact] - public void ShapePathFromPathScanXProxyToShape() - { - int xToScan = 10; - ShapePath region = new ShapePath(pathMock1.Object); - - pathMock1.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((s, e, b, c, o) => { - Assert.Equal(xToScan, s.X); - Assert.Equal(xToScan, e.X); - Assert.True(s.Y < bounds1.Top); - Assert.True(e.Y > bounds1.Bottom); - }).Returns(0); - - int i = region.ScanX(xToScan, new float[0], 0, 0); - - pathMock1.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); - } - - [Fact] - public void ShapePathFromPathScanYProxyToShape() - { - int yToScan = 10; - ShapePath region = new ShapePath(pathMock1.Object); - - pathMock1.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((s, e, b, c, o) => { - Assert.Equal(yToScan, s.Y); - Assert.Equal(yToScan, e.Y); - Assert.True(s.X < bounds1.Left); - Assert.True(e.X > bounds1.Right); - }).Returns(0); - - int i = region.ScanY(yToScan, new float[0], 0, 0); - - pathMock1.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); - } - - - [Fact] - public void ShapePathFromShapeScanXProxyToShape() - { - int xToScan = 10; - ShapePath region = new ShapePath(pathMock1.Object); - - pathMock1.Setup(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((s, e, b, c, o) => { - Assert.Equal(xToScan, s.X); - Assert.Equal(xToScan, e.X); - Assert.True(s.Y < bounds1.Top); - Assert.True(e.Y > bounds1.Bottom); - }).Returns(0); - - int i = region.ScanX(xToScan, new float[0], 0, 0); - - pathMock1.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); - } - - [Fact] - public void GetPointInfoCallSinglePathForPath() - { - ShapePath region = new ShapePath(pathMock1.Object); - - ImageSharp.Drawing.PointInfo info = region.GetPointInfo(10, 1); - - pathMock1.Verify(x => x.Distance(new Vector2(10, 1)), Times.Once); - pathMock2.Verify(x => x.Distance(new Vector2(10, 1)), Times.Never); - } + // TODO readd these back in } } diff --git a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs index 78c3492332..7765436f72 100644 --- a/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Paths/ShapeRegionTests.cs @@ -5,7 +5,7 @@ namespace ImageSharp.Tests.Drawing.Paths using System.IO; using ImageSharp; using ImageSharp.Drawing.Brushes; - using Processing; + using ImageSharp.Processing; using System.Collections.Generic; using Xunit; using ImageSharp.Drawing; @@ -81,7 +81,7 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.True(e.X > bounds.Right); }).Returns(0); - int i = region.Scan(yToScan, new float[0], 0, 0); + int i = region.Scan(yToScan, new float[0]); pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } @@ -100,7 +100,7 @@ namespace ImageSharp.Tests.Drawing.Paths Assert.True(e.X > bounds.Right); }).Returns(0); - int i = region.Scan(yToScan, new float[0], 0, 0); + int i = region.Scan(yToScan, new float[0]); pathMock.Verify(x => x.FindIntersections(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs new file mode 100644 index 0000000000..60fe44acdf --- /dev/null +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs @@ -0,0 +1,251 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Drawing.Text +{ + using System; + using System.Numerics; + + using ImageSharp.Drawing; + using ImageSharp.Drawing.Brushes; + using ImageSharp.Drawing.Pens; + using ImageSharp.Drawing.Processors; + using ImageSharp.PixelFormats; + using ImageSharp.Tests.Drawing.Paths; + + using SixLabors.Fonts; + using SixLabors.Shapes; + + using Xunit; + + public class DrawText_Path : IDisposable + { + 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), new Vector2(20, 10), new Vector2(30, 10), })); + + private ProcessorWatchingImage img; + + private readonly FontCollection FontCollection; + + private readonly Font Font; + + public DrawText_Path() + { + this.FontCollection = new FontCollection(); + this.Font = this.FontCollection.Install(TestFontUtilities.GetPath("SixLaborsSampleAB.woff")); + this.img = new ProcessorWatchingImage(10, 10); + } + + public void Dispose() + { + this.img.Dispose(); + } + + [Fact] + public void FillsForEachACharachterWhenBrushSetAndNotPen() + { + this.img.DrawText( + "123", + this.Font, + Brushes.Solid(Rgba32.Red), + null, + path, + 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); + } + + [Fact] + public void FillsForEachACharachterWhenBrushSetAndNotPenDefaultOptions() + { + this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, path); + + Assert.NotEmpty(this.img.ProcessorApplications); + Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied + Assert.IsType>(this.img.ProcessorApplications[0].processor); + } + + [Fact] + public void FillsForEachACharachterWhenBrushSet() + { + this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), path, 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); + } + + [Fact] + public void FillsForEachACharachterWhenBrushSetDefaultOptions() + { + this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), path); + + Assert.NotEmpty(this.img.ProcessorApplications); + Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied + Assert.IsType>(this.img.ProcessorApplications[0].processor); + } + + [Fact] + public void FillsForEachACharachterWhenColorSet() + { + this.img.DrawText("123", this.Font, Rgba32.Red, path, new TextGraphicsOptions(true)); + + Assert.NotEmpty(this.img.ProcessorApplications); + Assert.Equal(3, this.img.ProcessorApplications.Count); + FillRegionProcessor processor = + Assert.IsType>(this.img.ProcessorApplications[0].processor); + + SolidBrush brush = Assert.IsType>(processor.Brush); + Assert.Equal(Rgba32.Red, brush.Color); + } + + [Fact] + public void FillsForEachACharachterWhenColorSetDefaultOptions() + { + this.img.DrawText("123", this.Font, Rgba32.Red, path); + + 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); + + SolidBrush brush = Assert.IsType>(processor.Brush); + Assert.Equal(Rgba32.Red, brush.Color); + } + + [Fact] + public void DrawForEachACharachterWhenPenSetAndNotBrush() + { + this.img.DrawText( + "123", + this.Font, + null, + Pens.Dash(Rgba32.Red, 1), + path, + 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); + } + + [Fact] + public void DrawForEachACharachterWhenPenSetAndNotBrushDefaultOptions() + { + this.img.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), path); + + Assert.NotEmpty(this.img.ProcessorApplications); + Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied + Assert.IsType>(this.img.ProcessorApplications[0].processor); + } + + [Fact] + public void DrawForEachACharachterWhenPenSet() + { + this.img.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), path, 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); + } + + [Fact] + public void DrawForEachACharachterWhenPenSetDefaultOptions() + { + this.img.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), path); + + Assert.NotEmpty(this.img.ProcessorApplications); + Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied + Assert.IsType>(this.img.ProcessorApplications[0].processor); + } + + [Fact] + public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSet() + { + this.img.DrawText( + "123", + this.Font, + Brushes.Solid(Rgba32.Red), + Pens.Dash(Rgba32.Red, 1), + path, + new TextGraphicsOptions(true)); + + Assert.NotEmpty(this.img.ProcessorApplications); + Assert.Equal(6, this.img.ProcessorApplications.Count); + } + + [Fact] + public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSetDefaultOptions() + { + this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), path); + + Assert.NotEmpty(this.img.ProcessorApplications); + Assert.Equal(6, this.img.ProcessorApplications.Count); + } + + [Fact] + public void BrushAppliesBeforPen() + { + this.img.DrawText( + "1", + this.Font, + Brushes.Solid(Rgba32.Red), + Pens.Dash(Rgba32.Red, 1), + path, + 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); + } + + [Fact] + public void BrushAppliesBeforPenDefaultOptions() + { + this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), path); + + 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); + } + + [Fact] + public void GlyphHeightChangesBasedOnuseImageResolutionFlag() + { + this.img.MetaData.VerticalResolution = 1; + this.img.MetaData.HorizontalResolution = 1; + this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), path, new TextGraphicsOptions(true) { + UseImageResolution = false + }); + + this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), path, 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); + + ShapeRegion ownRegion = Assert.IsType(ownResolution.Region); + ShapeRegion imgRegion = Assert.IsType(imgResolution.Region); + + // magic numbers based on the font used at well known resolutions + Assert.Equal(7.44, ownRegion.Shape.Bounds.Height, 2); + Assert.Equal(0.1, imgRegion.Shape.Bounds.Height, 2); + } + } +} diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs index 1516b33d43..4718ed4196 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs @@ -136,7 +136,7 @@ namespace ImageSharp.Tests.Drawing.Text 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] @@ -146,7 +146,7 @@ namespace ImageSharp.Tests.Drawing.Text 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] @@ -156,7 +156,7 @@ namespace ImageSharp.Tests.Drawing.Text 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] @@ -166,7 +166,7 @@ namespace ImageSharp.Tests.Drawing.Text 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] @@ -207,7 +207,7 @@ namespace ImageSharp.Tests.Drawing.Text 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[1].processor); } [Fact] @@ -218,7 +218,7 @@ namespace ImageSharp.Tests.Drawing.Text 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[1].processor); } [Fact] diff --git a/tests/ImageSharp.Tests/Drawing/Text/GlyphBuilder.cs b/tests/ImageSharp.Tests/Drawing/Text/GlyphBuilder.cs deleted file mode 100644 index 7f16f30bb8..0000000000 --- a/tests/ImageSharp.Tests/Drawing/Text/GlyphBuilder.cs +++ /dev/null @@ -1,68 +0,0 @@ - -namespace ImageSharp.Tests.Drawing.Text -{ - using ImageSharp.Drawing; - using SixLabors.Fonts; - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Threading.Tasks; - using Xunit; - - public class GlyphBuilderTests - { - [Fact] - public void OriginUsed() - { - // Y axis is inverted as it expects to be drawing for bottom left - GlyphBuilder fullBuilder = new GlyphBuilder(new System.Numerics.Vector2(10, 99)); - IGlyphRenderer builder = fullBuilder; - - builder.BeginGlyph(Vector2.Zero); - builder.BeginFigure(); - builder.MoveTo(new Vector2(0, 0)); - builder.LineTo(new Vector2(0, 10)); // becomes 0, -10 - - builder.CubicBezierTo( - new Vector2(15, 15), // control point - will not be in the final point collection - new Vector2(15, 10), // control point - will not be in the final point collection - new Vector2(10, 10));// becomes 10, -10 - - builder.QuadraticBezierTo( - new Vector2(10, 5), // control point - will not be in the final point collection - new Vector2(10, 0)); - - builder.EndFigure(); - builder.EndGlyph(); - - System.Collections.Immutable.ImmutableArray points = fullBuilder.Paths.Single().Flatten().Single().Points; - - Assert.Contains(new Vector2(10, 99), points); - Assert.Contains(new Vector2(10, 109), points); - Assert.Contains(new Vector2(20, 99), points); - Assert.Contains(new Vector2(20, 109), points); - } - - [Fact] - public void EachGlypeCausesNewPath() - { - // Y axis is inverted as it expects to be drawing for bottom left - GlyphBuilder fullBuilder = new GlyphBuilder(); - IGlyphRenderer builder = fullBuilder; - for (int i = 0; i < 10; i++) - { - builder.BeginGlyph(Vector2.Zero); - builder.BeginFigure(); - builder.MoveTo(new Vector2(0, 0)); - builder.LineTo(new Vector2(0, 10)); // becomes 0, -10 - builder.LineTo(new Vector2(10, 10));// becomes 10, -10 - builder.LineTo(new Vector2(10, 0)); - builder.EndFigure(); - builder.EndGlyph(); - } - - Assert.Equal(10, fullBuilder.Paths.Count()); - } - } -} diff --git a/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs b/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs index bb9cd264e7..21646b03f3 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/OutputText.cs @@ -5,7 +5,7 @@ namespace ImageSharp.Tests.Drawing.Text using System.IO; using ImageSharp; using ImageSharp.Drawing.Brushes; - using Processing; + using ImageSharp.Processing; using System.Collections.Generic; using Xunit; using ImageSharp.Drawing; diff --git a/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs b/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs index a291a7d18b..1a26bcfedc 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/TextGraphicsOptionsTests.cs @@ -22,7 +22,7 @@ namespace ImageSharp.Tests.Drawing.Text TextGraphicsOptions textOptions = opt; - Assert.Equal(false, textOptions.Antialias); + Assert.False(textOptions.Antialias); Assert.Equal(99, textOptions.AntialiasSubpixelDepth); } @@ -36,7 +36,7 @@ namespace ImageSharp.Tests.Drawing.Text GraphicsOptions opt = (GraphicsOptions)textOptions; - Assert.Equal(false, opt.Antialias); + Assert.False(opt.Antialias); Assert.Equal(99, opt.AntialiasSubpixelDepth); } } diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index 12c7d51541..51a1562f53 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/tests/ImageSharp.Tests/FileTestBase.cs @@ -12,6 +12,54 @@ namespace ImageSharp.Tests /// public abstract class FileTestBase : TestBase { + /// + /// A collection made up of one file for each image format + /// + public static IEnumerable DefaultFiles = + new[] + { + TestImages.Bmp.Car, + TestImages.Jpeg.Baseline.Calliphora, + TestImages.Png.Splash, + TestImages.Gif.Trans + }; + + /// + /// A collection of all the bmp test images + /// + public static IEnumerable AllBmpFiles = TestImages.Bmp.All; + + /// + /// A collection of all the jpeg test images + /// + public static IEnumerable AllJpegFiles = TestImages.Jpeg.All; + + /// + /// A collection of all the png test images + /// + public static IEnumerable AllPngFiles = TestImages.Png.All; + + /// + /// A collection of all the gif test images + /// + public static IEnumerable AllGifFiles = TestImages.Gif.All; + + /// + /// The standard pixel format enumeration + /// + public const PixelTypes DefaultPixelType = PixelTypes.Rgba32; + + public static class Extensions + { + public const string Bmp = "bmp"; + + public const string Jpeg = "jpg"; + + public const string Png = "png"; + + public const string Gif = "gif"; + } + /// /// The collection of image files to test against. /// diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index b47df8395b..ec1a8c4659 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -13,22 +13,16 @@ namespace ImageSharp.Tests public class GeneralFormatTests : FileTestBase { - [Fact] - public void ResolutionShouldChange() + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ResolutionShouldChange(TestImageProvider provider) + where TPixel : struct, IPixel { - string path = this.CreateOutputDirectory("Resolution"); - - foreach (TestFile file in Files) + using (Image image = provider.GetImage()) { - using (Image image = file.CreateImage()) - { - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) - { - image.MetaData.VerticalResolution = 150; - image.MetaData.HorizontalResolution = 150; - image.Save(output); - } - } + image.MetaData.VerticalResolution = 150; + image.MetaData.HorizontalResolution = 150; + image.DebugSave(provider, null, Extensions.Bmp); } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index a5fc92901e..89df2d086e 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -14,7 +14,7 @@ namespace ImageSharp.Tests public class GifDecoderTests { - private const PixelTypes PixelTypes = Tests.PixelTypes.StandardImageClass | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; + private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; public static readonly string[] TestFiles = { TestImages.Gif.Giphy, TestImages.Gif.Rings, TestImages.Gif.Trans }; diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index b0ffaaf859..70cc8e2ba6 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Tests public class GifEncoderTests { - private const PixelTypes PixelTypes = Tests.PixelTypes.StandardImageClass | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; + private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; [Theory] [WithTestPatternImages(100, 100, PixelTypes)] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index a6d6d31f3b..446c5e96ae 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -26,7 +26,7 @@ namespace ImageSharp.Tests public static string[] ProgressiveTestJpegs = TestImages.Jpeg.Progressive.All; [Theory] - [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32 | PixelTypes.StandardImageClass | PixelTypes.Argb32)] + [WithFileCollection(nameof(BaselineTestJpegs), PixelTypes.Rgba32 | PixelTypes.Rgba32 | PixelTypes.Argb32)] public void OpenBaselineJpeg_SaveBmp(TestImageProvider provider) where TPixel : struct, IPixel { @@ -37,7 +37,7 @@ namespace ImageSharp.Tests } [Theory] - [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32 | PixelTypes.StandardImageClass | PixelTypes.Argb32)] + [WithFileCollection(nameof(ProgressiveTestJpegs), PixelTypes.Rgba32 | PixelTypes.Rgba32 | PixelTypes.Argb32)] public void OpenProgressiveJpeg_SaveBmp(TestImageProvider provider) where TPixel : struct, IPixel { @@ -48,11 +48,11 @@ namespace ImageSharp.Tests } [Theory] - [WithSolidFilledImages(16, 16, 255, 0, 0, PixelTypes.StandardImageClass, JpegSubsample.Ratio420, 75)] - [WithSolidFilledImages(16, 16, 255, 0, 0, PixelTypes.StandardImageClass, JpegSubsample.Ratio420, 100)] - [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)] + [WithSolidFilledImages(16, 16, 255, 0, 0, PixelTypes.Rgba32, JpegSubsample.Ratio420, 75)] + [WithSolidFilledImages(16, 16, 255, 0, 0, PixelTypes.Rgba32, JpegSubsample.Ratio420, 100)] + [WithSolidFilledImages(16, 16, 255, 0, 0, PixelTypes.Rgba32, JpegSubsample.Ratio444, 75)] + [WithSolidFilledImages(16, 16, 255, 0, 0, PixelTypes.Rgba32, JpegSubsample.Ratio444, 100)] + [WithSolidFilledImages(8, 8, 255, 0, 0, PixelTypes.Rgba32, JpegSubsample.Ratio444, 100)] public void DecodeGenerated_SaveBmp( TestImageProvider provider, JpegSubsample subsample, @@ -79,7 +79,7 @@ namespace ImageSharp.Tests } [Theory] - [WithSolidFilledImages(42, 88, 255, 0, 0, PixelTypes.StandardImageClass)] + [WithSolidFilledImages(42, 88, 255, 0, 0, PixelTypes.Rgba32)] public void DecodeGenerated_MetadataOnly( TestImageProvider provider) where TPixel : struct, IPixel diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 1b4f3ea785..ab81c69b42 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -28,10 +28,10 @@ namespace ImageSharp.Tests } [Theory] - [WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.StandardImageClass, 75, JpegSubsample.Ratio420)] - [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)] + [WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio420)] + [WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio420)] + [WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio444)] + [WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio444)] public void LoadResizeSave(TestImageProvider provider, int quality, JpegSubsample subsample) where TPixel : struct, IPixel { @@ -49,8 +49,8 @@ namespace ImageSharp.Tests } [Theory] - [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32 | PixelTypes.StandardImageClass | PixelTypes.Argb32, JpegSubsample.Ratio420, 75)] - [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32 | PixelTypes.StandardImageClass | PixelTypes.Argb32, JpegSubsample.Ratio444, 75)] + [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32 | PixelTypes.Rgba32 | PixelTypes.Argb32, JpegSubsample.Ratio420, 75)] + [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32 | PixelTypes.Rgba32 | PixelTypes.Argb32, JpegSubsample.Ratio444, 75)] public void OpenBmp_SaveJpeg(TestImageProvider provider, JpegSubsample subSample, int quality) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs index b41826e2f6..f3412e45e2 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs @@ -34,7 +34,7 @@ namespace ImageSharp.Tests }; // [Theory] // Benchmark, enable manually - [MemberData(nameof(DecodeJpegData))] + // [MemberData(nameof(DecodeJpegData))] public void DecodeJpeg(string fileName) { const int ExecutionCount = 30; @@ -60,10 +60,10 @@ namespace ImageSharp.Tests // Benchmark, enable manually! // [Theory] - [InlineData(1, 75, JpegSubsample.Ratio420)] - [InlineData(30, 75, JpegSubsample.Ratio420)] - [InlineData(30, 75, JpegSubsample.Ratio444)] - [InlineData(30, 100, JpegSubsample.Ratio444)] + // [InlineData(1, 75, JpegSubsample.Ratio420)] + // [InlineData(30, 75, JpegSubsample.Ratio420)] + // [InlineData(30, 75, JpegSubsample.Ratio444)] + // [InlineData(30, 100, JpegSubsample.Ratio444)] public void EncodeJpeg(int executionCount, int quality, JpegSubsample subsample) { string[] testFiles = TestImages.Bmp.All @@ -72,7 +72,7 @@ namespace ImageSharp.Tests Image[] testImages = testFiles.Select( - tf => TestImageProvider.File(tf, pixelTypeOverride: PixelTypes.StandardImageClass).GetImage()) + tf => TestImageProvider.File(tf, pixelTypeOverride: PixelTypes.Rgba32).GetImage()) .ToArray(); using (MemoryStream ms = new MemoryStream()) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs index f242faf12c..f681e1d8f9 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegUtilsTests.cs @@ -40,7 +40,7 @@ namespace ImageSharp.Tests } [Theory] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Rgba32| PixelTypes.StandardImageClass | PixelTypes.Argb32)] + [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Rgba32| PixelTypes.Rgba32 | PixelTypes.Argb32)] public void CopyStretchedRGBTo_FromOrigo(TestImageProvider provider) where TPixel : struct, IPixel { @@ -62,7 +62,7 @@ namespace ImageSharp.Tests } [Theory] - [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Rgba32| PixelTypes.StandardImageClass | PixelTypes.Argb32)] + [WithMemberFactory(nameof(CreateTestImage), PixelTypes.Rgba32| PixelTypes.Rgba32 | PixelTypes.Argb32)] public void CopyStretchedRGBTo_WithOffset(TestImageProvider provider) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/YCbCrImageTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/YCbCrImageTests.cs index ba55665ca2..b7cd281cd3 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/YCbCrImageTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/YCbCrImageTests.cs @@ -19,11 +19,6 @@ namespace ImageSharp.Tests private ITestOutputHelper Output { get; } - private void PrintChannel(string name, JpegPixelArea channel) - { - this.Output.WriteLine($"{name}: Stride={channel.Stride}"); - } - [Theory] [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio410, 4, 2)] [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio411, 4, 1)] @@ -31,7 +26,10 @@ namespace ImageSharp.Tests [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio422, 2, 1)] [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio440, 1, 2)] [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio444, 1, 1)] - public void CalculateChrominanceSize(int ratioValue, int expectedDivX, int expectedDivY) + internal void CalculateChrominanceSize( + YCbCrImage.YCbCrSubsampleRatio ratioValue, + int expectedDivX, + int expectedDivY) { YCbCrImage.YCbCrSubsampleRatio ratio = (YCbCrImage.YCbCrSubsampleRatio)ratioValue; @@ -39,7 +37,7 @@ namespace ImageSharp.Tests Size size = YCbCrImage.CalculateChrominanceSize(400, 400, ratio); //this.Output.WriteLine($"Ch Size: {size}"); - Assert.Equal(new Size(400/expectedDivX, 400/expectedDivY), size); + Assert.Equal(new Size(400 / expectedDivX, 400 / expectedDivY), size); } [Theory] @@ -49,7 +47,7 @@ namespace ImageSharp.Tests [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio422, 2)] [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio440, 1)] [InlineData(YCbCrImage.YCbCrSubsampleRatio.YCbCrSubsampleRatio444, 1)] - public void Create(int ratioValue, int expectedCStrideDiv) + internal void Create(YCbCrImage.YCbCrSubsampleRatio ratioValue, int expectedCStrideDiv) { YCbCrImage.YCbCrSubsampleRatio ratio = (YCbCrImage.YCbCrSubsampleRatio)ratioValue; @@ -61,10 +59,14 @@ namespace ImageSharp.Tests //this.PrintChannel("Cb", img.CbChannel); //this.PrintChannel("Cr", img.CrChannel); - Assert.Equal(img.YChannel.Width, 400); + Assert.Equal(400, img.YChannel.Width); Assert.Equal(img.CbChannel.Width, 400 / expectedCStrideDiv); Assert.Equal(img.CrChannel.Width, 400 / expectedCStrideDiv); } + private void PrintChannel(string name, JpegPixelArea channel) + { + this.Output.WriteLine($"{name}: Stride={channel.Stride}"); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index cf5c1cfa9b..195a8dcb3e 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -13,12 +13,12 @@ namespace ImageSharp.Tests public class PngDecoderTests { - private const PixelTypes PixelTypes = Tests.PixelTypes.StandardImageClass | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; + private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | 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 + TestImages.Png.Bad.ChunkLength1, TestImages.Png.Bad.ChunkLength2 }; [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 195eaba105..02edf7688f 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -18,7 +18,7 @@ namespace ImageSharp.Tests public class PngEncoderTests : FileTestBase { - private const PixelTypes PixelTypes = Tests.PixelTypes.StandardImageClass | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; + private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; [Theory] [WithTestPatternImages(100, 100, PixelTypes, PngColorType.RgbWithAlpha)] diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index 22bb0b2447..90f994f366 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -20,7 +20,7 @@ namespace ImageSharp.Tests.Formats.Png public class PngSmokeTests { [Theory] - [WithTestPatternImages(300, 300, PixelTypes.StandardImageClass)] + [WithTestPatternImages(300, 300, PixelTypes.Rgba32)] public void GeneralTest(TestImageProvider provider) where TPixel : struct, IPixel { @@ -41,7 +41,7 @@ namespace ImageSharp.Tests.Formats.Png } [Theory] - [WithTestPatternImages(100, 100, PixelTypes.StandardImageClass)] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] public void CanSaveIndexedPng(TestImageProvider provider) where TPixel : struct, IPixel { @@ -105,7 +105,7 @@ namespace ImageSharp.Tests.Formats.Png //} [Theory] - [WithTestPatternImages(300, 300, PixelTypes.StandardImageClass)] + [WithTestPatternImages(300, 300, PixelTypes.Rgba32)] public void Resize(TestImageProvider provider) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/IO/BigEndianBitConverter.ToTypeTests.cs b/tests/ImageSharp.Tests/IO/BigEndianBitConverter.ToTypeTests.cs index 50a86d3cc0..a69727d457 100644 --- a/tests/ImageSharp.Tests/IO/BigEndianBitConverter.ToTypeTests.cs +++ b/tests/ImageSharp.Tests/IO/BigEndianBitConverter.ToTypeTests.cs @@ -56,13 +56,13 @@ namespace ImageSharp.Tests.IO [Fact] public void ToBoolean() { - Assert.Equal(false, EndianBitConverter.BigEndianConverter.ToBoolean(new byte[] { 0 }, 0)); - Assert.Equal(true, EndianBitConverter.BigEndianConverter.ToBoolean(new byte[] { 1 }, 0)); - Assert.Equal(true, EndianBitConverter.BigEndianConverter.ToBoolean(new byte[] { 42 }, 0)); + Assert.False(EndianBitConverter.BigEndianConverter.ToBoolean(new byte[] { 0 }, 0)); + Assert.True(EndianBitConverter.BigEndianConverter.ToBoolean(new byte[] { 1 }, 0)); + Assert.True(EndianBitConverter.BigEndianConverter.ToBoolean(new byte[] { 42 }, 0)); - Assert.Equal(false, EndianBitConverter.BigEndianConverter.ToBoolean(new byte[] { 1, 0 }, 1)); - Assert.Equal(true, EndianBitConverter.BigEndianConverter.ToBoolean(new byte[] { 0, 1 }, 1)); - Assert.Equal(true, EndianBitConverter.BigEndianConverter.ToBoolean(new byte[] { 0, 42 }, 1)); + Assert.False(EndianBitConverter.BigEndianConverter.ToBoolean(new byte[] { 1, 0 }, 1)); + Assert.True(EndianBitConverter.BigEndianConverter.ToBoolean(new byte[] { 0, 1 }, 1)); + Assert.True(EndianBitConverter.BigEndianConverter.ToBoolean(new byte[] { 0, 42 }, 1)); } /// diff --git a/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.ToTypeTests.cs b/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.ToTypeTests.cs index fa8b2a1a25..c46c011a14 100644 --- a/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.ToTypeTests.cs +++ b/tests/ImageSharp.Tests/IO/LittleEndianBitConverter.ToTypeTests.cs @@ -56,11 +56,11 @@ namespace ImageSharp.Tests.IO [Fact] public void ToBoolean() { - Assert.Equal(false, EndianBitConverter.LittleEndianConverter.ToBoolean(new byte[] { 0 }, 0)); - Assert.Equal(true, EndianBitConverter.LittleEndianConverter.ToBoolean(new byte[] { 1 }, 0)); + Assert.False(EndianBitConverter.LittleEndianConverter.ToBoolean(new byte[] { 0 }, 0)); + Assert.True(EndianBitConverter.LittleEndianConverter.ToBoolean(new byte[] { 1 }, 0)); - Assert.Equal(false, EndianBitConverter.LittleEndianConverter.ToBoolean(new byte[] { 1, 0 }, 1)); - Assert.Equal(true, EndianBitConverter.LittleEndianConverter.ToBoolean(new byte[] { 0, 1 }, 1)); + Assert.False(EndianBitConverter.LittleEndianConverter.ToBoolean(new byte[] { 1, 0 }, 1)); + Assert.True(EndianBitConverter.LittleEndianConverter.ToBoolean(new byte[] { 0, 1 }, 1)); } /// diff --git a/tests/ImageSharp.Tests/ImageComparer.cs b/tests/ImageSharp.Tests/ImageComparer.cs index d339dc83d2..9a30cc3633 100644 --- a/tests/ImageSharp.Tests/ImageComparer.cs +++ b/tests/ImageSharp.Tests/ImageComparer.cs @@ -1,4 +1,9 @@ -namespace ImageSharp.Tests +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests { using System; using ImageSharp; @@ -12,9 +17,41 @@ /// public static class ImageComparer { - const int DefaultScalingFactor = 32; // this is means the images get scaled into a 32x32 image to sample pixels - const int DefaultSegmentThreshold = 3; // the greyscale difference between 2 segements my be > 3 before it influances the overall difference - const float DefaultImageThreshold = 0.000f; // after segment threasholds the images must have no differences + const int DefaultScalingFactor = 32; // This is means the images get scaled into a 32x32 image to sample pixels + const int DefaultSegmentThreshold = 3; // The greyscale difference between 2 segements my be > 3 before it influences the overall difference + const float DefaultImageThreshold = 0.000F; // After segment thresholds the images must have no differences + + /// + /// Fills the bounded area with a solid color and does a visual comparison between 2 images asserting the difference outwith + /// that area is less then a configurable threshold. + /// + /// The color of the expected image + /// The color type fo the the actual image + /// The expected image + /// The actual image + /// The bounds within the image has been altered + /// + /// The threshold for the percentage difference where the images are asumed to be the same. + /// The default/undefined value is + /// + /// + /// The threshold of the individual segments before it acumulates towards the overall difference. + /// The default undefined value is + /// + /// + /// This is a sampling factor we sample a grid of average pixels width by high + /// The default undefined value is + /// + public static void EnsureProcessorChangesAreConstrained(Image expected, Image actual, Rectangle bounds, float imageTheshold = DefaultImageThreshold, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) + where TPixelA : struct, IPixel + where TPixelB : struct, IPixel + { + // Draw identical shapes over the bounded and compare to ensure changes are constrained. + expected.Fill(NamedColors.HotPink, bounds); + actual.Fill(NamedColors.HotPink, bounds); + + CheckSimilarity(expected, actual, imageTheshold, segmentThreshold, scalingFactor); + } /// /// Does a visual comparison between 2 images and then asserts the difference is less then a configurable threshold @@ -25,15 +62,15 @@ /// The actual image /// /// The threshold for the percentage difference where the images are asumed to be the same. - /// The default/undefined value is + /// The default/undefined value is /// /// - /// The threashold of the individual segments before it acumulates towards the overall difference. - /// The default undefined value is + /// The threshold of the individual segments before it acumulates towards the overall difference. + /// The default undefined value is /// /// /// This is a sampling factor we sample a grid of average pixels width by high - /// The default undefined value is + /// The default undefined value is /// public static void CheckSimilarity(Image expected, Image actual, float imageTheshold = DefaultImageThreshold, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) where TPixelA : struct, IPixel @@ -52,14 +89,14 @@ /// The source image /// The target image /// - /// The threashold of the individual segments before it acumulates towards the overall difference. - /// The default undefined value is + /// The threshold of the individual segments before it acumulates towards the overall difference. + /// The default undefined value is /// /// /// This is a sampling factor we sample a grid of average pixels width by high /// The default undefined value is /// - /// Returns a number from 0 - 1 which represents the diference focter between the images. + /// Returns a number from 0 - 1 which represents the difference focter between the images. public static float PercentageDifference(this Image source, Image target, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) where TPixelA : struct, IPixel where TPixelB : struct, IPixel @@ -74,14 +111,14 @@ if (b > segmentThreshold) { diffPixels++; } } - return (float)diffPixels / (float)(scalingFactor * scalingFactor); + return diffPixels / (float)(scalingFactor * scalingFactor); } private static Fast2DArray GetDifferences(Image source, Image target, int scalingFactor) where TPixelA : struct, IPixel where TPixelB : struct, IPixel { - Fast2DArray differences = new Fast2DArray(scalingFactor, scalingFactor); + var differences = new Fast2DArray(scalingFactor, scalingFactor); Fast2DArray firstGray = source.GetGrayScaleValues(scalingFactor); Fast2DArray secondGray = target.GetGrayScaleValues(scalingFactor); @@ -89,7 +126,7 @@ { for (int x = 0; x < scalingFactor; x++) { - var diff = firstGray[x, y] - secondGray[x, y]; + int diff = firstGray[x, y] - secondGray[x, y]; differences[x, y] = (byte)Math.Abs(diff); } } @@ -100,18 +137,18 @@ private static Fast2DArray GetGrayScaleValues(this Image source, int scalingFactor) where TPixelA : struct, IPixel { - byte[] buffer = new byte[4]; + byte[] buffer = new byte[3]; using (Image img = new Image(source).Resize(scalingFactor, scalingFactor).Grayscale()) { using (PixelAccessor pixels = img.Lock()) { - Fast2DArray grayScale = new Fast2DArray(scalingFactor, scalingFactor); + var grayScale = new Fast2DArray(scalingFactor, scalingFactor); for (int y = 0; y < scalingFactor; y++) { for (int x = 0; x < scalingFactor; x++) { pixels[x, y].ToXyzBytes(buffer, 0); - grayScale[x, y] = buffer[1]; + grayScale[x, y] = buffer[0]; } } diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 3ec2210c57..957ac12fea 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -9,13 +9,14 @@ + - + diff --git a/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs b/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs index 8b4c6ea103..3b224014da 100644 --- a/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs +++ b/tests/ImageSharp.Tests/MetaData/ImagePropertyTests.cs @@ -25,7 +25,7 @@ namespace ImageSharp.Tests Assert.Equal(property1, property2); Assert.True(property1 == property2); - Assert.Equal(property3, null); + Assert.Null(property3); } /// @@ -41,7 +41,7 @@ namespace ImageSharp.Tests Assert.False(property1.Equals("Foo")); - Assert.NotEqual(property1, null); + Assert.NotNull(property1); Assert.NotEqual(property1, property2); Assert.True(property1 != property2); @@ -69,7 +69,7 @@ namespace ImageSharp.Tests { ImageProperty property = new ImageProperty("Foo", null); Assert.Equal("Foo", property.Name); - Assert.Equal(null, property.Value); + Assert.Null(property.Value); property = new ImageProperty("Foo", string.Empty); Assert.Equal(string.Empty, property.Value); diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs index a91eb310d8..473af77121 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs @@ -44,7 +44,7 @@ namespace ImageSharp.Tests Assert.Equal(ExifDataType.Ascii, value.DataType); Assert.Equal(ExifTag.GPSDOP, value.Tag); - Assert.Equal(false, value.IsArray); + Assert.False(value.IsArray); Assert.Equal("Windows Photo Editor 10.0.10011.16384", value.ToString()); Assert.Equal("Windows Photo Editor 10.0.10011.16384", value.Value); } diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs index 9a2455f0e8..6f003cc4d6 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs @@ -10,10 +10,12 @@ namespace ImageSharp.Tests.Icc public class IccDataReaderTagDataEntryTests { [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.UnknownTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.UnknownTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadUnknownTagDataEntry(byte[] data, IccUnknownTagDataEntry expected, uint size) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccUnknownTagDataEntry output = reader.ReadUnknownTagDataEntry(size); @@ -21,10 +23,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.ChromaticityTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.ChromaticityTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadChromaticityTagDataEntry(byte[] data, IccChromaticityTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccChromaticityTagDataEntry output = reader.ReadChromaticityTagDataEntry(); @@ -32,10 +36,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.ColorantOrderTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.ColorantOrderTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadColorantOrderTagDataEntry(byte[] data, IccColorantOrderTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccColorantOrderTagDataEntry output = reader.ReadColorantOrderTagDataEntry(); @@ -43,10 +49,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.ColorantTableTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.ColorantTableTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadColorantTableTagDataEntry(byte[] data, IccColorantTableTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccColorantTableTagDataEntry output = reader.ReadColorantTableTagDataEntry(); @@ -54,10 +62,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.CurveTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.CurveTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadCurveTagDataEntry(byte[] data, IccCurveTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccCurveTagDataEntry output = reader.ReadCurveTagDataEntry(); @@ -65,10 +75,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.DataTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.DataTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadDataTagDataEntry(byte[] data, IccDataTagDataEntry expected, uint size) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccDataTagDataEntry output = reader.ReadDataTagDataEntry(size); @@ -76,10 +88,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.DateTimeTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.DateTimeTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadDateTimeTagDataEntry(byte[] data, IccDateTimeTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccDateTimeTagDataEntry output = reader.ReadDateTimeTagDataEntry(); @@ -87,10 +101,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.Lut16TagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.Lut16TagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadLut16TagDataEntry(byte[] data, IccLut16TagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccLut16TagDataEntry output = reader.ReadLut16TagDataEntry(); @@ -98,10 +114,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.Lut8TagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.Lut8TagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadLut8TagDataEntry(byte[] data, IccLut8TagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccLut8TagDataEntry output = reader.ReadLut8TagDataEntry(); @@ -109,10 +127,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.LutAToBTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.LutAToBTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadLutAToBTagDataEntry(byte[] data, IccLutAToBTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccLutAToBTagDataEntry output = reader.ReadLutAtoBTagDataEntry(); @@ -120,10 +140,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.LutBToATagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.LutBToATagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadLutBToATagDataEntry(byte[] data, IccLutBToATagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccLutBToATagDataEntry output = reader.ReadLutBtoATagDataEntry(); @@ -131,10 +153,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.MeasurementTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.MeasurementTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadMeasurementTagDataEntry(byte[] data, IccMeasurementTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccMeasurementTagDataEntry output = reader.ReadMeasurementTagDataEntry(); @@ -142,10 +166,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.MultiLocalizedUnicodeTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.MultiLocalizedUnicodeTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadMultiLocalizedUnicodeTagDataEntry(byte[] data, IccMultiLocalizedUnicodeTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccMultiLocalizedUnicodeTagDataEntry output = reader.ReadMultiLocalizedUnicodeTagDataEntry(); @@ -153,10 +179,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.MultiProcessElementsTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.MultiProcessElementsTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadMultiProcessElementsTagDataEntry(byte[] data, IccMultiProcessElementsTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccMultiProcessElementsTagDataEntry output = reader.ReadMultiProcessElementsTagDataEntry(); @@ -164,10 +192,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.NamedColor2TagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.NamedColor2TagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadNamedColor2TagDataEntry(byte[] data, IccNamedColor2TagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccNamedColor2TagDataEntry output = reader.ReadNamedColor2TagDataEntry(); @@ -175,10 +205,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.ParametricCurveTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.ParametricCurveTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadParametricCurveTagDataEntry(byte[] data, IccParametricCurveTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccParametricCurveTagDataEntry output = reader.ReadParametricCurveTagDataEntry(); @@ -186,10 +218,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.ProfileSequenceDescTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.ProfileSequenceDescTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadProfileSequenceDescTagDataEntry(byte[] data, IccProfileSequenceDescTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccProfileSequenceDescTagDataEntry output = reader.ReadProfileSequenceDescTagDataEntry(); @@ -197,10 +231,14 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.ProfileSequenceIdentifierTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] - internal void ReadProfileSequenceIdentifierTagDataEntry(byte[] data, IccProfileSequenceIdentifierTagDataEntry expected) + [MemberData( + nameof(IccTestDataTagDataEntry.ProfileSequenceIdentifierTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] + internal void ReadProfileSequenceIdentifierTagDataEntry( + byte[] data, + IccProfileSequenceIdentifierTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccProfileSequenceIdentifierTagDataEntry output = reader.ReadProfileSequenceIdentifierTagDataEntry(); @@ -208,10 +246,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.ResponseCurveSet16TagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.ResponseCurveSet16TagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadResponseCurveSet16TagDataEntry(byte[] data, IccResponseCurveSet16TagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccResponseCurveSet16TagDataEntry output = reader.ReadResponseCurveSet16TagDataEntry(); @@ -219,10 +259,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.Fix16ArrayTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.Fix16ArrayTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadFix16ArrayTagDataEntry(byte[] data, IccFix16ArrayTagDataEntry expected, uint size) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccFix16ArrayTagDataEntry output = reader.ReadFix16ArrayTagDataEntry(size); @@ -230,10 +272,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.SignatureTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.SignatureTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadSignatureTagDataEntry(byte[] data, IccSignatureTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccSignatureTagDataEntry output = reader.ReadSignatureTagDataEntry(); @@ -241,10 +285,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.TextTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.TextTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadTextTagDataEntry(byte[] data, IccTextTagDataEntry expected, uint size) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccTextTagDataEntry output = reader.ReadTextTagDataEntry(size); @@ -252,10 +298,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.UFix16ArrayTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.UFix16ArrayTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadUFix16ArrayTagDataEntry(byte[] data, IccUFix16ArrayTagDataEntry expected, uint size) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccUFix16ArrayTagDataEntry output = reader.ReadUFix16ArrayTagDataEntry(size); @@ -263,10 +311,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.UInt16ArrayTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.UInt16ArrayTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadUInt16ArrayTagDataEntry(byte[] data, IccUInt16ArrayTagDataEntry expected, uint size) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccUInt16ArrayTagDataEntry output = reader.ReadUInt16ArrayTagDataEntry(size); @@ -274,10 +324,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.UInt32ArrayTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.UInt32ArrayTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadUInt32ArrayTagDataEntry(byte[] data, IccUInt32ArrayTagDataEntry expected, uint size) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccUInt32ArrayTagDataEntry output = reader.ReadUInt32ArrayTagDataEntry(size); @@ -285,10 +337,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.UInt64ArrayTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.UInt64ArrayTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadUInt64ArrayTagDataEntry(byte[] data, IccUInt64ArrayTagDataEntry expected, uint size) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccUInt64ArrayTagDataEntry output = reader.ReadUInt64ArrayTagDataEntry(size); @@ -296,10 +350,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.UInt8ArrayTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.UInt8ArrayTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadUInt8ArrayTagDataEntry(byte[] data, IccUInt8ArrayTagDataEntry expected, uint size) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccUInt8ArrayTagDataEntry output = reader.ReadUInt8ArrayTagDataEntry(size); @@ -307,10 +363,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.ViewingConditionsTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.ViewingConditionsTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadViewingConditionsTagDataEntry(byte[] data, IccViewingConditionsTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccViewingConditionsTagDataEntry output = reader.ReadViewingConditionsTagDataEntry(); @@ -318,10 +376,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.XYZTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.XYZTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadXyzTagDataEntry(byte[] data, IccXyzTagDataEntry expected, uint size) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccXyzTagDataEntry output = reader.ReadXyzTagDataEntry(size); @@ -329,10 +389,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.TextDescriptionTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.TextDescriptionTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadTextDescriptionTagDataEntry(byte[] data, IccTextDescriptionTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccTextDescriptionTagDataEntry output = reader.ReadTextDescriptionTagDataEntry(); @@ -340,10 +402,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.CrdInfoTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.CrdInfoTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadCrdInfoTagDataEntry(byte[] data, IccCrdInfoTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccCrdInfoTagDataEntry output = reader.ReadCrdInfoTagDataEntry(); @@ -351,10 +415,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.ScreeningTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.ScreeningTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadScreeningTagDataEntry(byte[] data, IccScreeningTagDataEntry expected) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccScreeningTagDataEntry output = reader.ReadScreeningTagDataEntry(); @@ -362,10 +428,12 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.UcrBgTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData( + nameof(IccTestDataTagDataEntry.UcrBgTagDataEntryTestData), + MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadUcrBgTagDataEntry(byte[] data, IccUcrBgTagDataEntry expected, uint size) { - IccDataReader reader = CreateReader(data); + IccDataReader reader = this.CreateReader(data); IccUcrBgTagDataEntry output = reader.ReadUcrBgTagDataEntry(size); diff --git a/tests/ImageSharp.Tests/Numerics/PointFTests.cs b/tests/ImageSharp.Tests/Numerics/PointFTests.cs new file mode 100644 index 0000000000..3b5a72a14c --- /dev/null +++ b/tests/ImageSharp.Tests/Numerics/PointFTests.cs @@ -0,0 +1,192 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Numerics +{ + using System; + using System.Globalization; + using System.Numerics; + using System.Reflection; + using Xunit; + + public class PointFTests + { + [Fact] + public void DefaultConstructorTest() + { + Assert.Equal(PointF.Empty, new PointF()); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(float.MinValue, float.MaxValue)] + [InlineData(0.0, 0.0)] + public void NonDefaultConstructorTest(float x, float y) + { + var p1 = new PointF(x, y); + + Assert.Equal(x, p1.X); + Assert.Equal(y, p1.Y); + } + + [Fact] + public void IsEmptyDefaultsTest() + { + Assert.True(PointF.Empty.IsEmpty); + Assert.True(new PointF().IsEmpty); + Assert.True(new PointF(0, 0).IsEmpty); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + public void IsEmptyRandomTest(float x, float y) + { + Assert.False(new PointF(x, y).IsEmpty); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void CoordinatesTest(float x, float y) + { + var p = new PointF(x, y); + Assert.Equal(x, p.X); + Assert.Equal(y, p.Y); + + p.X = 10; + Assert.Equal(10, p.X); + + p.Y = -10.123f; + Assert.Equal(-10.123, p.Y, 3); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue, int.MaxValue, int.MinValue)] + [InlineData(float.MinValue, float.MaxValue, int.MinValue, int.MaxValue)] + [InlineData(0, 0, 0, 0)] + public void ArithmeticTestWithSize(float x, float y, int x1, int y1) + { + var p = new PointF(x, y); + var s = new Size(x1, y1); + + var addExpected = new PointF(x + x1, y + y1); + var subExpected = new PointF(x - x1, y - y1); + Assert.Equal(addExpected, p + s); + Assert.Equal(subExpected, p - s); + Assert.Equal(addExpected, PointF.Add(p, s)); + Assert.Equal(subExpected, PointF.Subtract(p, s)); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MaxValue)] + [InlineData(0, 0)] + public void ArithmeticTestWithSizeF(float x, float y) + { + var p = new PointF(x, y); + var s = new SizeF(y, x); + + var addExpected = new PointF(x + y, y + x); + var subExpected = new PointF(x - y, y - x); + Assert.Equal(addExpected, p + s); + Assert.Equal(subExpected, p - s); + Assert.Equal(addExpected, PointF.Add(p, s)); + Assert.Equal(subExpected, PointF.Subtract(p, s)); + } + + [Fact] + public void RotateTest() + { + var p = new PointF(13, 17); + Matrix3x2 matrix = Matrix3x2Extensions.CreateRotation(45, PointF.Empty); + + var pout = PointF.Rotate(p, matrix); + + Assert.Equal(new PointF(-2.82842732F, 21.2132034F), pout); + } + + [Fact] + public void SkewTest() + { + var p = new PointF(13, 17); + Matrix3x2 matrix = Matrix3x2Extensions.CreateSkew(45, 45, PointF.Empty); + + var pout = PointF.Skew(p, matrix); + Assert.Equal(new PointF(30, 30), pout); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MaxValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void EqualityTest(float x, float y) + { + var pLeft = new PointF(x, y); + var pRight = new PointF(y, x); + + if (x == y) + { + Assert.True(pLeft == pRight); + Assert.False(pLeft != pRight); + Assert.True(pLeft.Equals(pRight)); + Assert.True(pLeft.Equals((object)pRight)); + Assert.Equal(pLeft.GetHashCode(), pRight.GetHashCode()); + return; + } + + Assert.True(pLeft != pRight); + Assert.False(pLeft == pRight); + Assert.False(pLeft.Equals(pRight)); + Assert.False(pLeft.Equals((object)pRight)); + } + + [Fact] + public static void EqualityTest_NotPointF() + { + var point = new PointF(0, 0); + Assert.False(point.Equals(null)); + Assert.False(point.Equals(0)); + + // If PointF implements IEquatable (e.g. in .NET Core), then structs that are implicitly + // convertible to var can potentially be equal. + // See https://github.com/dotnet/corefx/issues/5255. + bool expectsImplicitCastToPointF = typeof(IEquatable).IsAssignableFrom(point.GetType()); + Assert.Equal(expectsImplicitCastToPointF, point.Equals(new Point(0, 0))); + + Assert.False(point.Equals((object)new Point(0, 0))); // No implicit cast + } + + [Fact] + public static void GetHashCodeTest() + { + var point = new PointF(10, 10); + Assert.Equal(point.GetHashCode(), new PointF(10, 10).GetHashCode()); + Assert.NotEqual(point.GetHashCode(), new PointF(20, 10).GetHashCode()); + Assert.NotEqual(point.GetHashCode(), new PointF(10, 20).GetHashCode()); + } + + [Fact] + public void ToStringTest() + { + var p = new PointF(5.1F, -5.123F); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "PointF [ X={0}, Y={1} ]", p.X, p.Y), p.ToString()); + } + + [Fact] + public void ToStringEmptyTest() + { + var p = new PointF(0, 0); + Assert.Equal("PointF [ Empty ]", p.ToString()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Numerics/PointTests.cs b/tests/ImageSharp.Tests/Numerics/PointTests.cs index 82b26b946f..9f7abe067a 100644 --- a/tests/ImageSharp.Tests/Numerics/PointTests.cs +++ b/tests/ImageSharp.Tests/Numerics/PointTests.cs @@ -5,46 +5,248 @@ namespace ImageSharp.Tests { + using System.Globalization; + using System.Numerics; + using Xunit; - /// - /// Tests the struct. - /// public class PointTests { - /// - /// Tests the equality operators for equality. - /// [Fact] - public void AreEqual() + public void DefaultConstructorTest() + { + Assert.Equal(Point.Empty, new Point()); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void NonDefaultConstructorTest(int x, int y) + { + var p1 = new Point(x, y); + var p2 = new Point(new Size(x, y)); + + Assert.Equal(p1, p2); + } + + [Theory] + [InlineData(int.MaxValue)] + [InlineData(int.MinValue)] + [InlineData(0)] + public void SingleIntConstructorTest(int x) + { + var p1 = new Point(x); + var p2 = new Point(unchecked((short)(x & 0xFFFF)), unchecked((short)((x >> 16) & 0xFFFF))); + + Assert.Equal(p1, p2); + } + + [Fact] + public void IsEmptyDefaultsTest() + { + Assert.True(Point.Empty.IsEmpty); + Assert.True(new Point().IsEmpty); + Assert.True(new Point(0, 0).IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + public void IsEmptyRandomTest(int x, int y) + { + Assert.False(new Point(x, y).IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void CoordinatesTest(int x, int y) + { + var p = new Point(x, y); + Assert.Equal(x, p.X); + Assert.Equal(y, p.Y); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void PointFConversionTest(int x, int y) + { + PointF p = new Point(x, y); + Assert.Equal(new PointF(x, y), p); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void SizeConversionTest(int x, int y) + { + var sz = (Size)new Point(x, y); + Assert.Equal(new Size(x, y), sz); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void ArithmeticTest(int x, int y) + { + Point addExpected, subExpected, p = new Point(x, y); + var s = new Size(y, x); + + unchecked + { + addExpected = new Point(x + y, y + x); + subExpected = new Point(x - y, y - x); + } + + Assert.Equal(addExpected, p + s); + Assert.Equal(subExpected, p - s); + Assert.Equal(addExpected, Point.Add(p, s)); + Assert.Equal(subExpected, Point.Subtract(p, s)); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void PointFMathematicalTest(float x, float y) + { + var pf = new PointF(x, y); + Point pCeiling, pTruncate, pRound; + + unchecked + { + pCeiling = new Point((int)MathF.Ceiling(x), (int)MathF.Ceiling(y)); + pTruncate = new Point((int)x, (int)y); + pRound = new Point((int)MathF.Round(x), (int)MathF.Round(y)); + } + + Assert.Equal(pCeiling, Point.Ceiling(pf)); + Assert.Equal(pRound, Point.Round(pf)); + Assert.Equal(pTruncate, (Point)pf); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void OffsetTest(int x, int y) + { + var p1 = new Point(x, y); + var p2 = new Point(y, x); + + p1.Offset(p2); + + Assert.Equal(unchecked(p2.X + p2.Y), p1.X); + Assert.Equal(p1.X, p1.Y); + + p2.Offset(x, y); + Assert.Equal(p1, p2); + } + + [Fact] + public void RotateTest() + { + var p = new Point(13, 17); + Matrix3x2 matrix = Matrix3x2Extensions.CreateRotation(45, Point.Empty); + + var pout = Point.Rotate(p, matrix); + + Assert.Equal(new Point(-3, 21), pout); + } + + [Fact] + public void SkewTest() { - Point first = new Point(100, 100); - Point second = new Point(100, 100); + var p = new Point(13, 17); + Matrix3x2 matrix = Matrix3x2Extensions.CreateSkew(45, 45, Point.Empty); - Assert.Equal(first, second); + var pout = Point.Skew(p, matrix); + Assert.Equal(new Point(30, 30), pout); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void EqualityTest(int x, int y) + { + var p1 = new Point(x, y); + var p2 = new Point(x / 2 - 1, y / 2 - 1); + var p3 = new Point(x, y); + + Assert.True(p1 == p3); + Assert.True(p1 != p2); + Assert.True(p2 != p3); + + Assert.True(p1.Equals(p3)); + Assert.False(p1.Equals(p2)); + Assert.False(p2.Equals(p3)); + + Assert.True(p1.Equals((object)p3)); + Assert.False(p1.Equals((object)p2)); + Assert.False(p2.Equals((object)p3)); + + Assert.Equal(p1.GetHashCode(), p3.GetHashCode()); } - /// - /// Tests the equality operators for inequality. - /// [Fact] - public void AreNotEqual() + public static void EqualityTest_NotPoint() { - Point first = new Point(0, 100); - Point second = new Point(100, 100); + var point = new Point(0, 0); + Assert.False(point.Equals(null)); + Assert.False(point.Equals(0)); + Assert.False(point.Equals(new PointF(0, 0))); + } - Assert.NotEqual(first, second); + [Fact] + public static void GetHashCodeTest() + { + var point = new Point(10, 10); + Assert.Equal(point.GetHashCode(), new Point(10, 10).GetHashCode()); + Assert.NotEqual(point.GetHashCode(), new Point(20, 10).GetHashCode()); + Assert.NotEqual(point.GetHashCode(), new Point(10, 20).GetHashCode()); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(1, -2, 3, -4)] + public void ConversionTest(int x, int y, int width, int height) + { + var rect = new Rectangle(x, y, width, height); + RectangleF rectF = rect; + Assert.Equal(x, rectF.X); + Assert.Equal(y, rectF.Y); + Assert.Equal(width, rectF.Width); + Assert.Equal(height, rectF.Height); + } + + [Fact] + public void ToStringTest() + { + var p = new Point(5, -5); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Point [ X={0}, Y={1} ]", p.X, p.Y), p.ToString()); } - /// - /// Tests whether the point constructor correctly assign properties. - /// [Fact] - public void ConstructorAssignsProperties() + public void ToStringEmptyTest() { - Point first = new Point(4, 5); - Assert.Equal(4, first.X); - Assert.Equal(5, first.Y); + var p = new Point(0, 0); + Assert.Equal("Point [ Empty ]", p.ToString()); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Numerics/RectangleFTests.cs b/tests/ImageSharp.Tests/Numerics/RectangleFTests.cs new file mode 100644 index 0000000000..78fbc68f62 --- /dev/null +++ b/tests/ImageSharp.Tests/Numerics/RectangleFTests.cs @@ -0,0 +1,268 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Globalization; + using System.Reflection; + + using Xunit; + + /// + /// Tests the struct. + /// + public class RectangleFTests + { + [Fact] + public void DefaultConstructorTest() + { + Assert.Equal(RectangleF.Empty, new RectangleF()); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void NonDefaultConstructorTest(float x, float y, float width, float height) + { + var rect1 = new RectangleF(x, y, width, height); + var p = new PointF(x, y); + var s = new SizeF(width, height); + var rect2 = new RectangleF(p, s); + + Assert.Equal(rect1, rect2); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void FromLTRBTest(float left, float top, float right, float bottom) + { + var expected = new RectangleF(left, top, right - left, bottom - top); + var actual = RectangleF.FromLTRB(left, top, right, bottom); + + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void DimensionsTest(float x, float y, float width, float height) + { + var rect = new RectangleF(x, y, width, height); + var p = new PointF(x, y); + var s = new SizeF(width, height); + + Assert.Equal(p, rect.Location); + Assert.Equal(s, rect.Size); + Assert.Equal(x, rect.X); + Assert.Equal(y, rect.Y); + Assert.Equal(width, rect.Width); + Assert.Equal(height, rect.Height); + Assert.Equal(x, rect.Left); + Assert.Equal(y, rect.Top); + Assert.Equal(x + width, rect.Right); + Assert.Equal(y + height, rect.Bottom); + } + + [Fact] + public void IsEmptyTest() + { + Assert.True(RectangleF.Empty.IsEmpty); + Assert.True(new RectangleF().IsEmpty); + Assert.True(new RectangleF(1, -2, -10, 10).IsEmpty); + Assert.True(new RectangleF(1, -2, 10, -10).IsEmpty); + Assert.True(new RectangleF(1, -2, 0, 0).IsEmpty); + + Assert.False(new RectangleF(0, 0, 10, 10).IsEmpty); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(float.MaxValue, float.MinValue)] + public static void LocationSetTest(float x, float y) + { + var point = new PointF(x, y); + var rect = new RectangleF(10, 10, 10, 10) { Location = point }; + Assert.Equal(point, rect.Location); + Assert.Equal(point.X, rect.X); + Assert.Equal(point.Y, rect.Y); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(float.MaxValue, float.MinValue)] + public static void SizeSetTest(float x, float y) + { + var size = new SizeF(x, y); + var rect = new RectangleF(10, 10, 10, 10) { Size = size }; + Assert.Equal(size, rect.Size); + Assert.Equal(size.Width, rect.Width); + Assert.Equal(size.Height, rect.Height); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void EqualityTest(float x, float y, float width, float height) + { + var rect1 = new RectangleF(x, y, width, height); + var rect2 = new RectangleF(width, height, x, y); + + Assert.True(rect1 != rect2); + Assert.False(rect1 == rect2); + Assert.False(rect1.Equals(rect2)); + Assert.False(rect1.Equals((object)rect2)); + } + + [Fact] + public static void EqualityTestNotRectangleF() + { + var rectangle = new RectangleF(0, 0, 0, 0); + Assert.False(rectangle.Equals(null)); + Assert.False(rectangle.Equals(0)); + + // If RectangleF implements IEquatable (e.g. in .NET Core), then classes that are implicitly + // convertible to RectangleF can potentially be equal. + // See https://github.com/dotnet/corefx/issues/5255. + bool expectsImplicitCastToRectangleF = typeof(IEquatable).IsAssignableFrom(rectangle.GetType()); + Assert.Equal(expectsImplicitCastToRectangleF, rectangle.Equals(new Rectangle(0, 0, 0, 0))); + + Assert.False(rectangle.Equals((object)new Rectangle(0, 0, 0, 0))); // No implicit cast + } + + [Fact] + public static void GetHashCodeTest() + { + var rect1 = new RectangleF(10, 10, 10, 10); + var rect2 = new RectangleF(10, 10, 10, 10); + Assert.Equal(rect1.GetHashCode(), rect2.GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new RectangleF(20, 10, 10, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new RectangleF(10, 20, 10, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new RectangleF(10, 10, 20, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new RectangleF(10, 10, 10, 20).GetHashCode()); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void ContainsTest(float x, float y, float width, float height) + { + var rect = new RectangleF(x, y, width, height); + float X = (x + width) / 2; + float Y = (y + height) / 2; + var p = new PointF(X, Y); + var r = new RectangleF(X, Y, width / 2, height / 2); + + Assert.False(rect.Contains(X, Y)); + Assert.False(rect.Contains(p)); + Assert.False(rect.Contains(r)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue / 2, float.MinValue / 2, float.MinValue / 2, float.MaxValue / 2)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void InflateTest(float x, float y, float width, float height) + { + var rect = new RectangleF(x, y, width, height); + var inflatedRect = new RectangleF(x - width, y - height, width + 2 * width, height + 2 * height); + + rect.Inflate(width, height); + Assert.Equal(inflatedRect, rect); + + var s = new SizeF(x, y); + inflatedRect = RectangleF.Inflate(rect, x, y); + + rect.Inflate(s); + Assert.Equal(inflatedRect, rect); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue, float.MaxValue / 2, float.MinValue / 2)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void IntersectTest(float x, float y, float width, float height) + { + var rect1 = new RectangleF(x, y, width, height); + var rect2 = new RectangleF(y, x, width, height); + var expectedRect = RectangleF.Intersect(rect1, rect2); + rect1.Intersect(rect2); + Assert.Equal(expectedRect, rect1); + Assert.False(rect1.IntersectsWith(expectedRect)); + } + + [Fact] + public static void IntersectIntersectingRectsTest() + { + var rect1 = new RectangleF(0, 0, 5, 5); + var rect2 = new RectangleF(1, 1, 3, 3); + var expected = new RectangleF(1, 1, 3, 3); + + Assert.Equal(expected, RectangleF.Intersect(rect1, rect2)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void UnionTest(float x, float y, float width, float height) + { + var a = new RectangleF(x, y, width, height); + var b = new RectangleF(width, height, x, y); + + float x1 = Math.Min(a.X, b.X); + float x2 = Math.Max(a.X + a.Width, b.X + b.Width); + float y1 = Math.Min(a.Y, b.Y); + float y2 = Math.Max(a.Y + a.Height, b.Y + b.Height); + + var expectedRectangle = new RectangleF(x1, y1, x2 - x1, y2 - y1); + + Assert.Equal(expectedRectangle, RectangleF.Union(a, b)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue)] + [InlineData(float.MaxValue, 0, 0, float.MaxValue)] + [InlineData(0, float.MinValue, float.MaxValue, 0)] + public void OffsetTest(float x, float y, float width, float height) + { + var r1 = new RectangleF(x, y, width, height); + var expectedRect = new RectangleF(x + width, y + height, width, height); + var p = new PointF(width, height); + + r1.Offset(p); + Assert.Equal(expectedRect, r1); + + expectedRect.Offset(p); + r1.Offset(width, height); + Assert.Equal(expectedRect, r1); + } + + [Fact] + public void ToStringTest() + { + var r = new RectangleF(5, 5.1F, 1.3F, 1); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "RectangleF [ X={0}, Y={1}, Width={2}, Height={3} ]", r.X, r.Y, r.Width, r.Height), r.ToString()); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(5, -5, 0.2, -1.3)] + public void ToStringTestEmpty(float x, float y, float width, float height) + { + var r = new RectangleF(x, y, width, height); + Assert.Equal("RectangleF [ Empty ]", r.ToString()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Numerics/RectangleTests.cs b/tests/ImageSharp.Tests/Numerics/RectangleTests.cs index 2f9ad3d372..1ef03f0c64 100644 --- a/tests/ImageSharp.Tests/Numerics/RectangleTests.cs +++ b/tests/ImageSharp.Tests/Numerics/RectangleTests.cs @@ -5,6 +5,9 @@ namespace ImageSharp.Tests { + using System; + using System.Globalization; + using Xunit; /// @@ -12,55 +15,294 @@ namespace ImageSharp.Tests /// public class RectangleTests { - /// - /// Tests the equality operators for equality. - /// [Fact] - public void AreEqual() + public void DefaultConstructorTest() + { + Assert.Equal(Rectangle.Empty, new Rectangle()); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, 0, int.MinValue, 0)] + [InlineData(0, 0, 0, 0)] + [InlineData(0, int.MinValue, 0, int.MaxValue)] + public void NonDefaultConstructorTest(int x, int y, int width, int height) + { + var rect1 = new Rectangle(x, y, width, height); + var rect2 = new Rectangle(new Point(x, y), new Size(width, height)); + + Assert.Equal(rect1, rect2); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, 0, int.MinValue, 0)] + [InlineData(0, 0, 0, 0)] + [InlineData(0, int.MinValue, 0, int.MaxValue)] + public void FromLTRBTest(int left, int top, int right, int bottom) { - Rectangle first = new Rectangle(1, 1, 100, 100); - Rectangle second = new Rectangle(1, 1, 100, 100); + var rect1 = new Rectangle(left, top, unchecked(right - left), unchecked(bottom - top)); + var rect2 = Rectangle.FromLTRB(left, top, right, bottom); - Assert.Equal(first, second); + Assert.Equal(rect1, rect2); } - /// - /// Tests the equality operators for inequality. - /// [Fact] - public void AreNotEqual() + public void EmptyTest() + { + Assert.True(Rectangle.Empty.IsEmpty); + Assert.True(new Rectangle(0, 0, 0, 0).IsEmpty); + Assert.True(new Rectangle().IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, 0, int.MinValue, 0)] + [InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)] + [InlineData(0, int.MinValue, 0, int.MaxValue)] + public void NonEmptyTest(int x, int y, int width, int height) + { + Assert.False(new Rectangle(x, y, width, height).IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, 0, int.MinValue, 0)] + [InlineData(0, 0, 0, 0)] + [InlineData(0, int.MinValue, 0, int.MaxValue)] + [InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)] + public void DimensionsTest(int x, int y, int width, int height) + { + var rect = new Rectangle(x, y, width, height); + Assert.Equal(new Point(x, y), rect.Location); + Assert.Equal(new Size(width, height), rect.Size); + + Assert.Equal(x, rect.X); + Assert.Equal(y, rect.Y); + Assert.Equal(width, rect.Width); + Assert.Equal(height, rect.Height); + Assert.Equal(x, rect.Left); + Assert.Equal(y, rect.Top); + Assert.Equal(unchecked(x + width), rect.Right); + Assert.Equal(unchecked(y + height), rect.Bottom); + + var p = new Point(width, height); + var s = new Size(x, y); + rect.Location = p; + rect.Size = s; + + Assert.Equal(p, rect.Location); + Assert.Equal(s, rect.Size); + + Assert.Equal(width, rect.X); + Assert.Equal(height, rect.Y); + Assert.Equal(x, rect.Width); + Assert.Equal(y, rect.Height); + Assert.Equal(width, rect.Left); + Assert.Equal(height, rect.Top); + Assert.Equal(unchecked(x + width), rect.Right); + Assert.Equal(unchecked(y + height), rect.Bottom); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(int.MaxValue, int.MinValue)] + public static void LocationSetTest(int x, int y) + { + var point = new Point(x, y); + var rect = new Rectangle(10, 10, 10, 10) { Location = point }; + Assert.Equal(point, rect.Location); + Assert.Equal(point.X, rect.X); + Assert.Equal(point.Y, rect.Y); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(int.MaxValue, int.MinValue)] + public static void SizeSetTest(int x, int y) { - Rectangle first = new Rectangle(1, 1, 0, 100); - Rectangle second = new Rectangle(1, 1, 100, 100); + var size = new Size(x, y); + var rect = new Rectangle(10, 10, 10, 10) { Size = size }; + Assert.Equal(size, rect.Size); + Assert.Equal(size.Width, rect.Width); + Assert.Equal(size.Height, rect.Height); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MaxValue, int.MinValue)] + [InlineData(int.MaxValue, 0, int.MinValue, 0)] + [InlineData(0, int.MinValue, 0, int.MaxValue)] + [InlineData(int.MinValue, int.MaxValue, int.MinValue, int.MaxValue)] + public void EqualityTest(int x, int y, int width, int height) + { + var rect1 = new Rectangle(x, y, width, height); + var rect2 = new Rectangle(width / 2, height / 2, x, y); - Assert.NotEqual(first, second); + Assert.True(rect1 != rect2); + Assert.False(rect1 == rect2); + Assert.False(rect1.Equals(rect2)); + Assert.False(rect1.Equals((object)rect2)); } - /// - /// Tests whether the rectangle constructors correctly assign properties. - /// [Fact] - public void ConstructorAssignsProperties() - { - Rectangle first = new Rectangle(1, 1, 50, 100); - Assert.Equal(1, first.X); - Assert.Equal(1, first.Y); - Assert.Equal(50, first.Width); - Assert.Equal(100, first.Height); - Assert.Equal(1, first.Top); - Assert.Equal(51, first.Right); - Assert.Equal(101, first.Bottom); - Assert.Equal(1, first.Left); - - Rectangle second = new Rectangle(new Point(1, 1), new Size(50, 100)); - Assert.Equal(1, second.X); - Assert.Equal(1, second.Y); - Assert.Equal(50, second.Width); - Assert.Equal(100, second.Height); - Assert.Equal(1, second.Top); - Assert.Equal(51, second.Right); - Assert.Equal(101, second.Bottom); - Assert.Equal(1, second.Left); + public static void EqualityTestNotRectangle() + { + var rectangle = new Rectangle(0, 0, 0, 0); + Assert.False(rectangle.Equals(null)); + Assert.False(rectangle.Equals(0)); + Assert.False(rectangle.Equals(new RectangleF(0, 0, 0, 0))); + } + + [Fact] + public static void GetHashCodeTest() + { + var rect1 = new Rectangle(10, 10, 10, 10); + var rect2 = new Rectangle(10, 10, 10, 10); + Assert.Equal(rect1.GetHashCode(), rect2.GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new Rectangle(20, 10, 10, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new Rectangle(10, 20, 10, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new Rectangle(10, 10, 20, 10).GetHashCode()); + Assert.NotEqual(rect1.GetHashCode(), new Rectangle(10, 10, 10, 20).GetHashCode()); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue, float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MaxValue, float.MinValue, float.MaxValue)] + [InlineData(0, 0, 0, 0)] + public void RectangleFConversionTest(float x, float y, float width, float height) + { + var rect = new RectangleF(x, y, width, height); + Rectangle rCeiling, rTruncate, rRound; + + unchecked + { + rCeiling = new Rectangle((int)Math.Ceiling(x), (int)Math.Ceiling(y), + (int)Math.Ceiling(width), (int)Math.Ceiling(height)); + rTruncate = new Rectangle((int)x, (int)y, (int)width, (int)height); + rRound = new Rectangle((int)Math.Round(x), (int)Math.Round(y), + (int)Math.Round(width), (int)Math.Round(height)); + } + + Assert.Equal(rCeiling, Rectangle.Ceiling(rect)); + Assert.Equal(rTruncate, Rectangle.Truncate(rect)); + Assert.Equal(rRound, Rectangle.Round(rect)); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(0, int.MinValue, int.MaxValue, 0)] + public void ContainsTest(int x, int y, int width, int height) + { + var rect = new Rectangle(unchecked(2 * x - width), unchecked(2 * y - height), width, height); + var p = new Point(x, y); + var r = new Rectangle(x, y, width / 2, height / 2); + + Assert.False(rect.Contains(x, y)); + Assert.False(rect.Contains(p)); + Assert.False(rect.Contains(r)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(0, int.MinValue, int.MaxValue, 0)] + public void InflateTest(int x, int y, int width, int height) + { + Rectangle inflatedRect, rect = new Rectangle(x, y, width, height); + unchecked + { + inflatedRect = new Rectangle(x - width, y - height, width + 2 * width, height + 2 * height); + } + + Assert.Equal(inflatedRect, Rectangle.Inflate(rect, width, height)); + + rect.Inflate(width, height); + Assert.Equal(inflatedRect, rect); + + var s = new Size(x, y); + unchecked + { + inflatedRect = new Rectangle(rect.X - x, rect.Y - y, rect.Width + 2 * x, rect.Height + 2 * y); + } + + rect.Inflate(s); + Assert.Equal(inflatedRect, rect); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(0, int.MinValue, int.MaxValue, 0)] + public void IntersectTest(int x, int y, int width, int height) + { + var rect = new Rectangle(x, y, width, height); + var expectedRect = Rectangle.Intersect(rect, rect); + rect.Intersect(rect); + Assert.Equal(expectedRect, rect); + Assert.False(rect.IntersectsWith(expectedRect)); + } + + [Fact] + public static void IntersectIntersectingRectsTest() + { + var rect1 = new Rectangle(0, 0, 5, 5); + var rect2 = new Rectangle(1, 1, 3, 3); + var expected = new Rectangle(1, 1, 3, 3); + + Assert.Equal(expected, Rectangle.Intersect(rect1, rect2)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(int.MaxValue, 0, 0, int.MaxValue)] + [InlineData(0, int.MinValue, int.MaxValue, 0)] + public void UnionTest(int x, int y, int width, int height) + { + var a = new Rectangle(x, y, width, height); + var b = new Rectangle(width, height, x, y); + + int x1 = Math.Min(a.X, b.X); + int x2 = Math.Max(a.X + a.Width, b.X + b.Width); + int y1 = Math.Min(a.Y, b.Y); + int y2 = Math.Max(a.Y + a.Height, b.Y + b.Height); + + var expectedRectangle = new Rectangle(x1, y1, x2 - x1, y2 - y1); + + Assert.Equal(expectedRectangle, Rectangle.Union(a, b)); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(int.MaxValue, int.MinValue, int.MinValue, int.MaxValue)] + [InlineData(int.MaxValue, 0, 0, int.MaxValue)] + [InlineData(0, int.MinValue, int.MaxValue, 0)] + public void OffsetTest(int x, int y, int width, int height) + { + var r1 = new Rectangle(x, y, width, height); + var expectedRect = new Rectangle(x + width, y + height, width, height); + var p = new Point(width, height); + + r1.Offset(p); + Assert.Equal(expectedRect, r1); + + expectedRect.Offset(p); + r1.Offset(width, height); + Assert.Equal(expectedRect, r1); + } + + [Fact] + public void ToStringTest() + { + var r = new Rectangle(5, -5, 0, 1); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Rectangle [ X={0}, Y={1}, Width={2}, Height={3} ]", r.X, r.Y, r.Width, r.Height), r.ToString()); + } + + [Fact] + public void ToStringTestEmpty() + { + var r = new Rectangle(0, 0, 0, 0); + Assert.Equal("Rectangle [ Empty ]", r.ToString()); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Numerics/SizeFTests.cs b/tests/ImageSharp.Tests/Numerics/SizeFTests.cs new file mode 100644 index 0000000000..dfeee762f5 --- /dev/null +++ b/tests/ImageSharp.Tests/Numerics/SizeFTests.cs @@ -0,0 +1,163 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Globalization; + using System.Reflection; + using Xunit; + + public class SizeFTests + { + [Fact] + public void DefaultConstructorTest() + { + Assert.Equal(SizeF.Empty, new SizeF()); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void NonDefaultConstructorAndDimensionsTest(float width, float height) + { + var s1 = new SizeF(width, height); + var p1 = new PointF(width, height); + var s2 = new SizeF(s1); + + Assert.Equal(s1, s2); + Assert.Equal(s1, new SizeF(p1)); + Assert.Equal(s2, new SizeF(p1)); + + Assert.Equal(width, s1.Width); + Assert.Equal(height, s1.Height); + + s1.Width = 10; + Assert.Equal(10, s1.Width); + + s1.Height = -10.123f; + Assert.Equal(-10.123, s1.Height, 3); + } + + [Fact] + public void IsEmptyDefaultsTest() + { + Assert.True(SizeF.Empty.IsEmpty); + Assert.True(new SizeF().IsEmpty); + Assert.True(new SizeF(0, 0).IsEmpty); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + public void IsEmptyRandomTest(float width, float height) + { + Assert.False(new SizeF(width, height).IsEmpty); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void ArithmeticTest(float width, float height) + { + var s1 = new SizeF(width, height); + var s2 = new SizeF(height, width); + var addExpected = new SizeF(width + height, width + height); + var subExpected = new SizeF(width - height, height - width); + + Assert.Equal(addExpected, s1 + s2); + Assert.Equal(addExpected, SizeF.Add(s1, s2)); + + Assert.Equal(subExpected, s1 - s2); + Assert.Equal(subExpected, SizeF.Subtract(s1, s2)); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void EqualityTest(float width, float height) + { + var sLeft = new SizeF(width, height); + var sRight = new SizeF(height, width); + + if (width == height) + { + Assert.True(sLeft == sRight); + Assert.False(sLeft != sRight); + Assert.True(sLeft.Equals(sRight)); + Assert.True(sLeft.Equals((object)sRight)); + Assert.Equal(sLeft.GetHashCode(), sRight.GetHashCode()); + return; + } + + Assert.True(sLeft != sRight); + Assert.False(sLeft == sRight); + Assert.False(sLeft.Equals(sRight)); + Assert.False(sLeft.Equals((object)sRight)); + } + + [Fact] + public static void EqualityTest_NotSizeF() + { + var size = new SizeF(0, 0); + Assert.False(size.Equals(null)); + Assert.False(size.Equals(0)); + + // If SizeF implements IEquatable (e.g in .NET Core), then classes that are implicitly + // convertible to SizeF can potentially be equal. + // See https://github.com/dotnet/corefx/issues/5255. + bool expectsImplicitCastToSizeF = typeof(IEquatable).IsAssignableFrom(size.GetType()); + Assert.Equal(expectsImplicitCastToSizeF, size.Equals(new Size(0, 0))); + + Assert.False(size.Equals((object)new Size(0, 0))); // No implicit cast + } + + [Fact] + public static void GetHashCodeTest() + { + var size = new SizeF(10, 10); + Assert.Equal(size.GetHashCode(), new SizeF(10, 10).GetHashCode()); + Assert.NotEqual(size.GetHashCode(), new SizeF(20, 10).GetHashCode()); + Assert.NotEqual(size.GetHashCode(), new SizeF(10, 20).GetHashCode()); + } + + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void ConversionTest(float width, float height) + { + var s1 = new SizeF(width, height); + var p1 = (PointF)s1; + var s2 = new Size(unchecked((int)width), unchecked((int)height)); + + Assert.Equal(new PointF(width, height), p1); + Assert.Equal(p1, (PointF)s1); + Assert.Equal(s2, (Size)s1); + } + + [Fact] + public void ToStringTest() + { + var sz = new SizeF(10, 5); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "SizeF [ Width={0}, Height={1} ]", sz.Width, sz.Height), sz.ToString()); + } + + [Fact] + public void ToStringTestEmpty() + { + var sz = new SizeF(0, 0); + Assert.Equal("SizeF [ Empty ]", sz.ToString()); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Numerics/SizeTests.cs b/tests/ImageSharp.Tests/Numerics/SizeTests.cs index 29eb768d93..010cdad3c4 100644 --- a/tests/ImageSharp.Tests/Numerics/SizeTests.cs +++ b/tests/ImageSharp.Tests/Numerics/SizeTests.cs @@ -5,6 +5,7 @@ namespace ImageSharp.Tests { + using System.Globalization; using Xunit; /// @@ -12,39 +13,183 @@ namespace ImageSharp.Tests /// public class SizeTests { - /// - /// Tests the equality operators for equality. - /// [Fact] - public void AreEqual() + public void DefaultConstructorTest() { - Size first = new Size(100, 100); - Size second = new Size(100, 100); + Assert.Equal(Size.Empty, new Size()); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void NonDefaultConstructorTest(int width, int height) + { + var s1 = new Size(width, height); + var s2 = new Size(new Point(width, height)); - Assert.Equal(first, second); + Assert.Equal(s1, s2); + + s1.Width = 10; + Assert.Equal(10, s1.Width); + + s1.Height = -10; + Assert.Equal(-10, s1.Height); } - /// - /// Tests the equality operators for inequality. - /// [Fact] - public void AreNotEqual() + public void IsEmptyDefaultsTest() + { + Assert.True(Size.Empty.IsEmpty); + Assert.True(new Size().IsEmpty); + Assert.True(new Size(0, 0).IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + public void IsEmptyRandomTest(int width, int height) + { + Assert.False(new Size(width, height).IsEmpty); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void DimensionsTest(int width, int height) { - Size first = new Size(0, 100); - Size second = new Size(100, 100); + var p = new Size(width, height); + Assert.Equal(width, p.Width); + Assert.Equal(height, p.Height); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void PointFConversionTest(int width, int height) + { + SizeF sz = new Size(width, height); + Assert.Equal(new SizeF(width, height), sz); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void SizeConversionTest(int width, int height) + { + var sz = (Point)new Size(width, height); + Assert.Equal(new Point(width, height), sz); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void ArithmeticTest(int width, int height) + { + var sz1 = new Size(width, height); + var sz2 = new Size(height, width); + Size addExpected, subExpected; + + unchecked + { + addExpected = new Size(width + height, height + width); + subExpected = new Size(width - height, height - width); + } + + Assert.Equal(addExpected, sz1 + sz2); + Assert.Equal(subExpected, sz1 - sz2); + Assert.Equal(addExpected, Size.Add(sz1, sz2)); + Assert.Equal(subExpected, Size.Subtract(sz1, sz2)); + } - Assert.NotEqual(first, second); + [Theory] + [InlineData(float.MaxValue, float.MinValue)] + [InlineData(float.MinValue, float.MinValue)] + [InlineData(float.MaxValue, float.MaxValue)] + [InlineData(0, 0)] + public void PointFMathematicalTest(float width, float height) + { + var szF = new SizeF(width, height); + Size pCeiling, pTruncate, pRound; + + unchecked + { + pCeiling = new Size((int)MathF.Ceiling(width), (int)MathF.Ceiling(height)); + pTruncate = new Size((int)width, (int)height); + pRound = new Size((int)MathF.Round(width), (int)MathF.Round(height)); + } + + Assert.Equal(pCeiling, Size.Ceiling(szF)); + Assert.Equal(pRound, Size.Round(szF)); + Assert.Equal(pTruncate, (Size)szF); + } + + [Theory] + [InlineData(int.MaxValue, int.MinValue)] + [InlineData(int.MinValue, int.MinValue)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(0, 0)] + public void EqualityTest(int width, int height) + { + var p1 = new Size(width, height); + var p2 = new Size(unchecked(width - 1), unchecked(height - 1)); + var p3 = new Size(width, height); + + Assert.True(p1 == p3); + Assert.True(p1 != p2); + Assert.True(p2 != p3); + + Assert.True(p1.Equals(p3)); + Assert.False(p1.Equals(p2)); + Assert.False(p2.Equals(p3)); + + Assert.True(p1.Equals((object)p3)); + Assert.False(p1.Equals((object)p2)); + Assert.False(p2.Equals((object)p3)); + + Assert.Equal(p1.GetHashCode(), p3.GetHashCode()); + } + + [Fact] + public static void EqualityTest_NotSize() + { + var size = new Size(0, 0); + Assert.False(size.Equals(null)); + Assert.False(size.Equals(0)); + Assert.False(size.Equals(new SizeF(0, 0))); + } + + [Fact] + public static void GetHashCodeTest() + { + var size = new Size(10, 10); + Assert.Equal(size.GetHashCode(), new Size(10, 10).GetHashCode()); + Assert.NotEqual(size.GetHashCode(), new Size(20, 10).GetHashCode()); + Assert.NotEqual(size.GetHashCode(), new Size(10, 20).GetHashCode()); + } + + [Fact] + public void ToStringTest() + { + var sz = new Size(10, 5); + Assert.Equal(string.Format(CultureInfo.CurrentCulture, "Size [ Width={0}, Height={1} ]", sz.Width, sz.Height), sz.ToString()); } - /// - /// Tests whether the size constructor correctly assign properties. - /// [Fact] - public void ConstructorAssignsProperties() + public void ToStringTestEmpty() { - Size first = new Size(4, 5); - Assert.Equal(4, first.Width); - Assert.Equal(5, first.Height); + var sz = new Size(0, 0); + Assert.Equal("Size [ Empty ]", sz.ToString()); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs new file mode 100644 index 0000000000..76001ed3a0 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs @@ -0,0 +1,143 @@ +// ReSharper disable InconsistentNaming +namespace ImageSharp.Tests +{ + using System.Numerics; + + using ImageSharp.PixelFormats; + + using Xunit; + + public class Bgr24Tests + { + public static readonly TheoryData ColorData = + new TheoryData() { { 1, 2, 3 }, { 4, 5, 6 }, { 0, 255, 42 } }; + + [Theory] + [MemberData(nameof(ColorData))] + public void Constructor(byte r, byte g, byte b) + { + var p = new Rgb24(r, g, b); + + Assert.Equal(r, p.R); + Assert.Equal(g, p.G); + Assert.Equal(b, p.B); + } + + [Fact] + public unsafe void ByteLayoutIsSequentialBgr() + { + var color = new Bgr24(1, 2, 3); + byte* ptr = (byte*)&color; + + Assert.Equal(3, ptr[0]); + Assert.Equal(2, ptr[1]); + Assert.Equal(1, ptr[2]); + } + + [Theory] + [MemberData(nameof(ColorData))] + public void Equals_WhenTrue(byte r, byte g, byte b) + { + var x = new Bgr24(r, g, b); + var y = new Bgr24(r, g, b); + + Assert.True(x.Equals(y)); + Assert.True(x.Equals((object)y)); + Assert.Equal(x.GetHashCode(), y.GetHashCode()); + } + + [Theory] + [InlineData(1, 2, 3, 1, 2, 4)] + [InlineData(0, 255, 0, 0, 244, 0)] + [InlineData(1, 255, 0, 0, 255, 0)] + public void Equals_WhenFalse(byte r1, byte g1, byte b1, byte r2, byte g2, byte b2) + { + var a = new Bgr24(r1, g1, b1); + var b = new Bgr24(r2, g2, b2); + + Assert.False(a.Equals(b)); + Assert.False(a.Equals((object)b)); + } + + + [Fact] + public void PackFromRgba32() + { + var rgb = default(Bgr24); + rgb.PackFromRgba32(new Rgba32(1, 2, 3, 4)); + + Assert.Equal(1, rgb.R); + Assert.Equal(2, rgb.G); + Assert.Equal(3, rgb.B); + } + + private static Vector4 Vec(byte r, byte g, byte b, byte a = 255) => new Vector4( + r / 255f, + g / 255f, + b / 255f, + a / 255f); + + [Fact] + public void PackFromVector4() + { + var rgb = default(Bgr24); + rgb.PackFromVector4(Vec(1, 2, 3, 4)); + + Assert.Equal(1, rgb.R); + Assert.Equal(2, rgb.G); + Assert.Equal(3, rgb.B); + } + + [Fact] + public void ToVector4() + { + var rgb = new Bgr24(1, 2, 3); + + Assert.Equal(Vec(1, 2, 3), rgb.ToVector4()); + } + + [Fact] + public void ToRgb24() + { + var rgb = new Bgr24(1, 2, 3); + var dest = default(Rgb24); + + rgb.ToRgb24(ref dest); + + Assert.Equal(new Rgb24(1, 2, 3), dest); + } + + [Fact] + public void ToRgba32() + { + var rgb = new Bgr24(1, 2, 3); + var rgba = default(Rgba32); + + rgb.ToRgba32(ref rgba); + + Assert.Equal(new Rgba32(1, 2, 3, 255), rgba); + } + + [Fact] + public void ToBgr24() + { + var rgb = new Bgr24(1, 2, 3); + var bgr = default(Bgr24); + + rgb.ToBgr24(ref bgr); + + Assert.Equal(new Bgr24(1, 2, 3), bgr); + } + + [Fact] + public void ToBgra32() + { + var rgb = new Bgr24(1, 2, 3); + var bgra = default(Bgra32); + + rgb.ToBgra32(ref bgra); + + Assert.Equal(new Bgra32(1, 2, 3, 255), bgra); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs new file mode 100644 index 0000000000..1928d51f69 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs @@ -0,0 +1,150 @@ +// ReSharper disable InconsistentNaming +namespace ImageSharp.Tests +{ + using System.Numerics; + + using ImageSharp.PixelFormats; + + using Xunit; + + public class Bgra32Tests + { + public static readonly TheoryData ColorData = + new TheoryData() + { + { 1, 2, 3, 4 }, { 4, 5, 6, 7 }, { 0, 255, 42, 0 }, { 1, 2, 3, 255 } + }; + + [Theory] + [MemberData(nameof(ColorData))] + public void Constructor(byte b, byte g, byte r, byte a) + { + var p = new Bgra32(r, g, b, a); + + Assert.Equal(r, p.R); + Assert.Equal(g, p.G); + Assert.Equal(b, p.B); + Assert.Equal(a, p.A); + } + + [Fact] + public unsafe void ByteLayoutIsSequentialBgra() + { + var color = new Bgra32(1, 2, 3, 4); + byte* ptr = (byte*)&color; + + Assert.Equal(3, ptr[0]); + Assert.Equal(2, ptr[1]); + Assert.Equal(1, ptr[2]); + Assert.Equal(4, ptr[3]); + } + + [Theory] + [MemberData(nameof(ColorData))] + public void Equality_WhenTrue(byte b, byte g, byte r, byte a) + { + var x = new Bgra32(r, g, b, a); + var y = new Bgra32(r, g, b, a); + + Assert.True(x.Equals(y)); + Assert.True(x.Equals((object)y)); + Assert.Equal(x.GetHashCode(), y.GetHashCode()); + } + + [Theory] + [InlineData(1, 2, 3, 4, 1, 2, 3, 5)] + [InlineData(0, 0, 255, 0, 0, 0, 244, 0)] + [InlineData(0, 255, 0, 0, 0, 244, 0, 0)] + [InlineData(1, 255, 0, 0, 0, 255, 0, 0)] + public void Equality_WhenFalse(byte b1, byte g1, byte r1, byte a1, byte b2, byte g2, byte r2, byte a2) + { + var x = new Bgra32(r1, g1, b1, a1); + var y = new Bgra32(r2, g2, b2, a2); + + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + } + + + [Fact] + public void PackFromRgba32() + { + var rgb = default(Rgb24); + rgb.PackFromRgba32(new Rgba32(1, 2, 3, 4)); + + Assert.Equal(1, rgb.R); + Assert.Equal(2, rgb.G); + Assert.Equal(3, rgb.B); + } + + private static Vector4 Vec(byte r, byte g, byte b, byte a = 255) => new Vector4( + r / 255f, + g / 255f, + b / 255f, + a / 255f); + + [Fact] + public void PackFromVector4() + { + var c = default(Bgra32); + c.PackFromVector4(Vec(1, 2, 3, 4)); + + Assert.Equal(1, c.R); + Assert.Equal(2, c.G); + Assert.Equal(3, c.B); + Assert.Equal(4, c.A); + } + + [Fact] + public void ToVector4() + { + var rgb = new Bgra32(1, 2, 3, 4); + + Assert.Equal(Vec(1, 2, 3, 4), rgb.ToVector4()); + } + + [Fact] + public void ToRgb24() + { + var c = new Bgra32(1, 2, 3, 4); + var dest = default(Rgb24); + + c.ToRgb24(ref dest); + + Assert.Equal(new Rgb24(1, 2, 3), dest); + } + + [Fact] + public void ToRgba32() + { + var c = new Bgra32(1, 2, 3, 4); + var rgba = default(Rgba32); + + c.ToRgba32(ref rgba); + + Assert.Equal(new Rgba32(1, 2, 3, 4), rgba); + } + + [Fact] + public void ToBgr24() + { + var rgb = new Bgra32(1, 2, 3, 4); + var bgr = default(Bgr24); + + rgb.ToBgr24(ref bgr); + + Assert.Equal(new Bgr24(1, 2, 3), bgr); + } + + [Fact] + public void ToBgra32() + { + var rgb = new Bgra32(1, 2, 3, 4); + var bgra = default(Bgra32); + + rgb.ToBgra32(ref bgra); + + Assert.Equal(new Bgra32(1, 2, 3, 4), bgra); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/ColorConstructorTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorConstructorTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Colors/ColorConstructorTests.cs rename to tests/ImageSharp.Tests/PixelFormats/ColorConstructorTests.cs diff --git a/tests/ImageSharp.Tests/Colors/ColorDefinitionTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Colors/ColorDefinitionTests.cs rename to tests/ImageSharp.Tests/PixelFormats/ColorDefinitionTests.cs diff --git a/tests/ImageSharp.Tests/Colors/ColorEqualityTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorEqualityTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Colors/ColorEqualityTests.cs rename to tests/ImageSharp.Tests/PixelFormats/ColorEqualityTests.cs diff --git a/tests/ImageSharp.Tests/Colors/ColorPackingTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorPackingTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Colors/ColorPackingTests.cs rename to tests/ImageSharp.Tests/PixelFormats/ColorPackingTests.cs diff --git a/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs b/tests/ImageSharp.Tests/PixelFormats/PackedPixelTests.cs similarity index 98% rename from tests/ImageSharp.Tests/Colors/PackedPixelTests.cs rename to tests/ImageSharp.Tests/PixelFormats/PackedPixelTests.cs index 5ec7c21bbf..773bfa513d 100644 --- a/tests/ImageSharp.Tests/Colors/PackedPixelTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PackedPixelTests.cs @@ -40,10 +40,10 @@ namespace ImageSharp.Tests.Colors // Test ordering Vector4 vector = new Alpha8(.5F).ToVector4(); - Assert.Equal(vector.X, 0); - Assert.Equal(vector.Y, 0); - Assert.Equal(vector.Z, 0); - Assert.Equal(vector.W, .5F, 2); + Assert.Equal(0, vector.X); + Assert.Equal(0, vector.Y); + Assert.Equal(0, vector.Z); + Assert.Equal(.5F, vector.W, 2); byte[] rgb = new byte[3]; byte[] rgba = new byte[4]; @@ -302,7 +302,7 @@ namespace ImageSharp.Tests.Colors Assert.Equal(bgra, new byte[] { 0, 0, 128, 0 }); Byte4 r = new Byte4(); - r.PackFromBytes(20, 38, 0, 255); + r.PackFromRgba32(new Rgba32(20, 38, 0, 255)); r.ToXyzwBytes(rgba, 0); Assert.Equal(rgba, new byte[] { 20, 38, 0, 255 }); } @@ -448,7 +448,7 @@ namespace ImageSharp.Tests.Colors float y = -0.3f; Assert.Equal(0xda0d, new NormalizedByte2(x, y).PackedValue); NormalizedByte2 n = new NormalizedByte2(); - n.PackFromBytes(141, 90, 0, 0); + n.PackFromRgba32(new Rgba32(141, 90, 0, 0)); Assert.Equal(0xda0d, n.PackedValue); byte[] rgb = new byte[3]; @@ -491,7 +491,7 @@ namespace ImageSharp.Tests.Colors float w = -0.7f; Assert.Equal(0xA740DA0D, new NormalizedByte4(x, y, z, w).PackedValue); NormalizedByte4 n = new NormalizedByte4(); - n.PackFromBytes(141, 90, 192, 39); + n.PackFromRgba32(new Rgba32(141, 90, 192, 39)); Assert.Equal(0xA740DA0D, n.PackedValue); Assert.Equal((uint)958796544, new NormalizedByte4(0.0008f, 0.15f, 0.30f, 0.45f).PackedValue); @@ -515,7 +515,7 @@ namespace ImageSharp.Tests.Colors // http://community.monogame.net/t/normalizedbyte4-texture2d-gives-different-results-from-xna/8012/8 NormalizedByte4 r = new NormalizedByte4(); - r.PackFromBytes(9, 115, 202, 127); + r.PackFromRgba32(new Rgba32(9, 115, 202, 127)); r.ToXyzwBytes(rgba, 0); Assert.Equal(rgba, new byte[] { 9, 115, 202, 127 }); @@ -554,7 +554,7 @@ namespace ImageSharp.Tests.Colors byte[] bgra = new byte[4]; NormalizedShort2 n = new NormalizedShort2(); - n.PackFromBytes(141, 90, 0, 0); + n.PackFromRgba32(new Rgba32(141, 90, 0, 0)); n.ToXyzBytes(rgb, 0); Assert.Equal(rgb, new byte[] { 141, 90, 0 }); @@ -615,7 +615,7 @@ namespace ImageSharp.Tests.Colors Assert.Equal(bgra, new byte[] { 192, 90, 141, 39 }); NormalizedShort4 r = new NormalizedShort4(); - r.PackFromBytes(9, 115, 202, 127); + r.PackFromRgba32(new Rgba32(9, 115, 202, 127)); r.ToXyzwBytes(rgba, 0); Assert.Equal(rgba, new byte[] { 9, 115, 202, 127 }); } @@ -708,7 +708,7 @@ namespace ImageSharp.Tests.Colors // Alpha component accuracy will be awful. Rgba1010102 r = new Rgba1010102(); - r.PackFromBytes(25, 0, 128, 0); + r.PackFromRgba32(new Rgba32(25, 0, 128, 0)); r.ToXyzwBytes(rgba, 0); Assert.Equal(rgba, new byte[] { 25, 0, 128, 0 }); } @@ -801,7 +801,7 @@ namespace ImageSharp.Tests.Colors Assert.Equal(bgra, new byte[] { 76, 38, 20, 115 }); Rgba64 r = new Rgba64(); - r.PackFromBytes(20, 38, 76, 115); + r.PackFromRgba32(new Rgba32(20, 38, 76, 115)); r.ToXyzwBytes(rgba, 0); Assert.Equal(rgba, new byte[] { 20, 38, 76, 115 }); } @@ -856,7 +856,7 @@ namespace ImageSharp.Tests.Colors Assert.Equal(bgra, new byte[] { 0, 127, 128, 255 }); Short2 r = new Short2(); - r.PackFromBytes(20, 38, 0, 255); + r.PackFromRgba32(new Rgba32(20, 38, 0, 255)); r.ToXyzwBytes(rgba, 0); Assert.Equal(rgba, new byte[] { 20, 38, 0, 255 }); } @@ -913,7 +913,7 @@ namespace ImageSharp.Tests.Colors Assert.Equal(bgra, new byte[] { 243, 177, 172, 128 }); Short4 r = new Short4(); - r.PackFromBytes(20, 38, 0, 255); + r.PackFromRgba32(new Rgba32(20, 38, 0, 255)); r.ToXyzwBytes(rgba, 0); Assert.Equal(rgba, new byte[] { 20, 38, 0, 255 }); } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs new file mode 100644 index 0000000000..ce81499eda --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.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 partial class PixelOperationsTests + { + 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/Colors/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs similarity index 90% rename from tests/ImageSharp.Tests/Colors/PixelOperationsTests.cs rename to tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs index c91218ccc7..0a121cfce9 100644 --- a/tests/ImageSharp.Tests/Colors/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs @@ -1,6 +1,6 @@ // ReSharper disable InconsistentNaming // ReSharper disable AccessToDisposedClosure -namespace ImageSharp.Tests.Colors +namespace ImageSharp.Tests.PixelFormats { using System; using System.Numerics; @@ -11,8 +11,9 @@ namespace ImageSharp.Tests.Colors using Xunit; using Xunit.Abstractions; - public class PixelOperationsTests + public partial class PixelOperationsTests { + public class Color32 : PixelOperationsTests { public Color32(ITestOutputHelper output) @@ -42,8 +43,11 @@ namespace ImageSharp.Tests.Colors ); } + // [Fact] // Profiling benchmark - enable manually! +#pragma warning disable xUnit1013 // Public method should be marked as test public void Benchmark_ToVector4() +#pragma warning restore xUnit1013 // Public method should be marked as test { int times = 200000; int count = 1024; @@ -155,13 +159,13 @@ namespace ImageSharp.Tests.Colors { int i3 = i * 3; - expected[i].PackFromBytes(source[i3 + 0], source[i3 + 1], source[i3 + 2], 255); + expected[i].PackFromRgba32(new Rgba32(source[i3 + 0], source[i3 + 1], source[i3 + 2], 255)); } TestOperation( source, expected, - (s, d) => Operations.PackFromXyzBytes(s, d, count) + (s, d) => Operations.PackFromRgb24Bytes(s, d, count) ); } @@ -181,7 +185,7 @@ namespace ImageSharp.Tests.Colors TestOperation( source, expected, - (s, d) => Operations.ToXyzBytes(s, d, count) + (s, d) => Operations.ToRgb24Bytes(s, d, count) ); } @@ -196,13 +200,13 @@ namespace ImageSharp.Tests.Colors { int i4 = i * 4; - expected[i].PackFromBytes(source[i4 + 0], source[i4 + 1], source[i4 + 2], source[i4 + 3]); + expected[i].PackFromRgba32(new Rgba32(source[i4 + 0], source[i4 + 1], source[i4 + 2], source[i4 + 3])); } TestOperation( source, expected, - (s, d) => Operations.PackFromXyzwBytes(s, d, count) + (s, d) => Operations.PackFromRgba32Bytes(s, d, count) ); } @@ -222,7 +226,7 @@ namespace ImageSharp.Tests.Colors TestOperation( source, expected, - (s, d) => Operations.ToXyzwBytes(s, d, count) + (s, d) => Operations.ToRgba32Bytes(s, d, count) ); } @@ -237,13 +241,13 @@ namespace ImageSharp.Tests.Colors { int i3 = i * 3; - expected[i].PackFromBytes(source[i3 + 2], source[i3 + 1], source[i3 + 0], 255); + expected[i].PackFromRgba32(new Rgba32(source[i3 + 2], source[i3 + 1], source[i3 + 0], 255)); } TestOperation( source, expected, - (s, d) => Operations.PackFromZyxBytes(s, d, count) + (s, d) => Operations.PackFromBgr24Bytes(s, d, count) ); } @@ -263,7 +267,7 @@ namespace ImageSharp.Tests.Colors TestOperation( source, expected, - (s, d) => Operations.ToZyxBytes(s, d, count) + (s, d) => Operations.ToBgr24Bytes(s, d, count) ); } @@ -278,13 +282,13 @@ namespace ImageSharp.Tests.Colors { int i4 = i * 4; - expected[i].PackFromBytes(source[i4 + 2], source[i4 + 1], source[i4 + 0], source[i4 + 3]); + expected[i].PackFromRgba32(new Rgba32(source[i4 + 2], source[i4 + 1], source[i4 + 0], source[i4 + 3])); } TestOperation( source, expected, - (s, d) => Operations.PackFromZyxwBytes(s, d, count) + (s, d) => Operations.PackFromBgra32Bytes(s, d, count) ); } @@ -304,7 +308,7 @@ namespace ImageSharp.Tests.Colors TestOperation( source, expected, - (s, d) => Operations.ToZyxwBytes(s, d, count) + (s, d) => Operations.ToBgra32Bytes(s, d, count) ); } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs new file mode 100644 index 0000000000..1d0d024fbb --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs @@ -0,0 +1,143 @@ +// ReSharper disable InconsistentNaming +namespace ImageSharp.Tests +{ + using System; + using System.Numerics; + + using ImageSharp.PixelFormats; + + using Xunit; + + public class Rgb24Tests + { + public static readonly TheoryData ColorData = + new TheoryData() { { 1, 2, 3 }, { 4, 5, 6 }, { 0, 255, 42 } }; + + [Theory] + [MemberData(nameof(ColorData))] + public void Constructor(byte r, byte g, byte b) + { + var p = new Rgb24(r, g, b); + + Assert.Equal(r, p.R); + Assert.Equal(g, p.G); + Assert.Equal(b, p.B); + } + + [Fact] + public unsafe void ByteLayoutIsSequentialRgb() + { + var color = new Rgb24(1, 2, 3); + byte* ptr = (byte*)&color; + + Assert.Equal(1, ptr[0]); + Assert.Equal(2, ptr[1]); + Assert.Equal(3, ptr[2]); + } + + [Theory] + [MemberData(nameof(ColorData))] + public void Equals_WhenTrue(byte r, byte g, byte b) + { + var x = new Rgb24(r, g, b); + var y = new Rgb24(r, g, b); + + Assert.True(x.Equals(y)); + Assert.True(x.Equals((object)y)); + Assert.Equal(x.GetHashCode(), y.GetHashCode()); + } + + [Theory] + [InlineData(1, 2, 3, 1, 2, 4)] + [InlineData(0, 255, 0, 0, 244, 0)] + [InlineData(1, 255, 0, 0, 255, 0)] + public void Equals_WhenFalse(byte r1, byte g1, byte b1, byte r2, byte g2, byte b2) + { + var a = new Rgb24(r1, g1, b1); + var b = new Rgb24(r2, g2, b2); + + Assert.False(a.Equals(b)); + Assert.False(a.Equals((object)b)); + } + + [Fact] + public void PackFromRgba32() + { + var rgb = default(Rgb24); + rgb.PackFromRgba32(new Rgba32(1, 2, 3, 4)); + + Assert.Equal(1, rgb.R); + Assert.Equal(2, rgb.G); + Assert.Equal(3, rgb.B); + } + + private static Vector4 Vec(byte r, byte g, byte b, byte a = 255) => new Vector4( + r / 255f, + g / 255f, + b / 255f, + a / 255f); + + [Fact] + public void PackFromVector4() + { + var rgb = default(Rgb24); + rgb.PackFromVector4(Vec(1, 2, 3, 4)); + + Assert.Equal(1, rgb.R); + Assert.Equal(2, rgb.G); + Assert.Equal(3, rgb.B); + } + + [Fact] + public void ToVector4() + { + var rgb = new Rgb24(1, 2, 3); + + Assert.Equal(Vec(1, 2, 3), rgb.ToVector4()); + } + + [Fact] + public void ToRgb24() + { + var rgb = new Rgb24(1, 2, 3); + var dest = default(Rgb24); + + rgb.ToRgb24(ref dest); + + Assert.Equal(rgb, dest); + } + + [Fact] + public void ToRgba32() + { + var rgb = new Rgb24(1, 2, 3); + var rgba = default(Rgba32); + + rgb.ToRgba32(ref rgba); + + Assert.Equal(new Rgba32(1, 2, 3, 255), rgba); + } + + [Fact] + public void ToBgr24() + { + var rgb = new Rgb24(1, 2, 3); + var bgr = default(Bgr24); + + rgb.ToBgr24(ref bgr); + + Assert.Equal(new Bgr24(1, 2, 3), bgr); + } + + [Fact] + public void ToBgra32() + { + var rgb = new Rgb24(1, 2, 3); + var bgra = default(Bgra32); + + rgb.ToBgra32(ref bgra); + + Assert.Equal(new Bgra32(1, 2, 3, 255), bgra); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/Rgba32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs similarity index 100% rename from tests/ImageSharp.Tests/Colors/Rgba32Tests.cs rename to tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs diff --git a/tests/ImageSharp.Tests/Colors/RgbaVectorTests.cs b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Colors/RgbaVectorTests.cs rename to tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs diff --git a/tests/ImageSharp.Tests/Colors/UnPackedPixelTests.cs b/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Colors/UnPackedPixelTests.cs rename to tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs diff --git a/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs b/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs new file mode 100644 index 0000000000..e792de22f0 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Binarization/BinaryThresholdTest.cs @@ -0,0 +1,51 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Binarization +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class BinaryThresholdTest : FileTestBase + { + public static readonly TheoryData BinaryThresholdValues + = new TheoryData + { + .25F, + .75F + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BinaryThresholdValues), DefaultPixelType)] + public void ImageShouldApplyBinaryThresholdFilter(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.BinaryThreshold(value) + .DebugSave(provider, value, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BinaryThresholdValues), DefaultPixelType)] + public void ImageShouldApplyBinaryThresholdInBox(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.BinaryThreshold(value, bounds) + .DebugSave(provider, value, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Binarization/DitherTest.cs b/tests/ImageSharp.Tests/Processing/Binarization/DitherTest.cs new file mode 100644 index 0000000000..2948c10460 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Binarization/DitherTest.cs @@ -0,0 +1,92 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Binarization +{ + using ImageSharp.Dithering; + using ImageSharp.Dithering.Ordered; + using ImageSharp.PixelFormats; + + using Xunit; + + public class DitherTest : FileTestBase + { + public static readonly TheoryData Ditherers = new TheoryData + { + { "Ordered", new Ordered() }, + { "Bayer", new Bayer() } + }; + + public static readonly TheoryData ErrorDiffusers = new TheoryData + { + { "Atkinson", new Atkinson() }, + { "Burks", new Burks() }, + { "FloydSteinberg", new FloydSteinberg() }, + { "JarvisJudiceNinke", new JarvisJudiceNinke() }, + { "Sierra2", new Sierra2() }, + { "Sierra3", new Sierra3() }, + { "SierraLite", new SierraLite() }, + { "Stucki", new Stucki() }, + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(Ditherers), DefaultPixelType)] + public void ImageShouldApplyDitherFilter(TestImageProvider provider, string name, IOrderedDither ditherer) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Dither(ditherer) + .DebugSave(provider, name, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(Ditherers), DefaultPixelType)] + public void ImageShouldApplyDitherFilterInBox(TestImageProvider provider, string name, IOrderedDither ditherer) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Dither(ditherer, bounds) + .DebugSave(provider, name, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(ErrorDiffusers), DefaultPixelType)] + public void ImageShouldApplyDiffusionFilter(TestImageProvider provider, string name, IErrorDiffuser diffuser) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Dither(diffuser, .5F) + .DebugSave(provider, name, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(ErrorDiffusers), DefaultPixelType)] + public void ImageShouldApplyDiffusionFilterInBox(TestImageProvider provider, string name, IErrorDiffuser diffuser) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Dither(diffuser,.5F, bounds) + .DebugSave(provider, name, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/BlackWhiteTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/BlackWhiteTest.cs new file mode 100644 index 0000000000..d825b4993a --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/BlackWhiteTest.cs @@ -0,0 +1,43 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.ColorMatrix +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class BlackWhiteTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyBlackWhiteFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.BlackWhite() + .DebugSave(provider, null, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyBlackWhiteFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.BlackWhite(bounds) + .DebugSave(provider, null, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/ColorBlindnessTest.cs new file mode 100644 index 0000000000..b56ec3471a --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/ColorBlindnessTest.cs @@ -0,0 +1,57 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.ColorMatrix +{ + using ImageSharp.PixelFormats; + using ImageSharp.Processing; + + using Xunit; + + public class ColorBlindnessTest : FileTestBase + { + public static readonly TheoryData ColorBlindnessFilters + = new TheoryData + { + ColorBlindness.Achromatomaly, + ColorBlindness.Achromatopsia, + ColorBlindness.Deuteranomaly, + ColorBlindness.Deuteranopia, + ColorBlindness.Protanomaly, + ColorBlindness.Protanopia, + ColorBlindness.Tritanomaly, + ColorBlindness.Tritanopia + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(ColorBlindnessFilters), DefaultPixelType)] + public void ImageShouldApplyColorBlindnessFilter(TestImageProvider provider, ColorBlindness colorBlindness) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.ColorBlindness(colorBlindness) + .DebugSave(provider, colorBlindness.ToString(), Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(ColorBlindnessFilters), DefaultPixelType)] + public void ImageShouldApplyColorBlindnessFilterInBox(TestImageProvider provider, ColorBlindness colorBlindness) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.ColorBlindness(colorBlindness, bounds) + .DebugSave(provider, colorBlindness.ToString(), Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/GrayscaleTest.cs similarity index 61% rename from tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs rename to tests/ImageSharp.Tests/Processing/ColorMatrix/GrayscaleTest.cs index 2e82191ec5..89a388e4d4 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/GrayscaleTest.cs @@ -3,21 +3,27 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Tests +namespace ImageSharp.Tests.Processing.ColorMatrix { - using Xunit; + using ImageSharp.PixelFormats; using ImageSharp.Processing; - using ImageSharp.PixelFormats; + using Xunit; public class GrayscaleTest : FileTestBase { + public static readonly TheoryData GrayscaleModeTypes + = new TheoryData + { + GrayscaleMode.Bt601, + GrayscaleMode.Bt709 + }; + /// /// Use test patterns over loaded images to save decode time. /// [Theory] - [WithTestPatternImages(50, 50, PixelTypes.StandardImageClass, GrayscaleMode.Bt709)] - [WithTestPatternImages(50, 50, PixelTypes.StandardImageClass, GrayscaleMode.Bt601)] + [WithTestPatternImages(nameof(GrayscaleModeTypes), 50, 50, DefaultPixelType)] public void ImageShouldApplyGrayscaleFilterAll(TestImageProvider provider, GrayscaleMode value) where TPixel : struct, IPixel { @@ -37,22 +43,18 @@ namespace ImageSharp.Tests } [Theory] - [WithTestPatternImages(50, 50, PixelTypes.StandardImageClass, GrayscaleMode.Bt709)] - [WithTestPatternImages(50, 50, PixelTypes.StandardImageClass, GrayscaleMode.Bt601)] + [WithTestPatternImages(nameof(GrayscaleModeTypes), 50, 50, DefaultPixelType)] public void ImageShouldApplyGrayscaleFilterInBox(TestImageProvider provider, GrayscaleMode value) where TPixel : struct, IPixel { using (Image source = provider.GetImage()) - using (Image image = new Image(source)) + using (var image = new Image(source)) { - Rectangle rect = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); - image.Grayscale(rect, value) + var bounds = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + image.Grayscale(value, bounds) .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); + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/HueTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/HueTest.cs new file mode 100644 index 0000000000..b71905374a --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/HueTest.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.ColorMatrix +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class HueTest : FileTestBase + { + public static readonly TheoryData HueValues + = new TheoryData + { + 180, + -180 + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(HueValues), DefaultPixelType)] + public void ImageShouldApplyHueFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Hue(value) + .DebugSave(provider, value, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(HueValues), DefaultPixelType)] + public void ImageShouldApplyHueFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Hue(value, bounds) + .DebugSave(provider, value, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/KodachromeTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/KodachromeTest.cs new file mode 100644 index 0000000000..3a5d4f0d34 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/KodachromeTest.cs @@ -0,0 +1,43 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.ColorMatrix +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class KodachromeTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyKodachromeFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Kodachrome() + .DebugSave(provider, null, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyKodachromeFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Kodachrome(bounds) + .DebugSave(provider, null, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/LomographTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/LomographTest.cs new file mode 100644 index 0000000000..bfa37eef4e --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/LomographTest.cs @@ -0,0 +1,45 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System.IO; + + using ImageSharp.PixelFormats; + + using Xunit; + + public class LomographTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyLomographFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Lomograph() + .DebugSave(provider, null, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyLomographFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Lomograph(bounds) + .DebugSave(provider, null, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/PolaroidTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/PolaroidTest.cs new file mode 100644 index 0000000000..2bf1c36989 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/PolaroidTest.cs @@ -0,0 +1,43 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.ColorMatrix +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class PolaroidTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyPolaroidFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Polaroid() + .DebugSave(provider, null, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyPolaroidFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Polaroid(bounds) + .DebugSave(provider, null, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/SaturationTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/SaturationTest.cs new file mode 100644 index 0000000000..9212c09e83 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/SaturationTest.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.ColorMatrix +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class SaturationTest : FileTestBase + { + public static readonly TheoryData SaturationValues + = new TheoryData + { + 50 , + -50 , + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(SaturationValues), DefaultPixelType)] + public void ImageShouldApplySaturationFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Saturation(value) + .DebugSave(provider, value, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(SaturationValues), DefaultPixelType)] + public void ImageShouldApplySaturationFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Saturation(value, bounds) + .DebugSave(provider, value, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/ColorMatrix/SepiaTest.cs b/tests/ImageSharp.Tests/Processing/ColorMatrix/SepiaTest.cs new file mode 100644 index 0000000000..de536050ba --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/ColorMatrix/SepiaTest.cs @@ -0,0 +1,43 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.ColorMatrix +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class SepiaTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplySepiaFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Sepia() + .DebugSave(provider, null, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplySepiaFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Sepia(bounds) + .DebugSave(provider, null, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs new file mode 100644 index 0000000000..b5ee876de7 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Convolution +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class BoxBlurTest : FileTestBase + { + public static readonly TheoryData BoxBlurValues + = new TheoryData + { + 3, + 5 + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BoxBlurValues), DefaultPixelType)] + public void ImageShouldApplyBoxBlurFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.BoxBlur(value) + .DebugSave(provider, value, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BoxBlurValues), DefaultPixelType)] + public void ImageShouldApplyBoxBlurFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.BoxBlur(value, bounds) + .DebugSave(provider, value, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs new file mode 100644 index 0000000000..220c0f4664 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs @@ -0,0 +1,59 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Convolution +{ + using ImageSharp.PixelFormats; + using ImageSharp.Processing; + + using Xunit; + + public class DetectEdgesTest : FileTestBase + { + public static readonly TheoryData DetectEdgesFilters + = new TheoryData + { + EdgeDetection.Kayyali, + EdgeDetection.Kirsch, + EdgeDetection.Lapacian3X3, + EdgeDetection.Lapacian5X5, + EdgeDetection.LaplacianOfGaussian, + EdgeDetection.Prewitt, + EdgeDetection.RobertsCross, + EdgeDetection.Robinson, + EdgeDetection.Scharr, + EdgeDetection.Sobel + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(DetectEdgesFilters), DefaultPixelType)] + public void ImageShouldApplyDetectEdgesFilter(TestImageProvider provider, EdgeDetection detector) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.DetectEdges(detector) + .DebugSave(provider, detector.ToString(), Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(DetectEdgesFilters), DefaultPixelType)] + public void ImageShouldApplyDetectEdgesFilterInBox(TestImageProvider provider, EdgeDetection detector) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.DetectEdges(detector, bounds) + .DebugSave(provider, detector.ToString(), Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs similarity index 50% rename from tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs rename to tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs index 4b2ac8b7cf..dbaed9290c 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs @@ -3,49 +3,47 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Tests +namespace ImageSharp.Tests.Processing.Convolution { - using System.IO; using ImageSharp.PixelFormats; + using Xunit; - public class GaussianBlurTest + public class GaussianBlurTest : FileTestBase { public static readonly TheoryData GaussianBlurValues = new TheoryData { - 3 , - 5 , + 3, + 5 }; [Theory] - [WithTestPatternImages(nameof(GaussianBlurValues), 320, 240, PixelTypes.StandardImageClass)] + [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] public void ImageShouldApplyGaussianBlurFilter(TestImageProvider provider, int value) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { image.GaussianBlur(value) - .DebugSave(provider, value.ToString()); + .DebugSave(provider, value, Extensions.Bmp); } } [Theory] - [WithTestPatternImages(nameof(GaussianBlurValues), 320, 240, PixelTypes.StandardImageClass)] + [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] public void ImageShouldApplyGaussianBlurFilterInBox(TestImageProvider provider, int value) where TPixel : struct, IPixel { using (Image source = provider.GetImage()) - using (Image image = new Image(source)) + using (var image = new Image(source)) { - Rectangle rect = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); - image.GaussianBlur(value, rect) - .DebugSave(provider, value.ToString()); + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.GaussianBlur(value, bounds) + .DebugSave(provider, value, Extensions.Bmp); - // 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); + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs similarity index 50% rename from tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs rename to tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs index 1fa1ae15c1..44296d9751 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs @@ -3,10 +3,10 @@ // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Tests +namespace ImageSharp.Tests.Processing.Convolution { - using System.IO; using ImageSharp.PixelFormats; + using Xunit; public class GaussianSharpenTest : FileTestBase @@ -14,38 +14,36 @@ namespace ImageSharp.Tests public static readonly TheoryData GaussianSharpenValues = new TheoryData { - 3 , - 5 , + 3, + 5 }; [Theory] - [WithTestPatternImages(nameof(GaussianSharpenValues), 320, 240, PixelTypes.StandardImageClass)] + [WithFileCollection(nameof(DefaultFiles), nameof(GaussianSharpenValues), DefaultPixelType)] public void ImageShouldApplyGaussianSharpenFilter(TestImageProvider provider, int value) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { image.GaussianSharpen(value) - .DebugSave(provider, value.ToString()); + .DebugSave(provider, value, Extensions.Bmp); } } [Theory] - [WithTestPatternImages(nameof(GaussianSharpenValues), 320, 240, PixelTypes.StandardImageClass)] + [WithFileCollection(nameof(DefaultFiles), nameof(GaussianSharpenValues), DefaultPixelType)] public void ImageShouldApplyGaussianSharpenFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel + where TPixel : struct, IPixel { using (Image source = provider.GetImage()) - using (Image image = new Image(source)) + using (var image = new Image(source)) { - Rectangle rect = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); - image.GaussianSharpen(value, rect) - .DebugSave(provider, value.ToString()); + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.GaussianSharpen(value, bounds) + .DebugSave(provider, value, Extensions.Bmp); - // 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); + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); } } } diff --git a/tests/ImageSharp.Tests/Processing/Effects/AlphaTest.cs b/tests/ImageSharp.Tests/Processing/Effects/AlphaTest.cs new file mode 100644 index 0000000000..63366dc1da --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Effects/AlphaTest.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Effects +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class AlphaTest : FileTestBase + { + public static readonly TheoryData AlphaValues + = new TheoryData + { + 20/100F, + 80/100F + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(AlphaValues), DefaultPixelType)] + public void ImageShouldApplyAlphaFilter(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Alpha(value) + .DebugSave(provider, value, Extensions.Png); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(AlphaValues), DefaultPixelType)] + public void ImageShouldApplyAlphaFilterInBox(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Alpha(value, bounds) + .DebugSave(provider, value, Extensions.Png); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs new file mode 100644 index 0000000000..c3fda4cd4b --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Effects/BackgroundColorTest.cs @@ -0,0 +1,43 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Effects +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class BackgroundColorTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyBackgroundColorFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.BackgroundColor(NamedColors.HotPink) + .DebugSave(provider, null, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyBackgroundColorFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.BackgroundColor(NamedColors.HotPink, bounds) + .DebugSave(provider, null, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Effects/BrightnessTest.cs new file mode 100644 index 0000000000..4c0217fed0 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Effects/BrightnessTest.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Effects +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class BrightnessTest : FileTestBase + { + public static readonly TheoryData BrightnessValues + = new TheoryData + { + 50, + -50 + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BrightnessValues), DefaultPixelType)] + public void ImageShouldApplyBrightnessFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Brightness(value) + .DebugSave(provider, value, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(BrightnessValues), DefaultPixelType)] + public void ImageShouldApplyBrightnessFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Brightness(value, bounds) + .DebugSave(provider, value, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); ; + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Effects/ContrastTest.cs new file mode 100644 index 0000000000..ac217e9f6a --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Effects/ContrastTest.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Effects +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class ContrastTest : FileTestBase + { + public static readonly TheoryData ContrastValues + = new TheoryData + { + 50, + -50 + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(ContrastValues), DefaultPixelType)] + public void ImageShouldApplyContrastFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Contrast(value) + .DebugSave(provider, value, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(ContrastValues), DefaultPixelType)] + public void ImageShouldApplyContrastFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Contrast(value, bounds) + .DebugSave(provider, value, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/InvertTest.cs b/tests/ImageSharp.Tests/Processing/Effects/InvertTest.cs new file mode 100644 index 0000000000..80e3450926 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Effects/InvertTest.cs @@ -0,0 +1,43 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Effects +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class InvertTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyInvertFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Invert() + .DebugSave(provider, null, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyInvertFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Invert(bounds) + .DebugSave(provider, null, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs b/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs new file mode 100644 index 0000000000..7d02ace3da --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs @@ -0,0 +1,50 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class OilPaintTest : FileTestBase + { + public static readonly TheoryData OilPaintValues + = new TheoryData + { + { 15, 10 }, + { 6, 5 } + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(OilPaintValues), DefaultPixelType)] + public void ImageShouldApplyOilPaintFilter(TestImageProvider provider, int levels, int brushSize) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.OilPaint(levels, brushSize) + .DebugSave(provider, string.Join("-", levels, brushSize), Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(OilPaintValues), DefaultPixelType)] + public void ImageShouldApplyOilPaintFilterInBox(TestImageProvider provider, int levels, int brushSize) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.OilPaint(levels, brushSize, bounds) + .DebugSave(provider, string.Join("-", levels, brushSize), Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds, 0.001F); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs b/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs new file mode 100644 index 0000000000..740b78c558 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs @@ -0,0 +1,84 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Effects +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class PixelateTest : FileTestBase + { + public static readonly TheoryData PixelateValues + = new TheoryData + { + 4 , + 8 + }; + + [Theory] + [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.Rgba32)] + public void ImageShouldApplyPixelateFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Pixelate(value) + .DebugSave(provider, value, Extensions.Bmp); + + // Test the neigbouring pixels + for (int y = 0; y < image.Height; y += value) + { + for (int x = 0; x < image.Width; x += value) + { + TPixel source = image[x, y]; + for (int pixY = y; pixY < y + value && pixY < image.Height; pixY++) + { + for (int pixX = x; pixX < x + value && pixX < image.Width; pixX++) + { + Assert.Equal(source, image[pixX, pixY]); + } + } + } + } + } + } + + [Theory] + [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.Rgba32)] + public void ImageShouldApplyPixelateFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + + image.Pixelate(value, bounds) + .DebugSave(provider, value, Extensions.Bmp); + + for (int y = 0; y < image.Height; y++) + { + for (int x = 0; x < image.Width; x++) + { + int tx = x; + int ty = y; + TPixel sourceColor = source[tx, ty]; + if (bounds.Contains(tx, ty)) + { + int sourceX = tx - ((tx - bounds.Left) % value) + (value / 2); + int sourceY = ty - ((ty - bounds.Top) % value) + (value / 2); + + sourceColor = image[sourceX, sourceY]; + } + Assert.Equal(sourceColor, image[tx, ty]); + } + } + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs new file mode 100644 index 0000000000..a233e204cb --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Overlays/GlowTest.cs @@ -0,0 +1,67 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Overlays +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class GlowTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyGlowFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Glow() + .DebugSave(provider, null, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyGlowFilterColor(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Glow(NamedColors.Orange) + .DebugSave(provider, null, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyGlowFilterRadius(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Glow(image.Width / 4F) + .DebugSave(provider, null, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyGlowFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Glow(bounds) + .DebugSave(provider, null, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs new file mode 100644 index 0000000000..85ccfca6f0 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Overlays/VignetteTest.cs @@ -0,0 +1,67 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Overlays +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class VignetteTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyVignetteFilter(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Vignette() + .DebugSave(provider, null, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyVignetteFilterColor(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Vignette(NamedColors.Orange) + .DebugSave(provider, null, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyVignetteFilterRadius(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Vignette(image.Width / 4F, image.Height / 4F) + .DebugSave(provider, null, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldApplyVignetteFilterInBox(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (var image = new Image(source)) + { + var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); + + image.Vignette(bounds) + .DebugSave(provider, null, Extensions.Bmp); + + ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs similarity index 56% rename from tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs rename to tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs index 470e7150b3..7bc0c8bb52 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/AutoOrientTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs @@ -1,19 +1,19 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -namespace ImageSharp.Tests +namespace ImageSharp.Tests.Processing.Transforms { - using System.IO; - using ImageSharp.PixelFormats; + using ImageSharp.Processing; - using Processing; using Xunit; public class AutoOrientTests : FileTestBase { + public static readonly string[] FlipFiles = { TestImages.Bmp.F }; + public static readonly TheoryData OrientationValues = new TheoryData { @@ -29,23 +29,19 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData(nameof(OrientationValues))] - public void ImageShouldFlip(RotateType rotateType, FlipType flipType, ushort orientation) + [WithFileCollection(nameof(FlipFiles), nameof(OrientationValues), DefaultPixelType)] + public void ImageShouldAutoRotate(TestImageProvider provider, RotateType rotateType, FlipType flipType, ushort orientation) + where TPixel : struct, IPixel { - string path = this.CreateOutputDirectory("AutoOrient"); - - TestFile file = TestFile.Create(TestImages.Bmp.F); - - using (Image image = file.CreateImage()) + using (Image image = provider.GetImage()) { image.MetaData.ExifProfile = new ExifProfile(); image.MetaData.ExifProfile.SetValue(ExifTag.Orientation, orientation); - using (FileStream before = File.OpenWrite($"{path}/before-{file.FileName}")) - using (FileStream after = File.OpenWrite($"{path}/after-{file.FileName}")) - { - image.RotateFlip(rotateType, flipType).Save(before).AutoOrient().Save(after); - } + image.RotateFlip(rotateType, flipType) + .DebugSave(provider, string.Join("_", rotateType, flipType, orientation, "1_before"), Extensions.Bmp) + .AutoOrient() + .DebugSave(provider, string.Join("_", rotateType, flipType, orientation, "2_after"), Extensions.Bmp); } } } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs new file mode 100644 index 0000000000..ca20abf790 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs @@ -0,0 +1,26 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Transforms +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class CropTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldCrop(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Crop(image.Width / 2, image.Height / 2) + .DebugSave(provider, null, Extensions.Bmp); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs new file mode 100644 index 0000000000..24febd5b2f --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs @@ -0,0 +1,33 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Transforms +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class EntropyCropTest : FileTestBase + { + public static readonly TheoryData EntropyCropValues + = new TheoryData + { + .25F, + .75F + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(EntropyCropValues), DefaultPixelType)] + public void ImageShouldEntropyCrop(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.EntropyCrop(value) + .DebugSave(provider, value, Extensions.Bmp); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs new file mode 100644 index 0000000000..45ab1e5f8b --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs @@ -0,0 +1,37 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Transforms +{ + using ImageSharp.PixelFormats; + using ImageSharp.Processing; + + using Xunit; + + public class FlipTests : FileTestBase + { + public static readonly string[] FlipFiles = { TestImages.Bmp.F }; + + public static readonly TheoryData FlipValues + = new TheoryData + { + { FlipType.None }, + { FlipType.Vertical }, + { FlipType.Horizontal }, + }; + + [Theory] + [WithFileCollection(nameof(FlipFiles), nameof(FlipValues), DefaultPixelType)] + public void ImageShouldFlip(TestImageProvider provider, FlipType flipType) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Flip(flipType) + .DebugSave(provider, flipType, Extensions.Bmp); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs new file mode 100644 index 0000000000..7caa1e7c0e --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs @@ -0,0 +1,35 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Transforms +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class PadTest : FileTestBase + { + [Theory] + [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] + public void ImageShouldPad(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Pad(image.Width + 50, image.Height + 50) + .DebugSave(provider, null, Extensions.Bmp); + + // Check pixels are empty + for (int y = 0; y < 25; y++) + { + for (int x = 0; x < 25; x++) + { + Assert.Equal(default(TPixel), image[x, y]); + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/ResizeProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeProfilingBenchmarks.cs similarity index 65% rename from tests/ImageSharp.Tests/Processors/Filters/ResizeProfilingBenchmarks.cs rename to tests/ImageSharp.Tests/Processing/Transforms/ResizeProfilingBenchmarks.cs index a743665d4b..a300672e85 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/ResizeProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeProfilingBenchmarks.cs @@ -1,9 +1,14 @@ -namespace ImageSharp.Tests +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Transforms { + using System; using System.IO; using System.Text; - using ImageSharp.PixelFormats; using ImageSharp.Processing; using ImageSharp.Processing.Processors; @@ -20,14 +25,14 @@ namespace ImageSharp.Tests public int ExecutionCount { get; set; } = 50; // [Theory] // Benchmark, enable manually! - [InlineData(100, 100)] - [InlineData(2000, 2000)] + // [InlineData(100, 100)] + // [InlineData(2000, 2000)] public void ResizeBicubic(int width, int height) { this.Measure(this.ExecutionCount, () => { - using (Image image = new Image(width, height)) + using (var image = new Image(width, height)) { image.Resize(width / 4, height / 4); } @@ -37,26 +42,28 @@ namespace ImageSharp.Tests // [Fact] public void PrintWeightsData() { - ResizeProcessor proc = new ResizeProcessor(new BicubicResampler(), 200, 200); + var proc = new ResizeProcessor(new BicubicResampler(), 200, 200); ResamplingWeightedProcessor.WeightsBuffer weights = proc.PrecomputeWeights(200, 500); - StringBuilder bld = new StringBuilder(); + var bld = new StringBuilder(); foreach (ResamplingWeightedProcessor.WeightsWindow window in weights.Weights) { + Span span = window.GetWindowSpan(); for (int i = 0; i < window.Length; i++) { - float value = window.Span[i]; + float value = span[i]; bld.Append(value); bld.Append("| "); } + bld.AppendLine(); } File.WriteAllText("BicubicWeights.MD", bld.ToString()); - //this.Output.WriteLine(bld.ToString()); + // this.Output.WriteLine(bld.ToString()); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs new file mode 100644 index 0000000000..008d041e7e --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs @@ -0,0 +1,273 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Transforms +{ + using ImageSharp.PixelFormats; + using ImageSharp.Processing; + + using Xunit; + + public class ResizeTests : FileTestBase + { + public static readonly string[] ResizeFiles = { TestImages.Jpeg.Baseline.Calliphora }; + + public static readonly TheoryData ReSamplers = + new TheoryData + { + { "Bicubic", new BicubicResampler() }, + { "Triangle", new TriangleResampler() }, + { "NearestNeighbor", new NearestNeighborResampler() }, + { "Box", new BoxResampler() }, + { "Lanczos3", new Lanczos3Resampler() }, + { "Lanczos5", new Lanczos5Resampler() }, + { "MitchellNetravali", new MitchellNetravaliResampler() }, + { "Lanczos8", new Lanczos8Resampler() }, + { "Hermite", new HermiteResampler() }, + { "Spline", new SplineResampler() }, + { "Robidoux", new RobidouxResampler() }, + { "RobidouxSharp", new RobidouxSharpResampler() }, + { "Welch", new WelchResampler() } + }; + + [Theory] + [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] + public void ImageShouldResize(TestImageProvider provider, string name, IResampler sampler) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Resize(image.Width / 2, image.Height / 2, sampler, true) + .DebugSave(provider, name, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] + public void ImageShouldResizeFromSourceRectangle(TestImageProvider provider, string name, IResampler sampler) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var sourceRectangle = new Rectangle(image.Width / 8, image.Height / 8, image.Width / 4, image.Height / 4); + var destRectangle = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + + image.Resize(image.Width, image.Height, sampler, sourceRectangle, destRectangle, false) + .DebugSave(provider, name, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] + public void ImageShouldResizeWidthAndKeepAspect(TestImageProvider provider, string name, IResampler sampler) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Resize(image.Width / 3, 0, sampler, false) + .DebugSave(provider, name, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] + public void ImageShouldResizeHeightAndKeepAspect(TestImageProvider provider, string name, IResampler sampler) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Resize(0, image.Height / 3, sampler, false) + .DebugSave(provider, name, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] + public void ImageShouldResizeWithCropWidthMode(TestImageProvider provider, string name, IResampler sampler) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Sampler = sampler, + Size = new Size(image.Width / 2, image.Height) + }; + + image.Resize(options) + .DebugSave(provider, name, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] + public void ImageShouldResizeWithCropHeightMode(TestImageProvider provider, string name, IResampler sampler) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Sampler = sampler, + Size = new Size(image.Width, image.Height / 2) + }; + + image.Resize(options) + .DebugSave(provider, name, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] + public void ImageShouldResizeWithPadMode(TestImageProvider provider, string name, IResampler sampler) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Sampler = sampler, + Size = new Size(image.Width + 200, image.Height), + Mode = ResizeMode.Pad + }; + + image.Resize(options) + .DebugSave(provider, name, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] + public void ImageShouldResizeWithBoxPadMode(TestImageProvider provider, string name, IResampler sampler) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Sampler = sampler, + Size = new Size(image.Width + 200, image.Height + 200), + Mode = ResizeMode.BoxPad + }; + + image.Resize(options) + .DebugSave(provider, name, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] + public void ImageShouldResizeWithMaxMode(TestImageProvider provider, string name, IResampler sampler) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Sampler = sampler, + Size = new Size(300, 300), + Mode = ResizeMode.Max + }; + + image.Resize(options) + .DebugSave(provider, name, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] + public void ImageShouldResizeWithMinMode(TestImageProvider provider, string name, IResampler sampler) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Sampler = sampler, + Size = new Size((int)MathF.Round(image.Width * .75F), (int)MathF.Round(image.Height * .95F)), + Mode = ResizeMode.Min + }; + + image.Resize(options) + .DebugSave(provider, name, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(ResizeFiles), nameof(ReSamplers), DefaultPixelType)] + public void ImageShouldResizeWithStretchMode(TestImageProvider provider, string name, IResampler sampler) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var options = new ResizeOptions + { + Sampler = sampler, + Size = new Size(image.Width / 2, image.Height), + Mode = ResizeMode.Stretch + }; + + image.Resize(options) + .DebugSave(provider, name, Extensions.Bmp); + } + } + + [Theory] + [InlineData(-2, 0)] + [InlineData(-1, 0)] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public static void BicubicWindowOscillatesCorrectly(float x, float expected) + { + var sampler = new BicubicResampler(); + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + + [Theory] + [InlineData(-2, 0)] + [InlineData(-1, 0)] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public static void TriangleWindowOscillatesCorrectly(float x, float expected) + { + var sampler = new TriangleResampler(); + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + + [Theory] + [InlineData(-2, 0)] + [InlineData(-1, 0)] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(2, 0)] + public static void Lanczos3WindowOscillatesCorrectly(float x, float expected) + { + var sampler = new Lanczos3Resampler(); + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + + [Theory] + [InlineData(-4, 0)] + [InlineData(-2, 0)] + [InlineData(0, 1)] + [InlineData(2, 0)] + [InlineData(4, 0)] + public static void Lanczos5WindowOscillatesCorrectly(float x, float expected) + { + var sampler = new Lanczos5Resampler(); + float result = sampler.GetValue(x); + + Assert.Equal(result, expected); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs new file mode 100644 index 0000000000..f85ef6f13a --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs @@ -0,0 +1,39 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Transforms +{ + using ImageSharp.PixelFormats; + using ImageSharp.Processing; + + using Xunit; + + public class RotateFlipTests : FileTestBase + { + public static readonly string[] FlipFiles = { TestImages.Bmp.F }; + + public static readonly TheoryData RotateFlipValues + = new TheoryData + { + { RotateType.None, FlipType.Vertical }, + { RotateType.None, FlipType.Horizontal }, + { RotateType.Rotate90, FlipType.None }, + { RotateType.Rotate180, FlipType.None }, + { RotateType.Rotate270, FlipType.None }, + }; + + [Theory] + [WithFileCollection(nameof(FlipFiles), nameof(RotateFlipValues), DefaultPixelType)] + public void ImageShouldRotateFlip(TestImageProvider provider, RotateType rotateType, FlipType flipType) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.RotateFlip(rotateType, flipType) + .DebugSave(provider, string.Join("_", rotateType, flipType), Extensions.Bmp); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs new file mode 100644 index 0000000000..1f18564290 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs @@ -0,0 +1,55 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Transforms +{ + using ImageSharp.PixelFormats; + using ImageSharp.Processing; + + using Xunit; + + public class RotateTests : FileTestBase + { + public static readonly TheoryData RotateFloatValues + = new TheoryData + { + 170, + -170 + }; + + public static readonly TheoryData RotateEnumValues + = new TheoryData + { + RotateType.None, + RotateType.Rotate90, + RotateType.Rotate180, + RotateType.Rotate270 + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(RotateFloatValues), DefaultPixelType)] + public void ImageShouldRotate(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Rotate(value) + .DebugSave(provider, value, Extensions.Bmp); + } + } + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(RotateEnumValues), DefaultPixelType)] + public void ImageShouldRotateEnum(TestImageProvider provider, RotateType value) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Rotate(value) + .DebugSave(provider, value, Extensions.Bmp); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs new file mode 100644 index 0000000000..f2c2d7cbd2 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs @@ -0,0 +1,33 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Processing.Transforms +{ + using ImageSharp.PixelFormats; + + using Xunit; + + public class SkewTest : FileTestBase + { + public static readonly TheoryData SkewValues + = new TheoryData + { + { 20, 10 }, + { -20, -10 } + }; + + [Theory] + [WithFileCollection(nameof(DefaultFiles), nameof(SkewValues), DefaultPixelType)] + public void ImageShouldSkew(TestImageProvider provider, float x, float y) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Skew(x, y) + .DebugSave(provider, string.Join("_", x, y), Extensions.Bmp); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs b/tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs deleted file mode 100644 index 401ac916bd..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class AlphaTest : FileTestBase - { - public static readonly TheoryData AlphaValues - = new TheoryData - { - 20/100f , - 80/100f - }; - - [Theory] - [MemberData(nameof(AlphaValues))] - public void ImageShouldApplyAlphaFilter(float value) - { - string path = this.CreateOutputDirectory("Alpha"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.Alpha(value).Save(output); - } - } - } - - [Theory] - [MemberData(nameof(AlphaValues))] - public void ImageShouldApplyAlphaFilterInBox(float value) - { - string path = this.CreateOutputDirectory("Alpha"); - - foreach (TestFile file in Files) - { - 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); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs deleted file mode 100644 index d7ca78d1bf..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class BackgroundColorTest : FileTestBase - { - [Fact] - public void ImageShouldApplyBackgroundColorFilter() - { - string path = this.CreateOutputDirectory("BackgroundColor"); - - foreach (TestFile file in Files) - { - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) - { - 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); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/BinaryThresholdTest.cs b/tests/ImageSharp.Tests/Processors/Filters/BinaryThresholdTest.cs deleted file mode 100644 index 4e5e8a82ee..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/BinaryThresholdTest.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class BinaryThresholdTest : FileTestBase - { - public static readonly TheoryData BinaryThresholdValues - = new TheoryData - { - .25f , - .75f , - }; - - [Theory] - [MemberData(nameof(BinaryThresholdValues))] - public void ImageShouldApplyBinaryThresholdFilter(float value) - { - string path = this.CreateOutputDirectory("BinaryThreshold"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(value); - 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 deleted file mode 100644 index d10698a994..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/BlackWhiteTest.cs +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class BlackWhiteTest : FileTestBase - { - [Fact] - public void ImageShouldApplyBlackWhiteFilter() - { - string path = this.CreateOutputDirectory("BlackWhite"); - - foreach (TestFile file in Files) - { - 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 deleted file mode 100644 index 03226a9615..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/BoxBlurTest.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class BoxBlurTest : FileTestBase - { - public static readonly TheoryData BoxBlurValues - = new TheoryData - { - 3 , - 5 , - }; - - [Theory] - [MemberData(nameof(BoxBlurValues))] - public void ImageShouldApplyBoxBlurFilter(int value) - { - string path = this.CreateOutputDirectory("BoxBlur"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(value); - 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 deleted file mode 100644 index ce434d734a..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/BrightnessTest.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class BrightnessTest : FileTestBase - { - public static readonly TheoryData BrightnessValues - = new TheoryData - { - 50 , - -50 , - }; - - [Theory] - [MemberData(nameof(BrightnessValues))] - public void ImageShouldApplyBrightnessFilter(int value) - { - string path = this.CreateOutputDirectory("Brightness"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(value); - 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 deleted file mode 100644 index c287322530..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/ColorBlindnessTest.cs +++ /dev/null @@ -1,64 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using Processing; - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class ColorBlindnessTest : FileTestBase - { - public static readonly TheoryData ColorBlindnessFilters - = new TheoryData - { - ColorBlindness.Achromatomaly, - ColorBlindness.Achromatopsia, - ColorBlindness.Deuteranomaly, - ColorBlindness.Deuteranopia, - ColorBlindness.Protanomaly, - ColorBlindness.Protanopia, - ColorBlindness.Tritanomaly, - ColorBlindness.Tritanopia - }; - - [Theory] - [MemberData(nameof(ColorBlindnessFilters))] - public void ImageShouldApplyColorBlindnessFilter(ColorBlindness colorBlindness) - { - string path = this.CreateOutputDirectory("ColorBlindness"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(colorBlindness); - 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 deleted file mode 100644 index 4626fbb6e7..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/ContrastTest.cs +++ /dev/null @@ -1,56 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class ContrastTest : FileTestBase - { - public static readonly TheoryData ContrastValues - = new TheoryData - { - 50 , - -50 , - }; - - [Theory] - [MemberData(nameof(ContrastValues))] - public void ImageShouldApplyContrastFilter(int value) - { - string path = this.CreateOutputDirectory("Contrast"); - - foreach (TestFile file in Files) - { - 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 deleted file mode 100644 index 6713d0d381..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/CropTest.cs +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class CropTest : FileTestBase - { - [Fact] - public void ImageShouldApplyCropSampler() - { - string path = this.CreateOutputDirectory("Crop"); - - foreach (TestFile file in Files) - { - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) - { - image.Crop(image.Width / 2, image.Height / 2).Save(output); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processors/Filters/DetectEdgesTest.cs deleted file mode 100644 index 00440e8a5a..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/DetectEdgesTest.cs +++ /dev/null @@ -1,67 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using Processing; - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class DetectEdgesTest : FileTestBase - { - public static readonly TheoryData DetectEdgesFilters - = new TheoryData - { - EdgeDetection.Kayyali, - EdgeDetection.Kirsch, - EdgeDetection.Lapacian3X3, - EdgeDetection.Lapacian5X5, - EdgeDetection.LaplacianOfGaussian, - EdgeDetection.Prewitt, - EdgeDetection.RobertsCross, - EdgeDetection.Robinson, - EdgeDetection.Scharr, - EdgeDetection.Sobel - }; - - [Theory] - [MemberData(nameof(DetectEdgesFilters))] - public void ImageShouldApplyDetectEdgesFilter(EdgeDetection detector) - { - string path = this.CreateOutputDirectory("DetectEdges"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(detector); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.DetectEdges(detector).Save(output); - } - } - } - - [Theory] - [MemberData(nameof(DetectEdgesFilters))] - public void ImageShouldApplyDetectEdgesFilterInBox(EdgeDetection detector) - { - string path = this.CreateOutputDirectory("DetectEdges"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(detector + "-InBox"); - 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)) - .Save(output); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs b/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs deleted file mode 100644 index 066e2d134b..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/DitherTest.cs +++ /dev/null @@ -1,104 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.Dithering; - using ImageSharp.Dithering.Ordered; - using ImageSharp.PixelFormats; - - using Xunit; - - public class DitherTest : FileTestBase - { - public static readonly TheoryData Ditherers = new TheoryData - { - { "Ordered", new Ordered() }, - { "Bayer", new Bayer() } - }; - - public static readonly TheoryData ErrorDiffusers = new TheoryData - { - { "Atkinson", new Atkinson() }, - { "Burks", new Burks() }, - { "FloydSteinberg", new FloydSteinberg() }, - { "JarvisJudiceNinke", new JarvisJudiceNinke() }, - { "Sierra2", new Sierra2() }, - { "Sierra3", new Sierra3() }, - { "SierraLite", new SierraLite() }, - { "Stucki", new Stucki() }, - }; - - [Theory] - [MemberData(nameof(Ditherers))] - public void ImageShouldApplyDitherFilter(string name, IOrderedDither ditherer) - { - string path = this.CreateOutputDirectory("Dither", "Dither"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.Dither(ditherer).Save(output); - } - } - } - - [Theory] - [MemberData(nameof(Ditherers))] - public void ImageShouldApplyDitherFilterInBox(string name, IOrderedDither ditherer) - { - string path = this.CreateOutputDirectory("Dither", "Dither"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName($"{name}-InBox"); - 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); - } - } - } - - [Theory] - [MemberData(nameof(ErrorDiffusers))] - public void ImageShouldApplyDiffusionFilter(string name, IErrorDiffuser diffuser) - { - string path = this.CreateOutputDirectory("Dither", "Diffusion"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.Dither(diffuser, .5F).Save(output); - } - } - } - - [Theory] - [MemberData(nameof(ErrorDiffusers))] - public void ImageShouldApplyDiffusionFilterInBox(string name, IErrorDiffuser diffuser) - { - string path = this.CreateOutputDirectory("Dither", "Diffusion"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName($"{name}-InBox"); - 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); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/EntropyCropTest.cs b/tests/ImageSharp.Tests/Processors/Filters/EntropyCropTest.cs deleted file mode 100644 index 710e23e370..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/EntropyCropTest.cs +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class EntropyCropTest : FileTestBase - { - public static readonly TheoryData EntropyCropValues - = new TheoryData - { - .25f , - .75f , - }; - - [Theory] - [MemberData(nameof(EntropyCropValues))] - public void ImageShouldApplyEntropyCropSampler(float value) - { - string path = this.CreateOutputDirectory("EntropyCrop"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.EntropyCrop(value).Save(output); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/FlipTests.cs b/tests/ImageSharp.Tests/Processors/Filters/FlipTests.cs deleted file mode 100644 index 87f5e1025a..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/FlipTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Processing; - using Xunit; - - public class FlipTests : FileTestBase - { - public static readonly TheoryData FlipValues - = new TheoryData - { - { FlipType.None }, - { FlipType.Vertical }, - { FlipType.Horizontal }, - }; - - [Theory] - [MemberData(nameof(FlipValues))] - public void ImageShouldFlip(FlipType flipType) - { - string path = this.CreateOutputDirectory("Flip"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(flipType); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.Flip(flipType).Save(output); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs b/tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs deleted file mode 100644 index 43e45f9ca7..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/GlowTest.cs +++ /dev/null @@ -1,80 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class GlowTest : FileTestBase - { - [Fact] - public void ImageShouldApplyGlowFilter() - { - string path = this.CreateOutputDirectory("Glow"); - - foreach (TestFile file in Files) - { - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) - { - image.Glow().Save(output); - } - } - } - - [Fact] - public void ImageShouldApplyGlowFilterColor() - { - string path = this.CreateOutputDirectory("Glow"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName("Color"); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.Glow(Rgba32.HotPink).Save(output); - } - } - } - - [Fact] - public void ImageShouldApplyGlowFilterRadius() - { - string path = this.CreateOutputDirectory("Glow"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName("Radius"); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.Glow(image.Width / 4F).Save(output); - } - } - } - - [Fact] - public void ImageShouldApplyGlowFilterInBox() - { - string path = this.CreateOutputDirectory("Glow"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName("InBox"); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.Glow(new Rectangle(image.Width / 8, image.Height / 8, image.Width / 2, image.Height / 2)) - .Save(output); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/HueTest.cs b/tests/ImageSharp.Tests/Processors/Filters/HueTest.cs deleted file mode 100644 index 488433931c..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/HueTest.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class HueTest : FileTestBase - { - public static readonly TheoryData HueValues - = new TheoryData - { - 180 , - -180 , - }; - - [Theory] - [MemberData(nameof(HueValues))] - public void ImageShouldApplyHueFilter(int value) - { - string path = this.CreateOutputDirectory("Hue"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(value); - 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 deleted file mode 100644 index 6d375d09a1..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/InvertTest.cs +++ /dev/null @@ -1,46 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class InvertTest : FileTestBase - { - [Fact] - public void ImageShouldApplyInvertFilter() - { - string path = this.CreateOutputDirectory("Invert"); - foreach (TestFile file in Files) - { - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) - { - image.Invert().Save(output); - } - } - } - - [Fact] - public void ImageShouldApplyInvertFilterInBox() - { - string path = this.CreateOutputDirectory("Invert"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName("InBox"); - 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); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs b/tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs deleted file mode 100644 index 29a459f974..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class KodachromeTest : FileTestBase - { - [Fact] - public void ImageShouldApplyKodachromeFilter() - { - string path = this.CreateOutputDirectory("Kodachrome"); - - foreach (TestFile file in Files) - { - 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 deleted file mode 100644 index 6ceedecbd8..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/LomographTest.cs +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class LomographTest : FileTestBase - { - [Fact] - public void ImageShouldApplyLomographFilter() - { - string path = this.CreateOutputDirectory("Lomograph"); - - foreach (TestFile file in Files) - { - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) - { - image.Lomograph().Save(output); - } - } - } - - [Fact] - public void ImageShouldApplyLomographFilterInBox() - { - string path = this.CreateOutputDirectory("Lomograph"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName("InBox"); - 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)) - .Save(output); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/OilPaintTest.cs b/tests/ImageSharp.Tests/Processors/Filters/OilPaintTest.cs deleted file mode 100644 index 5facee346b..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/OilPaintTest.cs +++ /dev/null @@ -1,66 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System; - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class OilPaintTest : FileTestBase - { - public static readonly TheoryData> OilPaintValues - = new TheoryData> - { - new Tuple(15, 10), - new Tuple(6, 5) - }; - - [Theory] - [MemberData(nameof(OilPaintValues))] - public void ImageShouldApplyOilPaintFilter(Tuple value) - { - string path = this.CreateOutputDirectory("OilPaint"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - if (image.Width > value.Item2 && image.Height > value.Item2) - { - image.OilPaint(value.Item1, value.Item2).Save(output); - } - } - } - } - - [Theory] - [MemberData(nameof(OilPaintValues))] - public void ImageShouldApplyOilPaintFilterInBox(Tuple value) - { - string path = this.CreateOutputDirectory("OilPaint"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(value + "-InBox"); - using (Image image = file.CreateImage()) - { - if (image.Width > value.Item2 && image.Height > value.Item2) - { - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.OilPaint(value.Item1, value.Item2, new Rectangle(image.Width / 4, image.Width / 4, image.Width / 2, image.Height / 2)).Save(output); - } - } - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/PadTest.cs b/tests/ImageSharp.Tests/Processors/Filters/PadTest.cs deleted file mode 100644 index 6095410a9a..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/PadTest.cs +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class PadTest : FileTestBase - { - [Fact] - public void ImageShouldApplyPadSampler() - { - string path = this.CreateOutputDirectory("Pad"); - - foreach (TestFile file in Files) - { - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) - { - image.Pad(image.Width + 50, image.Height + 50).Save(output); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs b/tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs deleted file mode 100644 index b21a8c969c..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs +++ /dev/null @@ -1,94 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - using ImageSharp.PixelFormats; - using Xunit; - - public class PixelateTest - { - public static readonly TheoryData PixelateValues - = new TheoryData - { - 4 , - 8 - }; - - [Theory] - [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.StandardImageClass)] - public void ImageShouldApplyPixelateFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Pixelate(value) - .DebugSave(provider, new - { - size = value - }); - - using (PixelAccessor pixels = image.Lock()) - { - 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] - [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.StandardImageClass)] - public void ImageShouldApplyPixelateFilterInBox(TestImageProvider provider, int 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.Pixelate(value, rect) - .DebugSave(provider, new - { - size = value - }); - - using (PixelAccessor pixels = image.Lock()) - using (PixelAccessor sourcePixels = source.Lock()) - { - 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]); - } - } - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs b/tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs deleted file mode 100644 index df38032552..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class PolaroidTest : FileTestBase - { - [Fact] - public void ImageShouldApplyPolaroidFilter() - { - string path = this.CreateOutputDirectory("Polaroid"); - - foreach (TestFile file in Files) - { - 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/ResizeTests.cs b/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs deleted file mode 100644 index 31f4020b6f..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs +++ /dev/null @@ -1,350 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// -namespace ImageSharp.Tests -{ - using System; - using System.IO; - - using ImageSharp.PixelFormats; - - using Processing; - using Xunit; - - public class ResizeTests : FileTestBase - { - public static readonly TheoryData ReSamplers = - new TheoryData - { - { "Bicubic", new BicubicResampler() }, - { "Triangle", new TriangleResampler() }, - { "NearestNeighbor", new NearestNeighborResampler() }, - - // Perf: Enable for local testing only - // { "Box", new BoxResampler() }, - // { "Lanczos3", new Lanczos3Resampler() }, - // { "Lanczos5", new Lanczos5Resampler() }, - { "MitchellNetravali", new MitchellNetravaliResampler() }, - - // { "Lanczos8", new Lanczos8Resampler() }, - // { "Hermite", new HermiteResampler() }, - // { "Spline", new SplineResampler() }, - // { "Robidoux", new RobidouxResampler() }, - // { "RobidouxSharp", new RobidouxSharpResampler() }, - // { "RobidouxSoft", new RobidouxSoftResampler() }, - // { "Welch", new WelchResampler() } - }; - - [Theory] - [MemberData(nameof(ReSamplers))] - public void ImageShouldResize(string name, IResampler sampler) - { - string path = this.CreateOutputDirectory("Resize"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.Resize(image.Width / 2, image.Height / 2, sampler, true).Save(output); - } - } - } - - [Theory] - [MemberData(nameof(ReSamplers))] - public void ImageShouldResizeFromSourceRectangle(string name, IResampler sampler) - { - name = $"{name}-SourceRect"; - - string path = this.CreateOutputDirectory("Resize"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(name); - 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); - Rectangle destRectangle = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); - image.Resize(image.Width, image.Height, sampler, sourceRectangle, destRectangle, false).Save(output); - } - } - } - - [Theory] - [MemberData(nameof(ReSamplers))] - public void ImageShouldResizeWidthAndKeepAspect(string name, IResampler sampler) - { - name = $"{name}-FixedWidth"; - - string path = this.CreateOutputDirectory("Resize"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.Resize(image.Width / 3, 0, sampler, false).Save(output); - } - } - } - - [Theory] - [MemberData(nameof(ReSamplers))] - public void ImageShouldResizeHeightAndKeepAspect(string name, IResampler sampler) - { - name = $"{name}-FixedHeight"; - - string path = this.CreateOutputDirectory("Resize"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.Resize(0, image.Height / 3, sampler, false).Save(output); - } - } - } - - [Theory] - [MemberData(nameof(ReSamplers))] - public void ImageShouldResizeWithCropWidthMode(string name, IResampler sampler) - { - name = $"{name}-CropWidth"; - - string path = this.CreateOutputDirectory("Resize"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - ResizeOptions options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(image.Width / 2, image.Height) - }; - - image.Resize(options).Save(output); - } - } - } - - [Theory] - [MemberData(nameof(ReSamplers))] - public void ImageShouldResizeWithCropHeightMode(string name, IResampler sampler) - { - name = $"{name}-CropHeight"; - - string path = this.CreateOutputDirectory("Resize"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - ResizeOptions options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(image.Width, image.Height / 2) - }; - - image.Resize(options).Save(output); - } - } - } - - [Theory] - [MemberData(nameof(ReSamplers))] - public void ImageShouldResizeWithPadMode(string name, IResampler sampler) - { - name = $"{name}-Pad"; - - string path = this.CreateOutputDirectory("Resize"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - ResizeOptions options = new ResizeOptions - { - Size = new Size(image.Width + 200, image.Height), - Mode = ResizeMode.Pad - }; - - image.Resize(options).Save(output); - } - } - } - - [Theory] - [MemberData(nameof(ReSamplers))] - public void ImageShouldResizeWithBoxPadMode(string name, IResampler sampler) - { - name = $"{name}-BoxPad"; - - string path = this.CreateOutputDirectory("Resize"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - ResizeOptions options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(image.Width + 200, image.Height + 200), - Mode = ResizeMode.BoxPad - }; - - image.Resize(options).Save(output); - } - } - } - - [Theory] - [MemberData(nameof(ReSamplers))] - public void ImageShouldResizeWithMaxMode(string name, IResampler sampler) - { - name = $"{name}Max"; - - string path = this.CreateOutputDirectory("Resize"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - ResizeOptions options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(300, 300), - Mode = ResizeMode.Max - }; - - image.Resize(options).Save(output); - } - } - } - - [Theory] - [MemberData(nameof(ReSamplers))] - public void ImageShouldResizeWithMinMode(string name, IResampler sampler) - { - name = $"{name}-Min"; - - string path = this.CreateOutputDirectory("Resize"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - ResizeOptions options = new ResizeOptions - { - Sampler = sampler, - Size = new Size((int)Math.Round(image.Width * .75F), (int)Math.Round(image.Height * .95F)), - Mode = ResizeMode.Min - }; - - image.Resize(options).Save(output); - } - } - } - - [Theory] - [MemberData(nameof(ReSamplers))] - public void ImageShouldResizeWithStretchMode(string name, IResampler sampler) - { - name = $"{name}Stretch"; - - string path = this.CreateOutputDirectory("Resize"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(name); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - ResizeOptions options = new ResizeOptions - { - Sampler = sampler, - Size = new Size(image.Width / 2, image.Height), - Mode = ResizeMode.Stretch - }; - - image.Resize(options).Save(output); - } - } - } - - [Theory] - [InlineData(-2, 0)] - [InlineData(-1, 0)] - [InlineData(0, 1)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public static void BicubicWindowOscillatesCorrectly(float x, float expected) - { - BicubicResampler sampler = new BicubicResampler(); - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - - [Theory] - [InlineData(-2, 0)] - [InlineData(-1, 0)] - [InlineData(0, 1)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public static void TriangleWindowOscillatesCorrectly(float x, float expected) - { - TriangleResampler sampler = new TriangleResampler(); - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - - [Theory] - [InlineData(-2, 0)] - [InlineData(-1, 0)] - [InlineData(0, 1)] - [InlineData(1, 0)] - [InlineData(2, 0)] - public static void Lanczos3WindowOscillatesCorrectly(float x, float expected) - { - Lanczos3Resampler sampler = new Lanczos3Resampler(); - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - - [Theory] - [InlineData(-4, 0)] - [InlineData(-2, 0)] - [InlineData(0, 1)] - [InlineData(2, 0)] - [InlineData(4, 0)] - public static void Lanczos5WindowOscillatesCorrectly(float x, float expected) - { - Lanczos5Resampler sampler = new Lanczos5Resampler(); - float result = sampler.GetValue(x); - - Assert.Equal(result, expected); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/RotateFlipTest.cs b/tests/ImageSharp.Tests/Processors/Filters/RotateFlipTest.cs deleted file mode 100644 index c4c4fa8d7a..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/RotateFlipTest.cs +++ /dev/null @@ -1,44 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Processing; - using Xunit; - - public class RotateFlipTest : FileTestBase - { - public static readonly TheoryData RotateFlipValues - = new TheoryData - { - { RotateType.None, FlipType.Vertical }, - { RotateType.None, FlipType.Horizontal }, - { RotateType.Rotate90, FlipType.None }, - { RotateType.Rotate180, FlipType.None }, - { RotateType.Rotate270, FlipType.None }, - }; - - [Theory] - [MemberData(nameof(RotateFlipValues))] - public void ImageShouldRotateFlip(RotateType rotateType, FlipType flipType) - { - string path = this.CreateOutputDirectory("RotateFlip"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(rotateType + "-" + flipType); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.RotateFlip(rotateType, flipType).Save(output); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/RotateTest.cs b/tests/ImageSharp.Tests/Processors/Filters/RotateTest.cs deleted file mode 100644 index b30c795ad6..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/RotateTest.cs +++ /dev/null @@ -1,67 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Processing; - using Xunit; - - public class RotateTest : FileTestBase - { - public static readonly TheoryData RotateFloatValues - = new TheoryData - { - 170 , - -170 , - }; - - public static readonly TheoryData RotateEnumValues - = new TheoryData - { - RotateType.None, - RotateType.Rotate90, - RotateType.Rotate180, - RotateType.Rotate270 - }; - - [Theory] - [MemberData(nameof(RotateFloatValues))] - public void ImageShouldApplyRotateSampler(float value) - { - string path = this.CreateOutputDirectory("Rotate"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.Rotate(value).Save(output); - } - } - } - - [Theory] - [MemberData(nameof(RotateEnumValues))] - public void ImageShouldApplyRotateSampler(RotateType value) - { - string path = this.CreateOutputDirectory("Rotate"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.Rotate(value).Save(output); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/SaturationTest.cs b/tests/ImageSharp.Tests/Processors/Filters/SaturationTest.cs deleted file mode 100644 index e28847fa3b..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/SaturationTest.cs +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class SaturationTest : FileTestBase - { - public static readonly TheoryData SaturationValues - = new TheoryData - { - 50 , - -50 , - }; - - [Theory] - [MemberData(nameof(SaturationValues))] - public void ImageShouldApplySaturationFilter(int value) - { - string path = CreateOutputDirectory("Saturation"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName(value); - 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 deleted file mode 100644 index 490213ce7b..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/SepiaTest.cs +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class SepiaTest : FileTestBase - { - [Fact] - public void ImageShouldApplySepiaFilter() - { - string path = CreateOutputDirectory("Sepia"); - - foreach (TestFile file in Files) - { - 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 deleted file mode 100644 index d096b3913e..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/SkewTest.cs +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class SkewTest : FileTestBase - { - public static readonly TheoryData SkewValues - = new TheoryData - { - { 20, 10 }, - { -20, -10 } - }; - - [Theory] - [MemberData(nameof(SkewValues))] - public void ImageShouldApplySkewSampler(float x, float y) - { - string path = this.CreateOutputDirectory("Skew"); - - // Matches live example - // http://www.w3schools.com/css/tryit.asp?filename=trycss3_transform_skew - foreach (TestFile file in Files) - { - string filename = file.GetFileName(x + "-" + y); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.Skew(x, y).Save(output); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/VignetteTest.cs b/tests/ImageSharp.Tests/Processors/Filters/VignetteTest.cs deleted file mode 100644 index 4191ae8fa0..0000000000 --- a/tests/ImageSharp.Tests/Processors/Filters/VignetteTest.cs +++ /dev/null @@ -1,80 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Tests -{ - using System.IO; - - using ImageSharp.PixelFormats; - - using Xunit; - - public class VignetteTest : FileTestBase - { - [Fact] - public void ImageShouldApplyVignetteFilter() - { - string path = this.CreateOutputDirectory("Vignette"); - - foreach (TestFile file in Files) - { - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) - { - image.Vignette().Save(output); - } - } - } - - [Fact] - public void ImageShouldApplyVignetteFilterColor() - { - string path = this.CreateOutputDirectory("Vignette"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName("Color"); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.Vignette(Rgba32.HotPink).Save(output); - } - } - } - - [Fact] - public void ImageShouldApplyVignetteFilterRadius() - { - string path = this.CreateOutputDirectory("Vignette"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName("Radius"); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.Vignette(image.Width / 4F, image.Height / 4F).Save(output); - } - } - } - - [Fact] - public void ImageShouldApplyVignetteFilterInBox() - { - string path = this.CreateOutputDirectory("Vignette"); - - foreach (TestFile file in Files) - { - string filename = file.GetFileName("InBox"); - 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)) - .Save(output); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 44c8c34ee3..471d61abb0 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -36,9 +36,20 @@ namespace ImageSharp.Tests // Filter changing per scanline public const string FilterVar = "Png/filterVar.png"; - // Odd chunk lengths - public const string ChunkLength1 = "Png/chunklength1.png"; - public const string ChunkLength2 = "Png/chunklength2.png"; + public static class Bad + { + // Odd chunk lengths + public const string ChunkLength1 = "Png/chunklength1.png"; + public const string ChunkLength2 = "Png/chunklength2.png"; + } + + public static readonly string[] All = + { + P1, Pd, Blur, Splash, Cross, + Powerpoint, SplashInterlaced, Interlaced, + Filter0, Filter1, Filter2, Filter3, Filter4, + FilterVar + }; } public static class Jpeg @@ -105,6 +116,8 @@ namespace ImageSharp.Tests public const string Giphy = "Gif/giphy.gif"; public const string Cheers = "Gif/cheers.gif"; public const string Trans = "Gif/trans.gif"; + + public static readonly string[] All = { Rings, Giphy, Cheers, Trans }; } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs index 379ce3d054..05e8c61360 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs @@ -21,25 +21,40 @@ namespace ImageSharp.Tests protected readonly PixelTypes PixelTypes; + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// protected ImageDataAttributeBase(string memberName, PixelTypes pixelTypes, object[] additionalParameters) { this.PixelTypes = pixelTypes; this.AdditionalParameters = additionalParameters; this.MemberName = memberName; - } - public string MemberName { get; private set; } + /// + /// Gets the member name + /// + public string MemberName { get; } + /// + /// Gets the member type + /// public Type MemberType { get; set; } + /// Returns the data to be used to test the theory. + /// The method that is being tested + /// One or more sets of theory data. Each invocation of the test method + /// is represented by a single object array. public override IEnumerable GetData(MethodInfo testMethod) { - IEnumerable addedRows = Enumerable.Empty(); + IEnumerable addedRows = Enumerable.Empty().ToArray(); if (!string.IsNullOrWhiteSpace(this.MemberName)) { Type type = this.MemberType ?? testMethod.DeclaringType; - Func accessor = GetPropertyAccessor(type) ?? GetFieldAccessor(type);// ?? GetMethodAccessor(type); + Func accessor = this.GetPropertyAccessor(type) ?? this.GetFieldAccessor(type); if (accessor != null) { @@ -49,7 +64,7 @@ namespace ImageSharp.Tests 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[]"); + throw new ArgumentException($"Property {this.MemberName} on {this.MemberType ?? testMethod.DeclaringType} yielded an item that is not an object[]"); } } } @@ -60,18 +75,20 @@ namespace ImageSharp.Tests addedRows = new[] { new object[0] }; } - bool firstIsprovider = FirstIsProvider(testMethod); - IEnumerable dataItems = Enumerable.Empty(); + bool firstIsprovider = this.FirstIsProvider(testMethod); if (firstIsprovider) { - return InnerGetData(testMethod, addedRows); - } - else - { - return addedRows.Select(x => x.Concat(this.AdditionalParameters).ToArray()); + return this.InnerGetData(testMethod, addedRows); } + + return addedRows.Select(x => x.Concat(this.AdditionalParameters).ToArray()); } + /// + /// Returns a value indicating whether the first parameter of the method is a test provider. + /// + /// + /// private bool FirstIsProvider(MethodInfo testMethod) { TypeInfo dataType = testMethod.GetParameters().First().ParameterType.GetTypeInfo(); @@ -106,25 +123,45 @@ namespace ImageSharp.Tests } } + /// + /// Generates the collection of method arguments from the given test as a generic enumerable. + /// + /// The test method + /// The test image provider factory type + /// The protected virtual IEnumerable GetAllFactoryMethodArgs(MethodInfo testMethod, Type factoryType) { object[] args = this.GetFactoryMethodArgs(testMethod, factoryType); return Enumerable.Repeat(args, 1); } + /// + /// Generates the collection of method arguments from the given test. + /// + /// The test method + /// The test image provider factory type + /// The protected virtual object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) { throw new InvalidOperationException("Semi-abstract method"); } + /// + /// Generates the method name from the given test method. + /// + /// The test method + /// The protected abstract string GetFactoryMethodName(MethodInfo testMethod); + /// + /// Gets the field accessor for the given type. + /// Func GetFieldAccessor(Type type) { FieldInfo fieldInfo = null; for (Type reflectionType = type; reflectionType != null; reflectionType = reflectionType.GetTypeInfo().BaseType) { - fieldInfo = reflectionType.GetRuntimeField(MemberName); + fieldInfo = reflectionType.GetRuntimeField(this.MemberName); if (fieldInfo != null) break; } @@ -135,39 +172,27 @@ namespace ImageSharp.Tests 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); - //} - + /// + /// Gets the property accessor for the given type. + /// Func GetPropertyAccessor(Type type) { PropertyInfo propInfo = null; for (Type reflectionType = type; reflectionType != null; reflectionType = reflectionType.GetTypeInfo().BaseType) { - propInfo = reflectionType.GetRuntimeProperty(MemberName); + propInfo = reflectionType.GetRuntimeProperty(this.MemberName); if (propInfo != null) + { break; + } } - if (propInfo == null || propInfo.GetMethod == null || !propInfo.GetMethod.IsStatic) + if (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 32278d7558..72be5abc29 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs @@ -31,6 +31,7 @@ namespace ImageSharp.Tests /// /// Triggers passing an that produces a blank image of size width * height /// + /// The member data /// The required width /// The required height /// The requested parameter diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs index df8f8d0909..1b37c45a92 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs @@ -16,55 +16,62 @@ namespace ImageSharp.Tests /// public class WithFileCollectionAttribute : ImageDataAttributeBase { - private readonly string enumeratorMemberName; + private readonly string fileEnumeratorMemberName; /// /// 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 name of the static test class field/property enumerating the files /// The requested pixel types /// Additional theory parameter values public WithFileCollectionAttribute( - string enumeratorMemberName, + string fileEnumeratorMemberName, PixelTypes pixelTypes, params object[] additionalParameters) : base(null, pixelTypes, additionalParameters) { - this.enumeratorMemberName = enumeratorMemberName; + this.fileEnumeratorMemberName = fileEnumeratorMemberName; } - /// + /// /// 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 member name for enumerating method parameters /// The requested pixel types /// Additional theory parameter values public WithFileCollectionAttribute( string enumeratorMemberName, - string DataMemberName, + string memberName, PixelTypes pixelTypes, params object[] additionalParameters) - : base(DataMemberName, pixelTypes, additionalParameters) + : base(memberName, pixelTypes, additionalParameters) { - this.enumeratorMemberName = enumeratorMemberName; + this.fileEnumeratorMemberName = enumeratorMemberName; } + /// + /// Generates the collection of method arguments from the given test. + /// + /// The test method + /// The test image provider factory type + /// The protected override IEnumerable GetAllFactoryMethodArgs(MethodInfo testMethod, Type factoryType) { Func accessor = this.GetPropertyAccessor(testMethod.DeclaringType); - accessor = accessor ?? this.GetFieldAccessor(testMethod.DeclaringType); - IEnumerable files = (IEnumerable)accessor(); + var files = (IEnumerable)accessor(); return files.Select(f => new object[] { f }); } + /// protected override string GetFactoryMethodName(MethodInfo testMethod) => "File"; /// - /// Based on MemberData implementation + /// Gets the field accessor for the given type. /// private Func GetFieldAccessor(Type type) { @@ -73,17 +80,23 @@ namespace ImageSharp.Tests reflectionType != null; reflectionType = reflectionType.GetTypeInfo().BaseType) { - fieldInfo = reflectionType.GetRuntimeField(this.enumeratorMemberName); - if (fieldInfo != null) break; + fieldInfo = reflectionType.GetRuntimeField(this.fileEnumeratorMemberName); + if (fieldInfo != null) + { + break; + } } - if (fieldInfo == null || !fieldInfo.IsStatic) return null; + if (fieldInfo == null || !fieldInfo.IsStatic) + { + return null; + } return () => fieldInfo.GetValue(null); } /// - /// Based on MemberData implementation + /// Gets the property accessor for the given type. /// private Func GetPropertyAccessor(Type type) { @@ -92,11 +105,14 @@ namespace ImageSharp.Tests reflectionType != null; reflectionType = reflectionType.GetTypeInfo().BaseType) { - propInfo = reflectionType.GetRuntimeProperty(this.enumeratorMemberName); + propInfo = reflectionType.GetRuntimeProperty(this.fileEnumeratorMemberName); if (propInfo != null) break; } - if (propInfo == null || propInfo.GetMethod == null || !propInfo.GetMethod.IsStatic) return null; + if (propInfo?.GetMethod == null || !propInfo.GetMethod.IsStatic) + { + return null; + } return () => propInfo.GetValue(null, null); } diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs index 77c60a9433..f6ab65f714 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs @@ -29,6 +29,7 @@ namespace ImageSharp.Tests /// /// Triggers passing an that produces a test pattern image of size width * height /// + /// The member data to apply to theories /// The required width /// The required height /// The requested parameter @@ -40,7 +41,14 @@ namespace ImageSharp.Tests this.Height = height; } + /// + /// Gets the width + /// public int Width { get; } + + /// + /// Gets the height + /// public int Height { get; } protected override string GetFactoryMethodName(MethodInfo testMethod) => "TestPattern"; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index 4217d52b06..16eecd2fc7 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -17,8 +17,8 @@ namespace ImageSharp.Tests { private class FileProvider : TestImageProvider, IXunitSerializable { - // Need PixelTypes in the dictionary key, because result images of TestImageProvider.FileProvider - // are shared between PixelTypes.Color & PixelTypes.StandardImageClass + // Need PixelTypes in the dictionary key, because result images of TestImageProvider.FileProvider + // are shared between PixelTypes.Color & PixelTypes.Rgba32 private class Key : Tuple { public Key(PixelTypes item1, string item2) @@ -27,8 +27,7 @@ namespace ImageSharp.Tests } } - private static ConcurrentDictionary> cache = - new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary> cache = new ConcurrentDictionary>(); private string filePath; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs index 65d55da554..0caded4201 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs @@ -53,11 +53,11 @@ namespace ImageSharp.Tests { Image image = base.GetImage(); TPixel color = default(TPixel); - color.PackFromBytes(this.r, this.g, this.b, this.a); + color.PackFromRgba32(new Rgba32(this.r, this.g, this.b, this.a)); return image.Fill(color); } - + public override void Serialize(IXunitSerializationInfo info) { info.AddValue("red", this.r); diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index bf30d48474..03a685d34f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -107,12 +107,12 @@ namespace ImageSharp.Tests this.TypeName = typeName; this.MethodName = methodName; - if (pixelTypeOverride == PixelTypes.StandardImageClass) + if (pixelTypeOverride == PixelTypes.Rgba32) { this.Factory = new ImageFactory() as GenericFactory; } - this.Utility = new ImagingTestCaseUtility() + this.Utility = new ImagingTestCaseUtility { SourceFileOrDescription = this.SourceFileOrDescription, PixelTypeName = this.PixelType.ToString() diff --git a/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs b/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs index 1dadc4884c..1a6bf5d8b8 100644 --- a/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs +++ b/tests/ImageSharp.Tests/TestUtilities/MeasureFixture.cs @@ -1,4 +1,9 @@ -namespace ImageSharp.Tests +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests { using System; using System.Diagnostics; @@ -24,8 +29,12 @@ /// The name of the operation to print to the output public void Measure(int times, Action action, [CallerMemberName] string operationName = null) { - if (this.EnablePrinting) this.Output?.WriteLine($"{operationName} X {times} ..."); - Stopwatch sw = Stopwatch.StartNew(); + if (this.EnablePrinting) + { + this.Output?.WriteLine($"{operationName} X {times} ..."); + } + + var sw = Stopwatch.StartNew(); for (int i = 0; i < times; i++) { @@ -33,7 +42,10 @@ } sw.Stop(); - if (this.EnablePrinting) this.Output?.WriteLine($"{operationName} finished in {sw.ElapsedMilliseconds} ms"); + if (this.EnablePrinting) + { + this.Output?.WriteLine($"{operationName} finished in {sw.ElapsedMilliseconds} ms"); + } } /// diff --git a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs index f64b772717..645a4dc596 100644 --- a/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs +++ b/tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs @@ -52,10 +52,11 @@ namespace ImageSharp.Tests Short4 = 1 << 17, - /// - /// Triggers instantiating the subclass of - /// - StandardImageClass = 1 << 29, + Rgb24 = 18, + + Bgr24 = 19, + + Bgra32 = 20, // TODO: Add multi-flag entries by rules defined in PackedPixelConverterHelper diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index dbd316423a..bb97daaa47 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -1,35 +1,61 @@ - +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + 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, ITestImageProvider provider, object settings = null, string extension = "png") - where TPixel : struct, IPixel + /// + /// Saves the image only when not running in the CI server. + /// + /// The pixel format + /// The image + /// The image provider + /// The settings + /// The extension + public static Image DebugSave(this Image image, ITestImageProvider provider, object settings = null, string extension = "png") + where TPixel : struct, IPixel { + if (bool.TryParse(Environment.GetEnvironmentVariable("CI"), out bool isCi) && isCi) + { + return image; + } + + // We are running locally then we want to save it out string tag = null; - if (settings is string) + string s = settings as string; + + if (s != null) { - tag = (string)settings; + tag = s; } else if (settings != null) { - var properties = settings.GetType().GetRuntimeProperties(); + Type type = settings.GetType(); + TypeInfo info = type.GetTypeInfo(); + if (info.IsPrimitive || info.IsEnum || type == typeof(decimal)) + { + tag = settings.ToString(); + } + else + { + IEnumerable 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, tag: tag); + tag = string.Join("_", properties.ToDictionary(x => x.Name, x => x.GetValue(settings)).Select(x => $"{x.Key}-{x.Value}")); + } } + + provider.Utility.SaveTestOutputFile(image, extension, tag: tag); + return image; } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs index dfaf1c0528..e42cbe1934 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtilityExtensions.cs @@ -38,14 +38,13 @@ namespace ImageSharp.Tests // 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)) + foreach (PixelTypes pt in AllConcretePixelTypes.Where(pt => pt != PixelTypes.Rgba32)) { string typeName = $"{nameSpace}.{pt}"; Type t = ImageSharpAssembly.GetType(typeName); PixelTypes2ClrTypes[pt] = t ?? throw new InvalidOperationException($"Could not find: {typeName}"); ClrTypes2PixelTypes[t] = pt; } - PixelTypes2ClrTypes[PixelTypes.StandardImageClass] = defaultPixelFormatType; } public static bool HasFlag(this PixelTypes pixelTypes, PixelTypes flag) => (pixelTypes & flag) == flag; @@ -106,6 +105,11 @@ namespace ImageSharp.Tests public static Type ToType(this PixelTypes pixelType) => PixelTypes2ClrTypes[pixelType]; + /// + /// Returns the enumerations for the given type. + /// + /// + /// public static PixelTypes GetPixelType(this Type colorStructClrType) => ClrTypes2PixelTypes[colorStructClrType]; public static IEnumerable> ExpandAllTypes(this PixelTypes pixelTypes) diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 4d3a0d9912..c01babb632 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -50,7 +50,7 @@ namespace ImageSharp.Tests [Theory] [WithBlankImages(1, 1, PixelTypes.Rgba32, PixelTypes.Rgba32)] [WithBlankImages(1, 1, PixelTypes.Alpha8, PixelTypes.Alpha8)] - [WithBlankImages(1, 1, PixelTypes.StandardImageClass, PixelTypes.StandardImageClass)] + [WithBlankImages(1, 1, PixelTypes.Rgba32, PixelTypes.Rgba32)] public void PixelType_PropertyValueIsCorrect(TestImageProvider provider, PixelTypes expected) where TPixel : struct, IPixel { @@ -58,8 +58,8 @@ namespace ImageSharp.Tests } [Theory] - [WithBlankImages(1, 1, PixelTypes.StandardImageClass)] - [WithFile(TestImages.Bmp.F, PixelTypes.StandardImageClass)] + [WithBlankImages(1, 1, PixelTypes.Rgba32)] + [WithFile(TestImages.Bmp.F, PixelTypes.Rgba32)] public void PixelTypes_ColorWithDefaultImageClass_TriggersCreatingTheNonGenericDerivedImageClass( TestImageProvider provider) where TPixel : struct, IPixel @@ -103,8 +103,8 @@ namespace ImageSharp.Tests where TPixel : struct, IPixel { Image img = provider.GetImage(); - Assert.Equal(img.Width, 10); - Assert.Equal(img.Height, 20); + Assert.Equal(10, img.Width); + Assert.Equal(20, img.Height); byte[] colors = new byte[4]; @@ -116,10 +116,10 @@ namespace ImageSharp.Tests { pixels[x, y].ToXyzwBytes(colors, 0); - Assert.Equal(colors[0], 255); - Assert.Equal(colors[1], 100); - Assert.Equal(colors[2], 50); - Assert.Equal(colors[3], 200); + Assert.Equal(255, colors[0]); + Assert.Equal(100, colors[1]); + Assert.Equal(50, colors[2]); + Assert.Equal(200, colors[3]); } } } @@ -143,8 +143,8 @@ namespace ImageSharp.Tests where TPixel : struct, IPixel { Image img = provider.GetImage(); - Assert.Equal(img.Width, 3); - if (provider.PixelType == PixelTypes.StandardImageClass) + Assert.Equal(3, img.Width); + if (provider.PixelType == PixelTypes.Rgba32) { Assert.IsType>(img); } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs index 9ff0ca64e7..437c295b9c 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs @@ -89,7 +89,7 @@ namespace ImageSharp.Tests [InlineData(PixelTypes.Rgba32, typeof(Rgba32))] [InlineData(PixelTypes.Argb32, typeof(Argb32))] [InlineData(PixelTypes.HalfVector4, typeof(HalfVector4))] - [InlineData(PixelTypes.StandardImageClass, typeof(Rgba32))] + [InlineData(PixelTypes.Rgba32, typeof(Rgba32))] public void ToType(PixelTypes pt, Type expectedType) { Assert.Equal(pt.ToType(), expectedType); @@ -114,17 +114,16 @@ namespace ImageSharp.Tests [Fact] public void ToTypes() { - PixelTypes pixelTypes = PixelTypes.Alpha8 | PixelTypes.Bgr565 | PixelTypes.Rgba32 | PixelTypes.HalfVector2 | PixelTypes.StandardImageClass; + PixelTypes pixelTypes = PixelTypes.Alpha8 | PixelTypes.Bgr565 | PixelTypes.HalfVector2 | PixelTypes.Rgba32; IEnumerable> expanded = pixelTypes.ExpandAllTypes(); - Assert.Equal(expanded.Count(), 5); + Assert.Equal(4, expanded.Count()); AssertContainsPixelType(PixelTypes.Alpha8, expanded); AssertContainsPixelType(PixelTypes.Bgr565, expanded); - AssertContainsPixelType(PixelTypes.Rgba32, expanded); AssertContainsPixelType(PixelTypes.HalfVector2, expanded); - AssertContainsPixelType(PixelTypes.StandardImageClass, expanded); + AssertContainsPixelType(PixelTypes.Rgba32, expanded); } [Fact] @@ -134,7 +133,7 @@ namespace ImageSharp.Tests Assert.True(expanded.Length >= TestUtilityExtensions.GetAllPixelTypes().Length - 2); AssertContainsPixelType(PixelTypes.Rgba32, expanded); - AssertContainsPixelType(PixelTypes.StandardImageClass, expanded); + AssertContainsPixelType(PixelTypes.Rgba32, expanded); } } }