diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 188c11ff31..01c09d2231 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -20,6 +20,11 @@ * Do not open an issue on GitHub until you have collected positive feedback about the change. GitHub issues are primarily intended for bug reports and fixes. +#### **Running tests and Debugging** + +* Expected test output is pulled in as a submodule from the [ImageSharp.Tests.Images repository](https://github.com/SixLabors/Imagesharp.Tests.Images/tree/master/ReferenceOutput). To succesfully run tests, make sure that you have updated the submodules! +* Debugging (running tests in Debug mode) is only supported on .NET Core 2.1, because of JIT Code Generation bugs like [dotnet/coreclr#16443](https://github.com/dotnet/coreclr/issues/16443) or [dotnet/coreclr#20657](https://github.com/dotnet/coreclr/issues/20657) + #### **Do you have questions about consuming the library or the source code?** * Ask any question about how to use ImageSharp in the [ImageSharp Gitter Chat Room](https://gitter.im/ImageSharp/General). diff --git a/README.md b/README.md index 66dc0dcf43..a72074f8f3 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Designed to democratize image processing, ImageSharp brings you an incredibly po Compared to `System.Drawing` we have been able to develop something much more flexible, easier to code against, and much, much less prone to memory leaks. Gone are system-wide process-locks; ImageSharp images are thread-safe and fully supported in web environments. -Built against .Net Standard 1.1 ImageSharp can be used in device, cloud, and embedded/IoT scenarios. +Built against .NET Standard 1.3 ImageSharp can be used in device, cloud, and embedded/IoT scenarios. ### Documentation For all SixLabors projects, including ImageSharp: @@ -83,24 +83,6 @@ using (Image image = Image.Load("foo.jpg")) image.Save("bar.jpg"); // Automatic encoder selected based on extension. } ``` -On netstandard 1.1 - 1.2 - -```csharp -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Processing; - -// Image.Load(Stream stream) is a shortcut for our default type. -// Other pixel formats use Image.Load(Stream stream)) -using (FileStream stream = File.OpenRead("foo.jpg")) -using (FileStream output = File.OpenWrite("bar.jpg")) -using (Image image = Image.Load(stream)) -{ - image.Mutate(x => x - .Resize(image.Width / 2, image.Height / 2) - .Grayscale()); - image.Save(output); -} -``` Setting individual pixel values can be performed as follows: @@ -133,7 +115,7 @@ If you prefer, you can compile ImageSharp yourself (please do and help!) Alternatively, you can work from command line and/or with a lightweight editor on **both Linux/Unix and Windows**: - [Visual Studio Code](https://code.visualstudio.com/) with [C# Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp) -- [.Net Core](https://www.microsoft.com/net/core#linuxubuntu) +- [.NET Core](https://www.microsoft.com/net/core#linuxubuntu) To clone ImageSharp locally click the "Clone in Windows" button above or run the following git commands. diff --git a/appveyor.yml b/appveyor.yml index 821fd427c8..2cc5182d39 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,10 +12,10 @@ environment: - target_framework: netcoreapp2.1 is_32bit: True - - target_framework: net471 + - target_framework: net472 is_32bit: False - - target_framework: net471 + - target_framework: net472 is_32bit: True - target_framework: net462 diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index 42ef080e53..ae5069be1d 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -5,8 +5,8 @@ $(packageversion) 0.0.1 SixLabors and contributors - netstandard1.1;netstandard2.0 - 7.2 + netstandard1.3;netstandard2.0 + 7.3 true true SixLabors.ImageSharp.Drawing @@ -38,8 +38,8 @@ - - + + All diff --git a/src/ImageSharp.Drawing/Processing/BrushApplicator.cs b/src/ImageSharp.Drawing/Processing/BrushApplicator.cs index 770d440656..0c6e0d3b40 100644 --- a/src/ImageSharp.Drawing/Processing/BrushApplicator.cs +++ b/src/ImageSharp.Drawing/Processing/BrushApplicator.cs @@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Processing } Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan); + this.Blender.Blend(this.Target.Configuration, destinationRow, destinationRow, overlaySpan, amountSpan); } } } diff --git a/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs b/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs index 897b3f384f..00141a8d8d 100644 --- a/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/GradientBrushBase{TPixel}.cs @@ -130,7 +130,7 @@ namespace SixLabors.ImageSharp.Processing onLocalGradient); TPixel resultColor = default; - resultColor.PackFromVector4(result); + resultColor.FromVector4(result); return resultColor; } } diff --git a/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs index c3f81868be..1ef4bb9ec9 100644 --- a/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/ImageBrush{TPixel}.cs @@ -140,7 +140,12 @@ namespace SixLabors.ImageSharp.Processing } Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend(this.source.MemoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan); + this.Blender.Blend( + this.source.Configuration, + destinationRow, + destinationRow, + overlaySpan, + amountSpan); } } } diff --git a/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs index 2ce9a7ce57..46ed36f687 100644 --- a/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/PatternBrush{TPixel}.cs @@ -170,7 +170,12 @@ namespace SixLabors.ImageSharp.Processing } Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan); + this.Blender.Blend( + this.Target.Configuration, + destinationRow, + destinationRow, + overlaySpan, + amountSpan); } } } diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs index 4e6018e07a..0957904c62 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs @@ -1,95 +1,99 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing.Processors.Drawing -{ - /// - /// Combines two images together by blending the pixels. - /// +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Drawing +{ + /// + /// Combines two images together by blending the pixels. + /// /// The pixel format of destination image. - /// The pixel format os source image. - internal class DrawImageProcessor : ImageProcessor + /// The pixel format of source image. + internal class DrawImageProcessor : ImageProcessor where TPixelDst : struct, IPixel - where TPixelSrc : struct, IPixel + where TPixelSrc : struct, IPixel { - /// - /// Initializes a new instance of the class. - /// - /// The image to blend with the currently processing image. + /// + /// Initializes a new instance of the class. + /// + /// The image to blend with the currently processing image. /// The location to draw the blended image. /// The blending mode to use when drawing the image. /// The Alpha blending mode to use when drawing the image. - /// The opacity of the image to blend. Must be between 0 and 1. - public DrawImageProcessor(Image image, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity) - { - Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); - - this.Image = image; - this.Opacity = opacity; - this.Blender = PixelOperations.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode); - this.Location = location; - } - - /// - /// Gets the image to blend - /// - public Image Image { get; } - - /// - /// Gets the opacity of the image to blend - /// - public float Opacity { get; } - - /// - /// Gets the pixel blender - /// - public PixelBlender Blender { get; } - - /// - /// Gets the location to draw the blended image - /// - public Point Location { get; } - - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - Image targetImage = this.Image; - PixelBlender blender = this.Blender; - int locationY = this.Location.Y; - - // Align start/end positions. - Rectangle bounds = targetImage.Bounds(); - - int minX = Math.Max(this.Location.X, sourceRectangle.X); - int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); - int targetX = minX - this.Location.X; - - int minY = Math.Max(this.Location.Y, sourceRectangle.Y); - int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); - - int width = maxX - minX; - - MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator; + /// The opacity of the image to blend. Must be between 0 and 1. + public DrawImageProcessor(Image image, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity) + { + Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); + + this.Image = image; + this.Opacity = opacity; + this.Blender = PixelOperations.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode); + this.Location = location; + } + + /// + /// Gets the image to blend + /// + public Image Image { get; } + + /// + /// Gets the opacity of the image to blend + /// + public float Opacity { get; } + + /// + /// Gets the pixel blender + /// + public PixelBlender Blender { get; } + + /// + /// Gets the location to draw the blended image + /// + public Point Location { get; } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + Image targetImage = this.Image; + PixelBlender blender = this.Blender; + int locationY = this.Location.Y; + + // Align start/end positions. + Rectangle bounds = targetImage.Bounds(); + + int minX = Math.Max(this.Location.X, sourceRectangle.X); + int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); + int targetX = minX - this.Location.X; + + int minY = Math.Max(this.Location.Y, sourceRectangle.Y); + int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); + + int width = maxX - minX; + + var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); - ParallelFor.WithConfiguration( - minY, - maxY, - configuration, - y => - { - Span background = source.GetPixelRowSpan(y).Slice(minX, width); - Span foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); - blender.Blend(memoryAllocator, background, background, foreground, this.Opacity); - }); - } - } + ParallelHelper.IterateRows( + workingRect, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span background = source.GetPixelRowSpan(y).Slice(minX, width); + Span foreground = + targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); + blender.Blend(configuration, background, background, foreground, this.Opacity); + } + }); + } + } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs index 3285e75a7b..ed6c869511 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs @@ -1,107 +1,116 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing.Processors.Drawing -{ - /// - /// Using the brush as a source of pixels colors blends the brush color with source. - /// - /// The pixel format. - internal class FillProcessor : ImageProcessor - where TPixel : struct, IPixel - { - /// - /// The brush. - /// - private readonly IBrush brush; - private readonly GraphicsOptions options; - - /// - /// Initializes a new instance of the class. - /// - /// The brush to source pixel colors from. - /// The options - public FillProcessor(IBrush brush, GraphicsOptions options) - { - this.brush = brush; - this.options = options; - } - - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - int width = maxX - minX; - - // If there's no reason for blending, then avoid it. - if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush)) - { - ParallelFor.WithConfiguration( - minY, - maxY, - configuration, - y => - { - source.GetPixelRowSpan(y).Slice(minX, width).Fill(solidBrush.Color); - }); - } - else - { - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } - - using (IMemoryOwner amount = source.MemoryAllocator.Allocate(width)) - using (BrushApplicator applicator = this.brush.CreateApplicator( - source, - sourceRectangle, - this.options)) - { - amount.GetSpan().Fill(1f); - - ParallelFor.WithConfiguration( - minY, - maxY, - configuration, - y => - { - int offsetY = y - startY; - int offsetX = minX - startX; - - applicator.Apply(amount.GetSpan(), offsetX, offsetY); - }); - } - } - } - - private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush) - { +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Threading.Tasks; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Drawing +{ + /// + /// Using the brush as a source of pixels colors blends the brush color with source. + /// + /// The pixel format. + internal class FillProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + /// The brush. + /// + private readonly IBrush brush; + private readonly GraphicsOptions options; + + /// + /// Initializes a new instance of the class. + /// + /// The brush to source pixel colors from. + /// The options + public FillProcessor(IBrush brush, GraphicsOptions options) + { + this.brush = brush; + this.options = options; + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + + // Align start/end positions. + int minX = Math.Max(0, startX); + int maxX = Math.Min(source.Width, endX); + int minY = Math.Max(0, startY); + int maxY = Math.Min(source.Height, endY); + + int width = maxX - minX; + + var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + + // If there's no reason for blending, then avoid it. + if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush)) + { + ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); + + ParallelHelper.IterateRows( + workingRect, + parallelSettings, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + source.GetPixelRowSpan(y).Slice(minX, width).Fill(solidBrush.Color); + } + }); + } + else + { + // Reset offset if necessary. + if (minX > 0) + { + startX = 0; + } + + if (minY > 0) + { + startY = 0; + } + + using (IMemoryOwner amount = source.MemoryAllocator.Allocate(width)) + using (BrushApplicator applicator = this.brush.CreateApplicator( + source, + sourceRectangle, + this.options)) + { + amount.GetSpan().Fill(1f); + + ParallelHelper.IterateRows( + workingRect, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + int offsetY = y - startY; + int offsetX = minX - startX; + + applicator.Apply(amount.GetSpan(), offsetX, offsetY); + } + }); + } + } + } + + private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush) + { solidBrush = this.brush as SolidBrush; if (solidBrush == null) @@ -109,7 +118,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing return false; } - return this.options.IsOpaqueColorWithoutBlending(solidBrush.Color); - } - } + return this.options.IsOpaqueColorWithoutBlending(solidBrush.Color); + } + } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs index 514249a2d4..550c021caa 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs @@ -3,7 +3,7 @@ using System; using System.Buffers; - +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; @@ -82,11 +82,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing // we need to offset the pixel grid to account for when we outline a path. // basically if the line is [1,2] => [3,2] then when outlining at 1 we end up with a region of [0.5,1.5],[1.5, 1.5],[3.5,2.5],[2.5,2.5] // and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the# - // region to alline with the pixel grid. + // region to align with the pixel grid. float offset = 0.5f; if (this.Options.Antialias) { - offset = 0f; // we are antialising skip offsetting as real antalising should take care of offset. + offset = 0f; // we are antialiasing skip offsetting as real antialiasing should take care of offset. subpixelCount = this.Options.AntialiasSubpixelDepth; if (subpixelCount < 4) { @@ -107,6 +107,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing Span buffer = bBuffer.GetSpan(); Span scanline = bScanline.GetSpan(); + bool isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush); + for (int y = minY; y < maxY; y++) { if (scanlineDirty) @@ -121,7 +123,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing int pointsFound = region.Scan(subPixel + offset, buffer, configuration); if (pointsFound == 0) { - // nothing on this line skip + // nothing on this line, skip continue; } @@ -168,16 +170,30 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing { if (!this.Options.Antialias) { + bool hasOnes = false; + bool hasZeros = false; for (int x = 0; x < scanlineWidth; x++) { if (scanline[x] >= 0.5) { scanline[x] = 1; + hasOnes = true; } else { scanline[x] = 0; + hasZeros = true; + } + } + + if (isSolidBrushWithoutBlending && hasOnes != hasZeros) + { + if (hasOnes) + { + source.GetPixelRowSpan(y).Slice(minX, scanlineWidth).Fill(solidBrush.Color); } + + continue; } } @@ -187,5 +203,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing } } } + + private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush) + { + solidBrush = this.Brush as SolidBrush; + + if (solidBrush == null) + { + return false; + } + + return this.Options.IsOpaqueColorWithoutBlending(solidBrush.Color); + } } } \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs index 1095de325f..266d842bfa 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text /// The font we want to render with /// The brush to source pixel colors from. /// The pen to outline text with. - /// The location on the image to start drawign the text from. + /// The location on the image to start drawing the text from. public DrawTextProcessor(TextGraphicsOptions options, string text, Font font, IBrush brush, IPen pen, PointF location) { Guard.NotNull(text, nameof(text)); @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text { base.BeforeImageApply(source, sourceRectangle); - // do everythign at the image level as we are deligating the processing down to other processors + // do everything at the image level as we are delegating the processing down to other processors var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location) { ApplyKerning = this.Options.ApplyKerning, @@ -97,7 +97,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text this.textRenderer = new CachingGlyphRenderer(source.GetMemoryAllocator(), this.Text.Length, this.Pen, this.Brush != null); this.textRenderer.Options = (GraphicsOptions)this.Options; - TextRenderer.RenderTextTo(this.textRenderer, this.Text, style); + var renderer = new TextRenderer(this.textRenderer); + renderer.RenderText(this.Text, style); } protected override void AfterImageApply(Image source, Rectangle sourceRectangle) @@ -138,10 +139,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text fistRow = -startY; } - int end = operation.Map.Height; - int maxHeight = source.Height - startY; - end = Math.Min(end, maxHeight); + int end = Math.Min(operation.Map.Height, maxHeight); for (int row = fistRow; row < end; row++) { @@ -164,18 +163,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text private class CachingGlyphRenderer : IGlyphRenderer, IDisposable { - private PathBuilder builder; + // just enough accuracy to allow for 1/8 pixel differences which + // later are accumulated while rendering, but do not grow into full pixel offsets + // The value 8 is benchmarked to: + // - Provide a good accuracy (smaller than 0.2% image difference compared to the non-caching variant) + // - Cache hit ratio above 60% + private const float AccuracyMultiple = 8; + + private readonly PathBuilder builder; private Point currentRenderPosition = default; - private GlyphRendererParameters currentGlyphRenderParams = default; - private int offset = 0; + private (GlyphRendererParameters glyph, PointF subPixelOffset) currentGlyphRenderParams = default; + private readonly int offset = 0; private PointF currentPoint = default(PointF); - private readonly Dictionary glyphData = new Dictionary(); + private readonly Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData> + glyphData = new Dictionary<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData>(); - private bool renderOutline = false; - private bool renderFill = false; - private bool raterizationRequired = false; + private readonly bool renderOutline = false; + private readonly bool renderFill = false; + private bool rasterizationRequired = false; public CachingGlyphRenderer(MemoryAllocator memoryAllocator, int size, IPen pen, bool renderFill) { @@ -213,17 +220,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text this.builder.StartFigure(); } - public bool BeginGlyph(RectangleF bounds, GlyphRendererParameters paramters) + public bool BeginGlyph(RectangleF bounds, GlyphRendererParameters parameters) { this.currentRenderPosition = Point.Truncate(bounds.Location); + PointF subPixelOffset = bounds.Location - this.currentRenderPosition; + + subPixelOffset.X = MathF.Round(subPixelOffset.X * AccuracyMultiple) / AccuracyMultiple; + subPixelOffset.Y = MathF.Round(subPixelOffset.Y * AccuracyMultiple) / AccuracyMultiple; // we have offset our rendering origion a little bit down to prevent edge cropping, move the draw origin up to compensate this.currentRenderPosition = new Point(this.currentRenderPosition.X - this.offset, this.currentRenderPosition.Y - this.offset); - this.currentGlyphRenderParams = paramters; - if (this.glyphData.ContainsKey(paramters)) + this.currentGlyphRenderParams = (parameters, subPixelOffset); + + if (this.glyphData.ContainsKey(this.currentGlyphRenderParams)) { // we have already drawn the glyph vectors skip trying again - this.raterizationRequired = false; + this.rasterizationRequired = false; return false; } @@ -233,7 +245,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text // ensure all glyphs render around [zero, zero] so offset negative root positions so when we draw the glyph we can offet it back this.builder.SetOrigin(new PointF(-(int)bounds.X + this.offset, -(int)bounds.Y + this.offset)); - this.raterizationRequired = true; + this.rasterizationRequired = true; return true; } @@ -252,7 +264,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text public void Dispose() { - foreach (KeyValuePair kv in this.glyphData) + foreach (KeyValuePair<(GlyphRendererParameters glyph, PointF subPixelOffset), GlyphRenderData> kv in this.glyphData) { kv.Value.Dispose(); } @@ -270,7 +282,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text GlyphRenderData renderData = default; // has the glyoh been rendedered already???? - if (this.raterizationRequired) + if (this.rasterizationRequired) { IPath path = this.builder.Build(); diff --git a/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs index 87e1dc146a..09a1ff71fb 100644 --- a/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/RecolorBrush{TPixel}.cs @@ -95,9 +95,9 @@ namespace SixLabors.ImageSharp.Processing // Lets hack a min max extremes for a color space by letting the IPackedPixel clamp our values to something in the correct spaces :) var maxColor = default(TPixel); - maxColor.PackFromVector4(new Vector4(float.MaxValue)); + maxColor.FromVector4(new Vector4(float.MaxValue)); var minColor = default(TPixel); - minColor.PackFromVector4(new Vector4(float.MinValue)); + minColor.FromVector4(new Vector4(float.MinValue)); this.threshold = Vector4.DistanceSquared(maxColor.ToVector4(), minColor.ToVector4()) * threshold; } @@ -158,7 +158,12 @@ namespace SixLabors.ImageSharp.Processing } Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); - this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan); + this.Blender.Blend( + this.Target.Configuration, + destinationRow, + destinationRow, + overlaySpan, + amountSpan); } } } diff --git a/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs b/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs index 6b69c33f07..20a6833c40 100644 --- a/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/SolidBrush{TPixel}.cs @@ -89,13 +89,24 @@ namespace SixLabors.ImageSharp.Processing /// internal override void Apply(Span scanline, int x, int y) { - Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); + Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x); + + // constrain the spans to each other + if (destinationRow.Length > scanline.Length) + { + destinationRow = destinationRow.Slice(0, scanline.Length); + } + else + { + scanline = scanline.Slice(0, destinationRow.Length); + } MemoryAllocator memoryAllocator = this.Target.MemoryAllocator; + Configuration configuration = this.Target.Configuration; if (this.Options.BlendPercentage == 1f) { - this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, this.Colors.GetSpan(), scanline); + this.Blender.Blend(configuration, destinationRow, destinationRow, this.Colors.GetSpan(), scanline); } else { @@ -108,7 +119,12 @@ namespace SixLabors.ImageSharp.Processing amountSpan[i] = scanline[i] * this.Options.BlendPercentage; } - this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, this.Colors.GetSpan(), amountSpan); + this.Blender.Blend( + configuration, + destinationRow, + destinationRow, + this.Colors.GetSpan(), + amountSpan); } } } diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs index 328d575969..63ccea4e66 100644 --- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs +++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs @@ -160,7 +160,7 @@ namespace SixLabors.ImageSharp.Advanced /// /// The type of the pixel. /// The source. - /// The span retuned from Pixel source + /// The span returned from Pixel source private static Span GetSpan(IPixelSource source) where TPixel : struct, IPixel => source.PixelBuffer.GetSpan(); @@ -172,7 +172,7 @@ namespace SixLabors.ImageSharp.Advanced /// The source. /// The row. /// - /// The span retuned from Pixel source + /// The span returned from Pixel source /// private static Span GetSpan(IPixelSource source, int row) where TPixel : struct, IPixel @@ -185,7 +185,7 @@ namespace SixLabors.ImageSharp.Advanced /// The source. /// The row. /// - /// The span retuned from Pixel source + /// The span returned from Pixel source /// private static Span GetSpan(Buffer2D source, int row) where TPixel : struct, IPixel diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs new file mode 100644 index 0000000000..9e7624480d --- /dev/null +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -0,0 +1,103 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Dithering; +using SixLabors.ImageSharp.Processing.Processors.Quantization; + +namespace SixLabors.ImageSharp.Advanced +{ + /// + /// Unlike traditional Mono/.NET, code on the iPhone is statically compiled ahead of time instead of being + /// compiled on demand by a JIT compiler. This means there are a few limitations with respect to generics, + /// these are caused because not every possible generic instantiation can be determined up front at compile time. + /// The Aot Compiler is designed to overcome the limitations of this compiler. + /// + public static class AotCompilerTools + { + /// + /// Seeds the compiler using the given pixel format. + /// + /// The pixel format. + public static void Seed() + where TPixel : struct, IPixel + { + // This is we actually call all the individual methods you need to seed. + AotCompileOctreeQuantizer(); + AotCompileWuQuantizer(); + AotCompileDithering(); + + // TODO: Do the discovery work to figure out what works and what doesn't. + } + + /// + /// Seeds the compiler using the given pixel formats. + /// + /// The first pixel format. + /// The second pixel format. + public static void Seed() + where TPixel : struct, IPixel + where TPixel2 : struct, IPixel + { + Seed(); + Seed(); + } + + /// + /// Seeds the compiler using the given pixel formats. + /// + /// The first pixel format. + /// The second pixel format. + /// The third pixel format. + public static void Seed() + where TPixel : struct, IPixel + where TPixel2 : struct, IPixel + where TPixel3 : struct, IPixel + { + Seed(); + Seed(); + } + + /// + /// This method doesn't actually do anything but serves an important purpose... + /// If you are running ImageSharp on iOS and try to call SaveAsGif, it will throw an excepion: + /// "Attempting to JIT compile method... OctreeFrameQuantizer.ConstructPalette... while running in aot-only mode." + /// The reason this happens is the SaveAsGif method makes haevy use of generics, which are too confusing for the AoT + /// compiler used on Xamarin.iOS. It spins up the JIT compiler to try and figure it out, but that is an illegal op on + /// iOS so it bombs out. + /// If you are getting the above error, you need to call this method, which will pre-seed the AoT compiler with the + /// necessary methods to complete the SaveAsGif call. That's it, otherwise you should NEVER need this method!!! + /// + /// The pixel format. + private static void AotCompileOctreeQuantizer() + where TPixel : struct, IPixel + { + var test = new OctreeFrameQuantizer(new OctreeQuantizer(false)); + test.AotGetPalette(); + } + + /// + /// This method pre-seeds the WuQuantizer in the AoT compiler for iOS. + /// + /// The pixel format. + private static void AotCompileWuQuantizer() + where TPixel : struct, IPixel + { + var test = new WuFrameQuantizer(new WuQuantizer(false)); + test.QuantizeFrame(new ImageFrame(Configuration.Default, 1, 1)); + test.AotGetPalette(); + } + + /// + /// This method pre-seeds the default dithering engine (FloydSteinbergDiffuser) in the AoT compiler for iOS. + /// + /// The pixel format. + private static void AotCompileDithering() + where TPixel : struct, IPixel + { + var test = new FloydSteinbergDiffuser(); + TPixel pixel = default; + test.Dither(new ImageFrame(Configuration.Default, 1, 1), pixel, pixel, 0, 0, 0, 0, 0, 0); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/CieLab.cs b/src/ImageSharp/ColorSpaces/CieLab.cs index 82975d9330..ea6df86e27 100644 --- a/src/ImageSharp/ColorSpaces/CieLab.cs +++ b/src/ImageSharp/ColorSpaces/CieLab.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; @@ -12,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents a CIE L*a*b* 1976 color. /// /// - internal readonly struct CieLab : IColorVector, IEquatable, IAlmostEquatable + public readonly struct CieLab : IEquatable { /// /// D50 standard illuminant. @@ -21,9 +20,27 @@ namespace SixLabors.ImageSharp.ColorSpaces public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; /// - /// The backing vector for SIMD support. + /// Gets the lightness dimension. + /// A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white). + /// + public readonly float L; + + /// + /// Gets the a color component. + /// A value usually ranging from -100 to 100. Negative is green, positive magenta. + /// + public readonly float A; + + /// + /// Gets the b color component. + /// A value usually ranging from -100 to 100. Negative is blue, positive is yellow + /// + public readonly float B; + + /// + /// Gets the reference white point of this color /// - private readonly Vector3 backingVector; + public readonly CieXyz WhitePoint; /// /// Initializes a new instance of the struct. @@ -32,9 +49,9 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The a (green - magenta) component. /// The b (blue - yellow) component. /// Uses as white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieLab(float l, float a, float b) - : this(new Vector3(l, a, b), DefaultWhitePoint) + : this(l, a, b, DefaultWhitePoint) { } @@ -45,7 +62,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The a (green - magenta) component. /// The b (blue - yellow) component. /// The reference white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieLab(float l, float a, float b, CieXyz whitePoint) : this(new Vector3(l, a, b), whitePoint) { @@ -56,7 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// The vector representing the l, a, b components. /// Uses as white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieLab(Vector3 vector) : this(vector, DefaultWhitePoint) { @@ -67,126 +84,62 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// The vector representing the l, a, b components. /// The reference white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieLab(Vector3 vector, CieXyz whitePoint) : this() { - this.backingVector = vector; + // Not clamping as documentation about this space only indicates "usual" ranges + this.L = vector.X; + this.A = vector.Y; + this.B = vector.Z; this.WhitePoint = whitePoint; } - /// - /// Gets the reference white point of this color - /// - public CieXyz WhitePoint { get; } - - /// - /// Gets the lightness dimension. - /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). - /// - public float L - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.X; - } - - /// - /// Gets the a color component. - /// A value ranging from -100 to 100. Negative is green, positive magenta. - /// - public float A - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Y; - } - - /// - /// Gets the b color component. - /// A value ranging from -100 to 100. Negative is blue, positive is yellow - /// - public float B - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Z; - } - - /// - public Vector3 Vector => this.backingVector; - /// /// 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 ==(CieLab left, CieLab right) - { - return left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(CieLab left, CieLab 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 !=(CieLab left, CieLab right) - { - return !left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(CieLab left, CieLab right) => !left.Equals(right); /// public override int GetHashCode() { - return HashHelpers.Combine(this.WhitePoint.GetHashCode(), this.backingVector.GetHashCode()); + int hash = this.L.GetHashCode(); + hash = HashHelpers.Combine(hash, this.A.GetHashCode()); + hash = HashHelpers.Combine(hash, this.B.GetHashCode()); + return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode()); } /// - public override string ToString() - { - return this.Equals(default) - ? "CieLab [Empty]" - : $"CieLab [ L={this.L:#0.##}, A={this.A:#0.##}, B={this.B:#0.##}]"; - } + public override string ToString() => FormattableString.Invariant($"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); /// - public override bool Equals(object obj) - { - return obj is CieLab other && this.Equals(other); - } + public override bool Equals(object obj) => obj is CieLab other && this.Equals(other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(CieLab other) { - return this.backingVector.Equals(other.backingVector) + return this.L.Equals(other.L) + && this.A.Equals(other.A) + && this.B.Equals(other.B) && this.WhitePoint.Equals(other.WhitePoint); } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool AlmostEquals(CieLab other, float precision) - { - var result = Vector3.Abs(this.backingVector - other.backingVector); - - return this.WhitePoint.Equals(other.WhitePoint) - && result.X <= precision - && result.Y <= precision - && result.Z <= precision; - } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/CieLch.cs b/src/ImageSharp/ColorSpaces/CieLch.cs index 67a9956bdc..f1a7425e9e 100644 --- a/src/ImageSharp/ColorSpaces/CieLch.cs +++ b/src/ImageSharp/ColorSpaces/CieLch.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; @@ -12,8 +11,11 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color. /// /// - internal readonly struct CieLch : IColorVector, IEquatable, IAlmostEquatable + public readonly struct CieLch : IEquatable { + private static readonly Vector3 Min = new Vector3(0, -200, 0); + private static readonly Vector3 Max = new Vector3(100, 200, 360); + /// /// D50 standard illuminant. /// Used when reference white is not specified explicitly. @@ -21,9 +23,27 @@ namespace SixLabors.ImageSharp.ColorSpaces public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; /// - /// The backing vector for SIMD support. + /// Gets the lightness dimension. + /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). + /// + public readonly float L; + + /// + /// Gets the a chroma component. + /// A value ranging from 0 to 200. + /// + public readonly float C; + + /// + /// Gets the h° hue component in degrees. + /// A value ranging from 0 to 360. /// - private readonly Vector3 backingVector; + public readonly float H; + + /// + /// Gets the reference white point of this color + /// + public readonly CieXyz WhitePoint; /// /// Initializes a new instance of the struct. @@ -32,9 +52,9 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The chroma, relative saturation. /// The hue in degrees. /// Uses as white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieLch(float l, float c, float h) - : this(new Vector3(l, c, h), DefaultWhitePoint) + : this(l, c, h, DefaultWhitePoint) { } @@ -45,7 +65,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The chroma, relative saturation. /// The hue in degrees. /// The reference white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieLch(float l, float c, float h, CieXyz whitePoint) : this(new Vector3(l, c, h), whitePoint) { @@ -56,7 +76,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// The vector representing the l, c, h components. /// Uses as white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieLch(Vector3 vector) : this(vector, DefaultWhitePoint) { @@ -67,129 +87,64 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// The vector representing the l, c, h components. /// The reference white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieLch(Vector3 vector, CieXyz whitePoint) - : this() { - this.backingVector = vector; + vector = Vector3.Clamp(vector, Min, Max); + this.L = vector.X; + this.C = vector.Y; + this.H = vector.Z; this.WhitePoint = whitePoint; } - /// - /// Gets the reference white point of this color - /// - public CieXyz WhitePoint { get; } - - /// - /// Gets the lightness dimension. - /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). - /// - public float L - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.X; - } - - /// - /// Gets the a chroma component. - /// A value ranging from 0 to 100. - /// - public float C - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Y; - } - - /// - /// Gets the h° hue component in degrees. - /// A value ranging from 0 to 360. - /// - public float H - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Z; - } - - /// - public Vector3 Vector => this.backingVector; - /// /// 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 ==(CieLch left, CieLch right) - { - return left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(CieLch left, CieLch 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 !=(CieLch left, CieLch right) - { - return !left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(CieLch left, CieLch right) => !left.Equals(right); /// public override int GetHashCode() { - return HashHelpers.Combine(this.WhitePoint.GetHashCode(), this.backingVector.GetHashCode()); + int hash = this.L.GetHashCode(); + hash = HashHelpers.Combine(hash, this.C.GetHashCode()); + hash = HashHelpers.Combine(hash, this.H.GetHashCode()); + return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode()); } /// - public override string ToString() - { - return this.Equals(default) - ? "CieLch [Empty]" - : $"CieLch [ L={this.L:#0.##}, C={this.C:#0.##}, H={this.H:#0.##}]"; - } + public override string ToString() => FormattableString.Invariant($"CieLch({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})"); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object obj) - { - return obj is CieLch other && this.Equals(other); - } + [MethodImpl(InliningOptions.ShortMethod)] + public override bool Equals(object obj) => obj is CieLch other && this.Equals(other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(CieLch other) { - return this.backingVector.Equals(other.backingVector) + return this.L.Equals(other.L) + && this.C.Equals(other.C) + && this.H.Equals(other.H) && this.WhitePoint.Equals(other.WhitePoint); } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool AlmostEquals(CieLch other, float precision) - { - var result = Vector3.Abs(this.backingVector - other.backingVector); - - return this.WhitePoint.Equals(other.WhitePoint) - && result.X <= precision - && result.Y <= precision - && result.Z <= precision; - } - /// /// Computes the saturation of the color (chroma normalized by lightness) /// @@ -197,7 +152,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// A value ranging from 0 to 100. /// /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public float Saturation() { float result = 100 * (this.C / this.L); diff --git a/src/ImageSharp/ColorSpaces/CieLchuv.cs b/src/ImageSharp/ColorSpaces/CieLchuv.cs index 0b4c7a9036..256b5dc0fd 100644 --- a/src/ImageSharp/ColorSpaces/CieLchuv.cs +++ b/src/ImageSharp/ColorSpaces/CieLchuv.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; @@ -10,10 +9,13 @@ namespace SixLabors.ImageSharp.ColorSpaces { /// /// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color. - /// + /// /// - internal readonly struct CieLchuv : IColorVector, IEquatable, IAlmostEquatable + public readonly struct CieLchuv : IEquatable { + private static readonly Vector3 Min = new Vector3(0, -200, 0); + private static readonly Vector3 Max = new Vector3(100, 200, 360); + /// /// D50 standard illuminant. /// Used when reference white is not specified explicitly. @@ -21,9 +23,27 @@ namespace SixLabors.ImageSharp.ColorSpaces public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; /// - /// The backing vector for SIMD support. + /// Gets the lightness dimension. + /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). + /// + public readonly float L; + + /// + /// Gets the a chroma component. + /// A value ranging from 0 to 200. + /// + public readonly float C; + + /// + /// Gets the h° hue component in degrees. + /// A value ranging from 0 to 360. /// - private readonly Vector3 backingVector; + public readonly float H; + + /// + /// Gets the reference white point of this color + /// + public readonly CieXyz WhitePoint; /// /// Initializes a new instance of the struct. @@ -32,9 +52,9 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The chroma, relative saturation. /// The hue in degrees. /// Uses as white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieLchuv(float l, float c, float h) - : this(new Vector3(l, c, h), DefaultWhitePoint) + : this(l, c, h, DefaultWhitePoint) { } @@ -45,9 +65,9 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The chroma, relative saturation. /// The hue in degrees. /// The reference white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieLchuv(float l, float c, float h, CieXyz whitePoint) - : this(new Vector3(l, c, h), whitePoint) + : this(new Vector3(l, c, h), whitePoint) { } @@ -56,7 +76,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// The vector representing the l, c, h components. /// Uses as white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieLchuv(Vector3 vector) : this(vector, DefaultWhitePoint) { @@ -67,127 +87,62 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// The vector representing the l, c, h components. /// The reference white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieLchuv(Vector3 vector, CieXyz whitePoint) : this() { - this.backingVector = vector; + vector = Vector3.Clamp(vector, Min, Max); + this.L = vector.X; + this.C = vector.Y; + this.H = vector.Z; this.WhitePoint = whitePoint; } - /// - /// Gets the reference white point of this color - /// - public CieXyz WhitePoint { get; } - - /// - /// Gets the lightness dimension. - /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). - /// - public float L - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.X; - } - - /// - /// Gets the a chroma component. - /// A value ranging from 0 to 100. - /// - public float C - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Y; - } - - /// - /// Gets the h° hue component in degrees. - /// A value ranging from 0 to 360. - /// - public float H - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Z; - } - - /// - public Vector3 Vector => this.backingVector; - /// /// 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. /// - public static bool operator ==(CieLchuv left, CieLchuv right) - { - return left.Equals(right); - } + public static bool operator ==(CieLchuv left, CieLchuv 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. /// - public static bool operator !=(CieLchuv left, CieLchuv right) - { - return !left.Equals(right); - } + public static bool operator !=(CieLchuv left, CieLchuv right) => !left.Equals(right); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { - return HashHelpers.Combine(this.WhitePoint.GetHashCode(), this.backingVector.GetHashCode()); + int hash = this.L.GetHashCode(); + hash = HashHelpers.Combine(hash, this.C.GetHashCode()); + hash = HashHelpers.Combine(hash, this.H.GetHashCode()); + return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode()); } /// - public override string ToString() - { - return this.Equals(default) - ? "CieLchuv [Empty]" - : $"CieLchuv [ L={this.L:#0.##}, C={this.C:#0.##}, H={this.H:#0.##}"; - } + public override string ToString() => FormattableString.Invariant($"CieLchuv({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})"); /// - public override bool Equals(object obj) - { - return obj is CieLchuv other && this.Equals(other); - } + public override bool Equals(object obj) => obj is CieLchuv other && this.Equals(other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(CieLchuv other) { - return this.backingVector.Equals(other.backingVector) + return this.L.Equals(other.L) + && this.C.Equals(other.C) + && this.H.Equals(other.H) && this.WhitePoint.Equals(other.WhitePoint); } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool AlmostEquals(CieLchuv other, float precision) - { - var result = Vector3.Abs(this.backingVector - other.backingVector); - - return this.WhitePoint.Equals(other.WhitePoint) - && result.X <= precision - && result.Y <= precision - && result.Z <= precision; - } - /// /// Computes the saturation of the color (chroma normalized by lightness) /// @@ -195,7 +150,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// A value ranging from 0 to 100. /// /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public float Saturation() { float result = 100 * (this.C / this.L); diff --git a/src/ImageSharp/ColorSpaces/CieLuv.cs b/src/ImageSharp/ColorSpaces/CieLuv.cs index dbc3b6dee5..8fe073d6bf 100644 --- a/src/ImageSharp/ColorSpaces/CieLuv.cs +++ b/src/ImageSharp/ColorSpaces/CieLuv.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; @@ -14,7 +13,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// attempted perceptual uniformity /// /// - internal readonly struct CieLuv : IColorVector, IEquatable, IAlmostEquatable + public readonly struct CieLuv : IEquatable { /// /// D65 standard illuminant. @@ -23,9 +22,27 @@ namespace SixLabors.ImageSharp.ColorSpaces public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; /// - /// The backing vector for SIMD support. + /// Gets the lightness dimension + /// A value usually ranging between 0 and 100. + /// + public readonly float L; + + /// + /// Gets the blue-yellow chromaticity coordinate of the given whitepoint. + /// A value usually ranging between -100 and 100. + /// + public readonly float U; + + /// + /// Gets the red-green chromaticity coordinate of the given whitepoint. + /// A value usually ranging between -100 and 100. + /// + public readonly float V; + + /// + /// Gets the reference white point of this color /// - private readonly Vector3 backingVector; + public readonly CieXyz WhitePoint; /// /// Initializes a new instance of the struct. @@ -34,9 +51,9 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The blue-yellow chromaticity coordinate of the given whitepoint. /// The red-green chromaticity coordinate of the given whitepoint. /// Uses as white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieLuv(float l, float u, float v) - : this(new Vector3(l, u, v), DefaultWhitePoint) + : this(l, u, v, DefaultWhitePoint) { } @@ -47,7 +64,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The blue-yellow chromaticity coordinate of the given whitepoint. /// The red-green chromaticity coordinate of the given whitepoint. /// The reference white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieLuv(float l, float u, float v, CieXyz whitePoint) : this(new Vector3(l, u, v), whitePoint) { @@ -58,7 +75,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// The vector representing the l, u, v components. /// Uses as white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieLuv(Vector3 vector) : this(vector, DefaultWhitePoint) { @@ -69,126 +86,61 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// The vector representing the l, u, v components. /// The reference white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieLuv(Vector3 vector, CieXyz whitePoint) - : this() { - this.backingVector = vector; + // Not clamping as documentation about this space only indicates "usual" ranges + this.L = vector.X; + this.U = vector.Y; + this.V = vector.Z; this.WhitePoint = whitePoint; } - /// - /// Gets the reference white point of this color - /// - public CieXyz WhitePoint { get; } - - /// - /// Gets the lightness dimension - /// A value usually ranging between 0 and 100. - /// - public float L - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.X; - } - - /// - /// Gets the blue-yellow chromaticity coordinate of the given whitepoint. - /// A value usually ranging between -100 and 100. - /// - public float U - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Y; - } - - /// - /// Gets the red-green chromaticity coordinate of the given whitepoint. - /// A value usually ranging between -100 and 100. - /// - public float V - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Z; - } - - /// - public Vector3 Vector => this.backingVector; - /// /// 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 ==(CieLuv left, CieLuv right) - { - return left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(CieLuv left, CieLuv 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 !=(CieLuv left, CieLuv right) - { - return !left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(CieLuv left, CieLuv right) => !left.Equals(right); /// public override int GetHashCode() { - return HashHelpers.Combine(this.WhitePoint.GetHashCode(), this.backingVector.GetHashCode()); + int hash = this.L.GetHashCode(); + hash = HashHelpers.Combine(hash, this.U.GetHashCode()); + hash = HashHelpers.Combine(hash, this.V.GetHashCode()); + return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode()); } /// - public override string ToString() - { - return this.Equals(default) - ? "CieLuv [ Empty ]" - : $"CieLuv [ L={this.L:#0.##}, U={this.U:#0.##}, V={this.V:#0.##} ]"; - } + public override string ToString() => FormattableString.Invariant($"CieLuv({this.L:#0.##}, {this.U:#0.##}, {this.V:#0.##})"); /// - public override bool Equals(object obj) - { - return obj is CieLuv other && this.Equals(other); - } + public override bool Equals(object obj) => obj is CieLuv other && this.Equals(other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(CieLuv other) { - return this.backingVector.Equals(other.backingVector) - && this.WhitePoint.Equals(other.WhitePoint); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool AlmostEquals(CieLuv other, float precision) - { - var result = Vector3.Abs(this.backingVector - other.backingVector); - - return this.WhitePoint.Equals(other.WhitePoint) - && result.X <= precision - && result.Y <= precision - && result.Z <= precision; + return this.L.Equals(other.L) + && this.U.Equals(other.U) + && this.V.Equals(other.V) + && this.WhitePoint.Equals(other.WhitePoint); } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs index 4f4f951472..f625bb7616 100644 --- a/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.ComponentModel; -using System.Numerics; using System.Runtime.CompilerServices; // ReSharper disable CompareOfFloatsByEqualityOperator @@ -12,45 +10,15 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Represents the coordinates of CIEXY chromaticity space /// - internal readonly struct CieXyChromaticityCoordinates : IEquatable, IAlmostEquatable + public readonly struct CieXyChromaticityCoordinates : IEquatable { - /// - /// The backing vector for SIMD support. - /// - private readonly Vector2 backingVector; - - /// - /// Initializes a new instance of the struct. - /// - /// Chromaticity coordinate x (usually from 0 to 1) - /// Chromaticity coordinate y (usually from 0 to 1) - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public CieXyChromaticityCoordinates(float x, float y) - : this(new Vector2(x, y)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the XY Chromaticity coordinates - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public CieXyChromaticityCoordinates(Vector2 vector) - { - this.backingVector = vector; - } - /// /// Gets the chromaticity X-coordinate. /// /// /// Ranges usually from 0 to 1. /// - public float X - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.X; - } + public readonly float X; /// /// Gets the chromaticity Y-coordinate @@ -58,85 +26,54 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Ranges usually from 0 to 1. /// - public float Y + public readonly float Y; + + /// + /// Initializes a new instance of the struct. + /// + /// Chromaticity coordinate x (usually from 0 to 1) + /// Chromaticity coordinate y (usually from 0 to 1) + [MethodImpl(InliningOptions.ShortMethod)] + public CieXyChromaticityCoordinates(float x, float y) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Y; + this.X = x; + this.Y = y; } /// /// 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 ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) - { - return left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates 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 !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) - { - return !left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) => !left.Equals(right); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - { - return this.backingVector.GetHashCode(); - } + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => HashHelpers.Combine(this.X.GetHashCode(), this.Y.GetHashCode()); /// - public override string ToString() - { - return this.Equals(default) - ? "CieXyChromaticityCoordinates [Empty]" - : $"CieXyChromaticityCoordinates [ X={this.X:#0.##}, Y={this.Y:#0.##}]"; - } + public override string ToString() => FormattableString.Invariant($"CieXyChromaticityCoordinates({this.X:#0.##}, {this.Y:#0.##})"); /// - public override bool Equals(object obj) - { - return obj is CieXyChromaticityCoordinates other && this.Equals(other); - } + public override bool Equals(object obj) => obj is CieXyChromaticityCoordinates other && this.Equals(other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(CieXyChromaticityCoordinates other) - { - // The memberwise comparison here is a workaround for https://github.com/dotnet/coreclr/issues/16443 - return this.X == other.X && this.Y == other.Y; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool AlmostEquals(CieXyChromaticityCoordinates other, float precision) - { - var result = Vector2.Abs(this.backingVector - other.backingVector); - - return result.X <= precision - && result.Y <= precision; - } + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(CieXyChromaticityCoordinates other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/CieXyy.cs b/src/ImageSharp/ColorSpaces/CieXyy.cs index ac1a4532c5..7137360e94 100644 --- a/src/ImageSharp/ColorSpaces/CieXyy.cs +++ b/src/ImageSharp/ColorSpaces/CieXyy.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; @@ -12,12 +11,25 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents an CIE xyY 1931 color /// /// - internal readonly struct CieXyy : IColorVector, IEquatable, IAlmostEquatable + public readonly struct CieXyy : IEquatable { /// - /// The backing vector for SIMD support. + /// Gets the X chrominance component. + /// A value usually ranging between 0 and 1. + /// + public readonly float X; + + /// + /// Gets the Y chrominance component. + /// A value usually ranging between 0 and 1. + /// + public readonly float Y; + + /// + /// Gets the Y luminance component. + /// A value usually ranging between 0 and 1. /// - private readonly Vector3 backingVector; + public readonly float Yl; /// /// Initializes a new instance of the struct. @@ -25,130 +37,72 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The x chroma component. /// The y chroma component. /// The y luminance component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieXyy(float x, float y, float yl) - : this(new Vector3(x, y, yl)) { + // Not clamping as documentation about this space only indicates "usual" ranges + this.X = x; + this.Y = y; + this.Yl = yl; } /// /// Initializes a new instance of the struct. /// /// The vector representing the x, y, Y components. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieXyy(Vector3 vector) : this() { - // Not clamping as documentation about this space seems to indicate "usual" ranges - this.backingVector = vector; + // Not clamping as documentation about this space only indicates "usual" ranges + this.X = vector.X; + this.Y = vector.Y; + this.Yl = vector.Z; } - /// - /// Gets the X chrominance component. - /// A value usually ranging between 0 and 1. - /// - public float X - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.X; - } - - /// - /// Gets the Y chrominance component. - /// A value usually ranging between 0 and 1. - /// - public float Y - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Y; - } - - /// - /// Gets the Y luminance component. - /// A value usually ranging between 0 and 1. - /// - public float Yl - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Z; - } - - /// - public Vector3 Vector => this.backingVector; - /// /// 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 ==(CieXyy left, CieXyy right) - { - return left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(CieXyy left, CieXyy 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 !=(CieXyy left, CieXyy right) - { - return !left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(CieXyy left, CieXyy right) => !left.Equals(right); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { - return this.backingVector.GetHashCode(); + int hash = this.X.GetHashCode(); + hash = HashHelpers.Combine(hash, this.Y.GetHashCode()); + return HashHelpers.Combine(hash, this.Yl.GetHashCode()); } /// - public override string ToString() - { - return this.Equals(default) - ? "CieXyy [ Empty ]" - : $"CieXyy [ X={this.X:#0.##}, Y={this.Y:#0.##}, Yl={this.Yl:#0.##} ]"; - } + public override string ToString() => FormattableString.Invariant($"CieXyy({this.X:#0.##}, {this.Y:#0.##}, {this.Yl:#0.##})"); /// - public override bool Equals(object obj) - { - return obj is CieXyy other && this.Equals(other); - } + public override bool Equals(object obj) => obj is CieXyy other && this.Equals(other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(CieXyy other) { - return this.backingVector.Equals(other.backingVector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool AlmostEquals(CieXyy other, float precision) - { - var result = Vector3.Abs(this.backingVector - other.backingVector); - - return result.X <= precision - && result.Y <= precision - && result.Z <= precision; + return this.X.Equals(other.X) + && this.Y.Equals(other.Y) + && this.Yl.Equals(other.Yl); } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/CieXyz.cs b/src/ImageSharp/ColorSpaces/CieXyz.cs index fa4261b46d..c0ed356601 100644 --- a/src/ImageSharp/ColorSpaces/CieXyz.cs +++ b/src/ImageSharp/ColorSpaces/CieXyz.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; @@ -12,12 +11,25 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents an CIE XYZ 1931 color /// /// - internal readonly struct CieXyz : IColorVector, IEquatable, IAlmostEquatable + public readonly struct CieXyz : IEquatable { /// - /// The backing vector for SIMD support. + /// Gets the X component. A mix (a linear combination) of cone response curves chosen to be nonnegative. + /// A value usually ranging between 0 and 1. + /// + public readonly float X; + + /// + /// Gets the Y luminance component. + /// A value usually ranging between 0 and 1. /// - private readonly Vector3 backingVector; + public readonly float Y; + + /// + /// Gets the Z component. Quasi-equal to blue stimulation, or the S cone response + /// A value usually ranging between 0 and 1. + /// + public readonly float Z; /// /// Initializes a new instance of the struct. @@ -25,7 +37,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// X is a mix (a linear combination) of cone response curves chosen to be nonnegative /// The y luminance component. /// Z is quasi-equal to blue stimulation, or the S cone of the human eye. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public CieXyz(float x, float y, float z) : this(new Vector3(x, y, z)) { @@ -38,116 +50,62 @@ namespace SixLabors.ImageSharp.ColorSpaces public CieXyz(Vector3 vector) : this() { - // Not clamping as documentation about this space seems to indicate "usual" ranges - this.backingVector = vector; - } - - /// - /// Gets the X component. A mix (a linear combination) of cone response curves chosen to be nonnegative. - /// A value usually ranging between 0 and 1. - /// - public float X - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.X; - } - - /// - /// Gets the Y luminance component. - /// A value usually ranging between 0 and 1. - /// - public float Y - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Y; + // Not clamping as documentation about this space only indicates "usual" ranges + this.X = vector.X; + this.Y = vector.Y; + this.Z = vector.Z; } - /// - /// Gets the Z component. Quasi-equal to blue stimulation, or the S cone response - /// A value usually ranging between 0 and 1. - /// - public float Z - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Z; - } - - /// - public Vector3 Vector => this.backingVector; - /// /// 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 ==(CieXyz left, CieXyz right) - { - return left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(CieXyz left, CieXyz 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 !=(CieXyz left, CieXyz right) - { - return !left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(CieXyz left, CieXyz right) => !left.Equals(right); + + /// + /// Returns a new representing this instance. + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Vector3 ToVector3() => new Vector3(this.X, this.Y, this.Z); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { - return this.backingVector.GetHashCode(); + int hash = this.X.GetHashCode(); + hash = HashHelpers.Combine(hash, this.Y.GetHashCode()); + return HashHelpers.Combine(hash, this.Z.GetHashCode()); } /// - public override string ToString() - { - return this.Equals(default) - ? "CieXyz [ Empty ]" - : $"CieXyz [ X={this.X:#0.##}, Y={this.Y:#0.##}, Z={this.Z:#0.##} ]"; - } + public override string ToString() => FormattableString.Invariant($"CieXyz({this.X:#0.##}, {this.Y:#0.##}, {this.Z:#0.##})"); /// - public override bool Equals(object obj) - { - return obj is CieXyz other && this.Equals(other); - } + public override bool Equals(object obj) => obj is CieXyz other && this.Equals(other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(CieXyz other) { - return this.backingVector.Equals(other.backingVector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool AlmostEquals(CieXyz other, float precision) - { - var result = Vector3.Abs(this.backingVector - other.backingVector); - - return result.X <= precision - && result.Y <= precision - && result.Z <= precision; + return this.X.Equals(other.X) + && this.Y.Equals(other.Y) + && this.Z.Equals(other.Z); } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Cmyk.cs b/src/ImageSharp/ColorSpaces/Cmyk.cs index 2702d4ba33..634667c0c6 100644 --- a/src/ImageSharp/ColorSpaces/Cmyk.cs +++ b/src/ImageSharp/ColorSpaces/Cmyk.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; @@ -11,151 +10,108 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Represents an CMYK (cyan, magenta, yellow, keyline) color. /// - internal readonly struct Cmyk : IEquatable, IAlmostEquatable + public readonly struct Cmyk : IEquatable { - /// - /// The backing vector for SIMD support. - /// - private readonly Vector4 backingVector; + private static readonly Vector4 Min = Vector4.Zero; + private static readonly Vector4 Max = Vector4.One; /// - /// Initializes a new instance of the struct. + /// Gets the cyan color component. + /// A value ranging between 0 and 1. /// - /// The cyan component. - /// The magenta component. - /// The yellow component. - /// The keyline black component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Cmyk(float c, float m, float y, float k) - : this(new Vector4(c, m, y, k)) - { - } + public readonly float C; /// - /// Initializes a new instance of the struct. + /// Gets the magenta color component. + /// A value ranging between 0 and 1. /// - /// The vector representing the c, m, y, k components. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Cmyk(Vector4 vector) - : this() - { - this.backingVector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); - } + public readonly float M; /// - /// Gets the cyan color component. + /// Gets the yellow color component. /// A value ranging between 0 and 1. /// - public float C - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.X; - } + public readonly float Y; /// - /// Gets the magenta color component. + /// Gets the keyline black color component. /// A value ranging between 0 and 1. /// - public float M - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Y; - } + public readonly float K; /// - /// Gets the yellow color component. - /// A value ranging between 0 and 1. + /// Initializes a new instance of the struct. /// - public float Y + /// The cyan component. + /// The magenta component. + /// The yellow component. + /// The keyline black component. + [MethodImpl(InliningOptions.ShortMethod)] + public Cmyk(float c, float m, float y, float k) + : this(new Vector4(c, m, y, k)) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Z; } /// - /// Gets the keyline black color component. - /// A value ranging between 0 and 1. + /// Initializes a new instance of the struct. /// - public float K + /// The vector representing the c, m, y, k components. + [MethodImpl(InliningOptions.ShortMethod)] + public Cmyk(Vector4 vector) { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.W; + vector = Vector4.Clamp(vector, Min, Max); + this.C = vector.X; + this.M = vector.Y; + this.Y = vector.Z; + this.K = vector.W; } /// /// 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 ==(Cmyk left, Cmyk right) - { - return left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Cmyk left, Cmyk 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 !=(Cmyk left, Cmyk right) - { - return !left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Cmyk left, Cmyk right) => !left.Equals(right); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public override int GetHashCode() { - return this.backingVector.GetHashCode(); + int hash = this.C.GetHashCode(); + hash = HashHelpers.Combine(hash, this.M.GetHashCode()); + hash = HashHelpers.Combine(hash, this.Y.GetHashCode()); + return HashHelpers.Combine(hash, this.K.GetHashCode()); } /// - public override string ToString() - { - return this.Equals(default) - ? "Cmyk [Empty]" - : $"Cmyk [ C={this.C:#0.##}, M={this.M:#0.##}, Y={this.Y:#0.##}, K={this.K:#0.##}]"; - } + public override string ToString() => FormattableString.Invariant($"Cmyk({this.C:#0.##}, {this.M:#0.##}, {this.Y:#0.##}, {this.K:#0.##})"); /// - public override bool Equals(object obj) - { - return obj is Cmyk other && this.Equals(other); - } + public override bool Equals(object obj) => obj is Cmyk other && this.Equals(other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(Cmyk other) { - return this.backingVector.Equals(other.backingVector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool AlmostEquals(Cmyk other, float precision) - { - var result = Vector4.Abs(this.backingVector - other.backingVector); - - return result.X <= precision - && result.Y <= precision - && result.Z <= precision - && result.W <= precision; + return this.C.Equals(other.C) + && this.M.Equals(other.M) + && this.Y.Equals(other.Y) + && this.K.Equals(other.K); } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs new file mode 100644 index 0000000000..13cca1582d --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs @@ -0,0 +1,36 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Companding +{ + /// + /// Implements gamma companding + /// + /// + /// + /// + /// + public static class GammaCompanding + { + /// + /// Expands a companded channel to its linear equivalent with respect to the energy. + /// + /// The channel value. + /// The gamma value. + /// The representing the linear channel value. + [MethodImpl(InliningOptions.ShortMethod)] + public static float Expand(float channel, float gamma) => MathF.Pow(channel, gamma); + + /// + /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. + /// + /// The channel value. + /// The gamma value. + /// The representing the nonlinear channel value. + [MethodImpl(InliningOptions.ShortMethod)] + public static float Compress(float channel, float gamma) => MathF.Pow(channel, 1 / gamma); + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs new file mode 100644 index 0000000000..9e2cf8ad86 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces.Conversion; + +namespace SixLabors.ImageSharp.ColorSpaces.Companding +{ + /// + /// Implements L* companding + /// + /// + /// For more info see: + /// + /// + /// + public static class LCompanding + { + /// + /// Expands a companded channel to its linear equivalent with respect to the energy. + /// + /// The channel value. + /// The representing the linear channel value. + [MethodImpl(InliningOptions.ShortMethod)] + public static float Expand(float channel) + => channel <= 0.08 ? 100 * channel / CieConstants.Kappa : ImageMaths.Pow3((channel + 0.16F) / 1.16F); + + /// + /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. + /// + /// The channel value + /// The representing the nonlinear channel value. + [MethodImpl(InliningOptions.ShortMethod)] + public static float Compress(float channel) + => channel <= CieConstants.Epsilon ? channel * CieConstants.Kappa / 100F : MathF.Pow(1.16F * channel, 0.3333333F) - 0.16F; + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs b/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs new file mode 100644 index 0000000000..a3a9121727 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs @@ -0,0 +1,36 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Companding +{ + /// + /// Implements Rec. 2020 companding function (for 12-bits). + /// + /// + /// + /// For 10-bits, companding is identical to + /// + public static class Rec2020Companding + { + /// + /// Expands a companded channel to its linear equivalent with respect to the energy. + /// + /// The channel value. + /// The representing the linear channel value. + [MethodImpl(InliningOptions.ShortMethod)] + public static float Expand(float channel) + => channel < 0.08145F ? channel / 4.5F : MathF.Pow((channel + 0.0993F) / 1.0993F, 2.222222F); + + /// + /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. + /// + /// The channel value. + /// The representing the nonlinear channel value. + [MethodImpl(InliningOptions.ShortMethod)] + public static float Compress(float channel) + => channel < 0.0181F ? 4500F * channel : (1.0993F * channel) - 0.0993F; + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs b/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs new file mode 100644 index 0000000000..e2e802d08a --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Companding +{ + /// + /// Implements the Rec. 709 companding function. + /// + /// + /// http://en.wikipedia.org/wiki/Rec._709 + /// + public static class Rec709Companding + { + /// + /// Expands a companded channel to its linear equivalent with respect to the energy. + /// + /// The channel value. + /// The representing the linear channel value. + [MethodImpl(InliningOptions.ShortMethod)] + public static float Expand(float channel) + => channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, 2.222222F); + + /// + /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. + /// + /// The channel value. + /// The representing the nonlinear channel value. + [MethodImpl(InliningOptions.ShortMethod)] + public static float Compress(float channel) + => channel < 0.018F ? 4500F * channel : (1.099F * channel) - 0.099F; + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs new file mode 100644 index 0000000000..5ae4629137 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs @@ -0,0 +1,89 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Companding +{ + /// + /// Implements sRGB companding + /// + /// + /// For more info see: + /// + /// + /// + public static class SRgbCompanding + { + /// + /// Expands the companded vectors to their linear equivalents with respect to the energy. + /// + /// The span of vectors. + [MethodImpl(InliningOptions.ShortMethod)] + public static void Expand(Span vectors) + { + ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); + + for (int i = 0; i < vectors.Length; i++) + { + ref Vector4 v = ref Unsafe.Add(ref baseRef, i); + v.X = Expand(v.X); + v.Y = Expand(v.Y); + v.Z = Expand(v.Z); + } + } + + /// + /// Compresses the uncompanded vectors to their nonlinear equivalents with respect to the energy. + /// + /// The span of vectors. + [MethodImpl(InliningOptions.ShortMethod)] + public static void Compress(Span vectors) + { + ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); + + for (int i = 0; i < vectors.Length; i++) + { + ref Vector4 v = ref Unsafe.Add(ref baseRef, i); + v.X = Compress(v.X); + v.Y = Compress(v.Y); + v.Z = Compress(v.Z); + } + } + + /// + /// Expands a companded vector to its linear equivalent with respect to the energy. + /// + /// The vector. + /// The representing the linear channel values. + [MethodImpl(InliningOptions.ShortMethod)] + public static Vector4 Expand(Vector4 vector) => new Vector4(Expand(vector.X), Expand(vector.Y), Expand(vector.Z), vector.W); + + /// + /// Compresses an uncompanded vector (linear) to its nonlinear equivalent. + /// + /// The vector. + /// The representing the nonlinear channel values. + [MethodImpl(InliningOptions.ShortMethod)] + public static Vector4 Compress(Vector4 vector) => new Vector4(Compress(vector.X), Compress(vector.Y), Compress(vector.Z), vector.W); + + /// + /// Expands a companded channel to its linear equivalent with respect to the energy. + /// + /// The channel value. + /// The representing the linear channel value. + [MethodImpl(InliningOptions.ShortMethod)] + public static float Expand(float channel) => channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F); + + /// + /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. + /// + /// The channel value. + /// The representing the nonlinear channel value. + [MethodImpl(InliningOptions.ShortMethod)] + public static float Compress(float channel) => channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F; + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs index e2c1308fa8..892c0d5e38 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs @@ -1,151 +1,130 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Performs chromatic adaptation on the various color spaces. /// - internal partial class ColorSpaceConverter + public partial class ColorSpaceConverter { /// /// Performs chromatic adaptation of given color. - /// Target white point is . + /// Target white point is . /// /// The color to adapt - /// The white point to adapt for + /// The source white point. /// The adapted color - public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint) + public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint) => this.Adapt(color, sourceWhitePoint, this.whitePoint); + + /// + /// Performs chromatic adaptation of given color. + /// Target white point is . + /// + /// The color to adapt + /// The source white point. + /// The target white point. + /// The adapted color + public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint, in CieXyz targetWhitePoint) { - if (!this.IsChromaticAdaptationPerformed) + if (!this.performChromaticAdaptation || sourceWhitePoint.Equals(targetWhitePoint)) { - throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); + return color; } - return this.ChromaticAdaptation.Transform(color, sourceWhitePoint, this.WhitePoint); + return this.chromaticAdaptation.Transform(color, sourceWhitePoint, targetWhitePoint); } /// - /// Adapts color from the source white point to white point set in . + /// Adapts color from the source white point to white point set in . /// /// The color to adapt /// The adapted color public CieLab Adapt(in CieLab color) { - if (!this.IsChromaticAdaptationPerformed) - { - throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); - } - - if (color.WhitePoint.Equals(this.TargetLabWhitePoint)) + if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint)) { return color; } - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); return this.ToCieLab(xyzColor); } /// - /// Adapts color from the source white point to white point set in . + /// Adapts color from the source white point to white point set in . /// /// The color to adapt /// The adapted color public CieLch Adapt(in CieLch color) { - if (!this.IsChromaticAdaptationPerformed) - { - throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); - } - - if (color.WhitePoint.Equals(this.TargetLabWhitePoint)) + if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint)) { return color; } - CieLab labColor = this.ToCieLab(color); + var labColor = this.ToCieLab(color); return this.ToCieLch(labColor); } /// - /// Adapts color from the source white point to white point set in . + /// Adapts color from the source white point to white point set in . /// /// The color to adapt /// The adapted color public CieLchuv Adapt(in CieLchuv color) { - if (!this.IsChromaticAdaptationPerformed) - { - throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); - } - - if (color.WhitePoint.Equals(this.TargetLabWhitePoint)) + if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint)) { return color; } - CieLuv luvColor = this.ToCieLuv(color); + var luvColor = this.ToCieLuv(color); return this.ToCieLchuv(luvColor); } /// - /// Adapts color from the source white point to white point set in . + /// Adapts color from the source white point to white point set in . /// /// The color to adapt /// The adapted color public CieLuv Adapt(in CieLuv color) { - if (!this.IsChromaticAdaptationPerformed) - { - throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); - } - - if (color.WhitePoint.Equals(this.TargetLuvWhitePoint)) + if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLuvWhitePoint)) { return color; } - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); return this.ToCieLuv(xyzColor); } /// - /// Adapts color from the source white point to white point set in . + /// Adapts color from the source white point to white point set in . /// /// The color to adapt /// The adapted color public HunterLab Adapt(in HunterLab color) { - if (!this.IsChromaticAdaptationPerformed) - { - throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); - } - - if (color.WhitePoint.Equals(this.TargetHunterLabWhitePoint)) + if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetHunterLabWhitePoint)) { return color; } - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); return this.ToHunterLab(xyzColor); } /// - /// Adapts a color from the source working space to working space set in . + /// Adapts a color from the source working space to working space set in . /// /// The color to adapt /// The adapted color public LinearRgb Adapt(in LinearRgb color) { - if (!this.IsChromaticAdaptationPerformed) - { - throw new InvalidOperationException("Cannot perform chromatic adaptation, provide a chromatic adaptation method and white point."); - } - - if (color.WorkingSpace.Equals(this.TargetRgbWorkingSpace)) + if (!this.performChromaticAdaptation || color.WorkingSpace.Equals(this.targetRgbWorkingSpace)) { return color; } @@ -155,21 +134,25 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion CieXyz unadapted = converterToXYZ.Convert(color); // Adaptation - CieXyz adapted = this.ChromaticAdaptation.Transform(unadapted, color.WorkingSpace.WhitePoint, this.TargetRgbWorkingSpace.WhitePoint); + CieXyz adapted = this.chromaticAdaptation.Transform(unadapted, color.WorkingSpace.WhitePoint, this.targetRgbWorkingSpace.WhitePoint); // Conversion back to RGB - CieXyzToLinearRgbConverter converterToRGB = this.GetCieXyxToLinearRgbConverter(this.TargetRgbWorkingSpace); - return converterToRGB.Convert(adapted); + return this.cieXyzToLinearRgbConverter.Convert(adapted); } /// - /// Adapts an color from the source working space to working space set in . + /// Adapts an color from the source working space to working space set in . /// /// The color to adapt /// The adapted color public Rgb Adapt(in Rgb color) { - LinearRgb linearInput = this.ToLinearRgb(color); + if (!this.performChromaticAdaptation) + { + return color; + } + + var linearInput = this.ToLinearRgb(color); LinearRgb linearOutput = this.Adapt(linearInput); return this.ToRgb(linearOutput); } diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs index 1cead3001f..3c197673d3 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs @@ -1,15 +1,17 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Allows conversion to . /// - internal partial class ColorSpaceConverter + public partial class ColorSpaceConverter { /// /// The converter for converting between CieLch to CieLab. @@ -26,15 +28,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion // Conversion (perserving white point) CieLab unadapted = CieLchToCieLabConverter.Convert(color); - if (!this.IsChromaticAdaptationPerformed) - { - return unadapted; - } - // Adaptation return this.Adapt(unadapted); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLab(sp); + } + } + /// /// Converts a into a /// @@ -42,10 +60,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLab ToCieLab(in CieLchuv color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLab(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLab(sp); + } + } + /// /// Converts a into a /// @@ -53,10 +93,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLab ToCieLab(in CieLuv color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLab(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLab(sp); + } + } + /// /// Converts a into a /// @@ -64,10 +126,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLab ToCieLab(in CieXyy color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLab(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLab(sp); + } + } + /// /// Converts a into a /// @@ -76,13 +160,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion public CieLab ToCieLab(in CieXyz color) { // Adaptation - CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed - ? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint) - : color; + CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetLabWhitePoint); // Conversion - var converter = new CieXyzToCieLabConverter(this.TargetLabWhitePoint); - return converter.Convert(adapted); + return this.cieXyzToCieLabConverter.Convert(adapted); + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLab(sp); + } } /// @@ -92,10 +194,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLab ToCieLab(in Cmyk color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); return this.ToCieLab(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLab(sp); + } + } + /// /// Converts a into a /// @@ -103,10 +226,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLab ToCieLab(in Hsl color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLab(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLab(sp); + } + } + /// /// Converts a into a /// @@ -114,10 +259,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLab ToCieLab(in Hsv color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); return this.ToCieLab(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLab(sp); + } + } + /// /// Converts a into a /// @@ -125,10 +291,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLab ToCieLab(in HunterLab color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLab(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLab(sp); + } + } + /// /// Converts a into a /// @@ -136,10 +324,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLab ToCieLab(in Lms color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLab(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Lms sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Lms sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLab(sp); + } + } + /// /// Converts a into a /// @@ -147,10 +357,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLab ToCieLab(in LinearRgb color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLab(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLab(sp); + } + } + /// /// Converts a into a /// @@ -158,10 +390,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLab ToCieLab(in Rgb color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLab(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLab(sp); + } + } + /// /// Converts a into a /// @@ -169,8 +423,30 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLab ToCieLab(in YCbCr color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLab(xyzColor); } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLab(sp); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs index cbefc5ac59..0a8607e3bc 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs @@ -1,14 +1,18 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Allows conversion to . /// - internal partial class ColorSpaceConverter + public partial class ColorSpaceConverter { /// /// The converter for converting between CieLab to CieLch. @@ -23,12 +27,33 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion public CieLch ToCieLch(in CieLab color) { // Adaptation - CieLab adapted = this.IsChromaticAdaptationPerformed ? this.Adapt(color) : color; + CieLab adapted = this.Adapt(color); // Conversion return CieLabToCieLchConverter.Convert(adapted); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLch destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLch dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLch(sp); + } + } + /// /// Converts a into a /// @@ -36,10 +61,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLch ToCieLch(in CieLchuv color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLch destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLch dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLch(sp); + } + } + /// /// Converts a into a /// @@ -47,10 +94,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLch ToCieLch(in CieLuv color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLch destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLch dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLch(sp); + } + } + /// /// Converts a into a /// @@ -58,10 +127,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLch ToCieLch(in CieXyy color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLch destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLch dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLch(sp); + } + } + /// /// Converts a into a /// @@ -69,10 +160,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLch ToCieLch(in CieXyz color) { - CieLab labColor = this.ToCieLab(color); + var labColor = this.ToCieLab(color); + return this.ToCieLch(labColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLch destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLch dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLch(sp); + } + } + /// /// Converts a into a /// @@ -80,10 +193,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLch ToCieLch(in Cmyk color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); return this.ToCieLch(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLch destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLch dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLch(sp); + } + } + /// /// Converts a into a /// @@ -91,10 +225,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLch ToCieLch(in Hsl color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLch destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLch dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLch(sp); + } + } + /// /// Converts a into a /// @@ -102,10 +258,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLch ToCieLch(in Hsv color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLch destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLch dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLch(sp); + } + } + /// /// Converts a into a /// @@ -113,10 +291,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLch ToCieLch(in HunterLab color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLch destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLch dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLch(sp); + } + } + /// /// Converts a into a /// @@ -124,10 +324,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLch ToCieLch(in LinearRgb color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLch destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLch dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLch(sp); + } + } + /// /// Converts a into a /// @@ -135,10 +357,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLch ToCieLch(in Lms color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Lms sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLch destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Lms sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLch dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLch(sp); + } + } + /// /// Converts a into a /// @@ -146,10 +390,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLch ToCieLch(in Rgb color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLch destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLch dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLch(sp); + } + } + /// /// Converts a into a /// @@ -157,8 +423,30 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLch ToCieLch(in YCbCr color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLch(xyzColor); } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLch destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLch dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLch(sp); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs index 49c25462ea..3a779ee722 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs @@ -1,14 +1,18 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Allows conversion to . /// - internal partial class ColorSpaceConverter + public partial class ColorSpaceConverter { /// /// The converter for converting between CieLab to CieLchuv. @@ -22,10 +26,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLchuv ToCieLchuv(in CieLab color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLchuv(sp); + } + } + /// /// Converts a into a /// @@ -33,10 +59,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLchuv ToCieLchuv(in CieLch color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLchuv(sp); + } + } + /// /// Converts a into a /// @@ -45,12 +93,33 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion public CieLchuv ToCieLchuv(in CieLuv color) { // Adaptation - CieLuv adapted = this.IsChromaticAdaptationPerformed ? this.Adapt(color) : color; + CieLuv adapted = this.Adapt(color); // Conversion return CieLuvToCieLchuvConverter.Convert(adapted); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLchuv(sp); + } + } + /// /// Converts a into a /// @@ -58,10 +127,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLchuv ToCieLchuv(in CieXyy color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLchuv(sp); + } + } + /// /// Converts a into a /// @@ -69,8 +160,30 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLchuv ToCieLchuv(in CieXyz color) { - CieLab labColor = this.ToCieLab(color); - return this.ToCieLchuv(labColor); + var luvColor = this.ToCieLuv(color); + + return this.ToCieLchuv(luvColor); + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLchuv(sp); + } } /// @@ -80,10 +193,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLchuv ToCieLchuv(in Cmyk color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLchuv(sp); + } + } + /// /// Converts a into a /// @@ -91,10 +226,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLchuv ToCieLchuv(in Hsl color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLchuv(sp); + } + } + /// /// Converts a into a /// @@ -102,10 +259,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLchuv ToCieLchuv(in Hsv color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLchuv(sp); + } + } + /// /// Converts a into a /// @@ -113,10 +292,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLchuv ToCieLchuv(in HunterLab color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLchuv(sp); + } + } + /// /// Converts a into a /// @@ -124,10 +325,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLchuv ToCieLchuv(in LinearRgb color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLchuv(sp); + } + } + /// /// Converts a into a /// @@ -135,10 +358,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLchuv ToCieLchuv(in Lms color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Lms sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Lms sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLchuv(sp); + } + } + /// /// Converts a into a /// @@ -146,10 +391,32 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLchuv ToCieLchuv(in Rgb color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); + return this.ToCieLchuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLchuv(sp); + } + } + /// /// Converts a into a /// @@ -157,8 +424,29 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// The public CieLchuv ToCieLchuv(in YCbCr color) { - CieXyz xyzColor = this.ToCieXyz(color); + var xyzColor = this.ToCieXyz(color); return this.ToCieLchuv(xyzColor); } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLchuv(sp); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs index 36e6501a5f..90eb8e34d7 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs @@ -1,16 +1,18 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Allows conversion to . /// - internal partial class ColorSpaceConverter + public partial class ColorSpaceConverter { private static readonly CieLchuvToCieLuvConverter CieLchuvToCieLuvConverter = new CieLchuvToCieLuvConverter(); @@ -25,6 +27,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieLuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLuv(sp); + } + } + /// /// Converts a into a /// @@ -36,6 +59,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieLuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLuv(sp); + } + } + /// /// Converts a into a /// @@ -46,15 +90,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion // Conversion (perserving white point) CieLuv unadapted = CieLchuvToCieLuvConverter.Convert(color); - if (!this.IsChromaticAdaptationPerformed) - { - return unadapted; - } - // Adaptation return this.Adapt(unadapted); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLuv(sp); + } + } + /// /// Converts a into a /// @@ -66,6 +126,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieLuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLuv(sp); + } + } + /// /// Converts a into a /// @@ -74,13 +155,31 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion public CieLuv ToCieLuv(in CieXyz color) { // Adaptation - CieXyz adapted = !this.WhitePoint.Equals(this.TargetLabWhitePoint) && this.IsChromaticAdaptationPerformed - ? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetLabWhitePoint) - : color; + CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetLuvWhitePoint); // Conversion - var converter = new CieXyzToCieLuvConverter(this.TargetLuvWhitePoint); - return converter.Convert(adapted); + return this.cieXyzToCieLuvConverter.Convert(adapted); + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLuv(sp); + } } /// @@ -94,6 +193,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieLuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLuv(sp); + } + } + /// /// Converts a into a /// @@ -105,6 +225,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieLuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLuv(sp); + } + } + /// /// Converts a into a /// @@ -116,6 +257,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieLuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLuv(sp); + } + } + /// /// Converts a into a /// @@ -127,6 +289,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieLuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLuv(sp); + } + } + /// /// Converts a into a /// @@ -138,6 +321,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieLuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Lms sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Lms sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLuv(sp); + } + } + /// /// Converts a into a /// @@ -149,6 +353,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieLuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLuv(sp); + } + } + /// /// Converts a into a /// @@ -160,6 +385,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieLuv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLuv(sp); + } + } + /// /// Converts a into a /// @@ -170,5 +416,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion var xyzColor = this.ToCieXyz(color); return this.ToCieLuv(xyzColor); } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); + ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); + ref CieLuv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieLuv(sp); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs index 25a9f75a4b..d03c10a01d 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs @@ -1,14 +1,18 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColorSapce; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Allows conversion to . /// - internal partial class ColorSpaceConverter + public partial class ColorSpaceConverter { private static readonly CieXyzAndCieXyyConverter CieXyzAndCieXyyConverter = new CieXyzAndCieXyyConverter(); @@ -24,6 +28,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyy(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyy dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyy(sp); + } + } + /// /// Converts a into a /// @@ -36,6 +61,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyy(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyy dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyy(sp); + } + } + /// /// Converts a into a /// @@ -48,6 +94,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyy(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyy dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyy(sp); + } + } + /// /// Converts a into a /// @@ -60,14 +127,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyy(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyy dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyy(sp); + } + } + /// /// Converts a into a /// /// The color to convert. /// The - public CieXyy ToCieXyy(in CieXyz color) + public CieXyy ToCieXyy(in CieXyz color) => CieXyzAndCieXyyConverter.Convert(color); + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) { - return CieXyzAndCieXyyConverter.Convert(color); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyy dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyy(sp); + } } /// @@ -82,6 +188,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyy(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyy dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyy(sp); + } + } + /// /// Converts a into a /// @@ -94,6 +221,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyy(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyy dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyy(sp); + } + } + /// /// Converts a into a /// @@ -106,6 +254,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyy(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyy dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyy(sp); + } + } + /// /// Converts a into a /// @@ -118,6 +287,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyy(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyy dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyy(sp); + } + } + /// /// Converts a into a /// @@ -130,6 +320,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyy(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyy dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyy(sp); + } + } + /// /// Converts a into a /// @@ -142,6 +353,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyy(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Lms sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Lms sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyy dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyy(sp); + } + } + /// /// Converts a into a /// @@ -154,6 +386,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyy(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyy dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyy(sp); + } + } + /// /// Converts a into a /// @@ -165,5 +418,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyy(xyzColor); } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyy dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyy(sp); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs index f4f28401f1..fada6d9c59 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs @@ -1,23 +1,25 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Allows conversion to . /// - internal partial class ColorSpaceConverter + public partial class ColorSpaceConverter { private static readonly CieLabToCieXyzConverter CieLabToCieXyzConverter = new CieLabToCieXyzConverter(); private static readonly CieLuvToCieXyzConverter CieLuvToCieXyzConverter = new CieLuvToCieXyzConverter(); - private static readonly HunterLabToCieXyzConverter HunterLabToCieXyzConverter = new HunterLabToCieXyzConverter(); + private static readonly HunterLabToCieXyzConverter + HunterLabToCieXyzConverter = new HunterLabToCieXyzConverter(); private LinearRgbToCieXyzConverter linearRgbToCieXyzConverter; @@ -32,11 +34,28 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion CieXyz unadapted = CieLabToCieXyzConverter.Convert(color); // Adaptation - CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed - ? unadapted - : this.Adapt(unadapted, color.WhitePoint); + return this.Adapt(unadapted, color.WhitePoint); + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; - return adapted; + ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyz dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyz(sp); + } } /// @@ -53,6 +72,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyz(labColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyz dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyz(sp); + } + } + /// /// Converts a into a /// @@ -67,6 +107,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyz(luvColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyz dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyz(sp); + } + } + /// /// Converts a into a /// @@ -78,11 +139,28 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion CieXyz unadapted = CieLuvToCieXyzConverter.Convert(color); // Adaptation - CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed - ? unadapted - : this.Adapt(unadapted, color.WhitePoint); + return this.Adapt(unadapted, color.WhitePoint); + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - return adapted; + for (int i = 0; i < count; i++) + { + ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyz dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyz(sp); + } } /// @@ -96,6 +174,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return CieXyzAndCieXyyConverter.Convert(color); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyz dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyz(sp); + } + } + /// /// Converts a into a /// @@ -109,6 +208,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyz(rgb); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyz dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyz(sp); + } + } + /// /// Converts a into a /// @@ -122,6 +242,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyz(rgb); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyz dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyz(sp); + } + } + /// /// Converts a into a /// @@ -135,6 +276,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyz(rgb); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyz dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyz(sp); + } + } + /// /// Converts a into a /// @@ -146,11 +308,28 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion CieXyz unadapted = HunterLabToCieXyzConverter.Convert(color); // Adaptation - CieXyz adapted = color.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed - ? unadapted - : this.Adapt(unadapted, color.WhitePoint); + return this.Adapt(unadapted, color.WhitePoint); + } - return adapted; + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyz dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyz(sp); + } } /// @@ -165,9 +344,28 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion CieXyz unadapted = converter.Convert(color); // Adaptation - return color.WorkingSpace.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed - ? unadapted - : this.Adapt(unadapted, color.WorkingSpace.WhitePoint); + return this.Adapt(unadapted, color.WorkingSpace.WhitePoint); + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyz dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyz(sp); + } } /// @@ -178,7 +376,28 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion public CieXyz ToCieXyz(in Lms color) { // Conversion - return this.cachedCieXyzAndLmsConverter.Convert(color); + return this.cieXyzAndLmsConverter.Convert(color); + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Lms sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Lms sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyz dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyz(sp); + } } /// @@ -193,6 +412,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyz(linear); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyz dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyz(sp); + } + } + /// /// Converts a into a /// @@ -206,14 +446,35 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCieXyz(rgb); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyz dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCieXyz(sp); + } + } + /// /// Gets the correct converter for the given rgb working space. /// /// The source working space /// The - private LinearRgbToCieXyzConverter GetLinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace) + private LinearRgbToCieXyzConverter GetLinearRgbToCieXyzConverter(RgbWorkingSpaceBase workingSpace) { - if (this.linearRgbToCieXyzConverter != null && this.linearRgbToCieXyzConverter.SourceWorkingSpace.Equals(workingSpace)) + if (this.linearRgbToCieXyzConverter?.SourceWorkingSpace.Equals(workingSpace) == true) { return this.linearRgbToCieXyzConverter; } diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs index 1e403828a2..b798516359 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs @@ -1,14 +1,18 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CmykColorSapce; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Allows conversion to . /// - internal partial class ColorSpaceConverter + public partial class ColorSpaceConverter { private static readonly CmykAndRgbConverter CmykAndRgbConverter = new CmykAndRgbConverter(); @@ -24,6 +28,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCmyk(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); + ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); + ref Cmyk dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCmyk(sp); + } + } + /// /// Converts a into a /// @@ -36,6 +61,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCmyk(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); + ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); + ref Cmyk dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCmyk(sp); + } + } + /// /// Converts a into a /// @@ -48,6 +94,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCmyk(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); + ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); + ref Cmyk dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCmyk(sp); + } + } + /// /// Converts a into a /// @@ -60,6 +127,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCmyk(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); + ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); + ref Cmyk dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCmyk(sp); + } + } + /// /// Converts a into a /// @@ -72,6 +160,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCmyk(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); + ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); + ref Cmyk dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCmyk(sp); + } + } + /// /// Converts a into a /// @@ -84,6 +193,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return CmykAndRgbConverter.Convert(rgb); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); + ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); + ref Cmyk dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCmyk(sp); + } + } + /// /// Converts a into a /// @@ -96,6 +226,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return CmykAndRgbConverter.Convert(rgb); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); + ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); + ref Cmyk dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCmyk(sp); + } + } + /// /// Converts a into a /// @@ -108,6 +259,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return CmykAndRgbConverter.Convert(rgb); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); + ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); + ref Cmyk dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCmyk(sp); + } + } + /// /// Converts a into a /// @@ -120,6 +292,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCmyk(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); + ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); + ref Cmyk dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCmyk(sp); + } + } + /// /// Converts a into a /// @@ -132,6 +325,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return CmykAndRgbConverter.Convert(rgb); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); + ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); + ref Cmyk dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCmyk(sp); + } + } + /// /// Converts a into a /// @@ -144,14 +358,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToCmyk(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Lms sourceRef = ref MemoryMarshal.GetReference(source); + ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Lms sp = ref Unsafe.Add(ref sourceRef, i); + ref Cmyk dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCmyk(sp); + } + } + /// /// Converts a into a /// /// The color to convert. /// The - public Cmyk ToCmyk(in Rgb color) + public Cmyk ToCmyk(in Rgb color) => CmykAndRgbConverter.Convert(color); + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) { - return CmykAndRgbConverter.Convert(color); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); + ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); + ref Cmyk dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCmyk(sp); + } } /// @@ -165,5 +418,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return CmykAndRgbConverter.Convert(rgb); } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); + ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); + ref Cmyk dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToCmyk(sp); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs index 78f8c36fb6..a7080b9749 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs @@ -1,14 +1,18 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSapce; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Allows conversion to . /// - internal partial class ColorSpaceConverter + public partial class ColorSpaceConverter { private static readonly HslAndRgbConverter HslAndRgbConverter = new HslAndRgbConverter(); @@ -24,6 +28,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToHsl(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsl destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsl dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsl(sp); + } + } + /// /// Converts a into a /// @@ -36,6 +61,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToHsl(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsl destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsl dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsl(sp); + } + } + /// /// Converts a into a /// @@ -48,6 +94,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToHsl(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsl destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsl dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsl(sp); + } + } + /// /// Converts a into a /// @@ -60,6 +127,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToHsl(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsl destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsl dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsl(sp); + } + } + /// /// Converts a into a /// @@ -72,6 +160,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToHsl(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsl destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsl dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsl(sp); + } + } + /// /// Converts a into a /// @@ -84,6 +193,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return HslAndRgbConverter.Convert(rgb); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsl destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsl dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsl(sp); + } + } + /// /// Converts a into a /// @@ -96,6 +226,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return HslAndRgbConverter.Convert(rgb); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsl destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsl dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsl(sp); + } + } + /// /// Converts a into a /// @@ -108,6 +259,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return HslAndRgbConverter.Convert(rgb); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsl destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsl dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsl(sp); + } + } + /// /// Converts a into a /// @@ -120,6 +292,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToHsl(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsl destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsl dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsl(sp); + } + } + /// /// Converts a into a /// @@ -132,6 +325,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return HslAndRgbConverter.Convert(rgb); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsl destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsl dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsl(sp); + } + } + /// /// Converts a into a /// @@ -144,14 +358,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToHsl(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Lms sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsl destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Lms sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsl dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsl(sp); + } + } + /// /// Converts a into a /// /// The color to convert. /// The - public Hsl ToHsl(in Rgb color) + public Hsl ToHsl(in Rgb color) => HslAndRgbConverter.Convert(color); + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) { - return HslAndRgbConverter.Convert(color); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsl destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsl dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsl(sp); + } } /// @@ -165,5 +418,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return HslAndRgbConverter.Convert(rgb); } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsl destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsl dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsl(sp); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs index 3edd72c59b..a2121203c1 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs @@ -1,14 +1,18 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSapce; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Allows conversion to . /// - internal partial class ColorSpaceConverter + public partial class ColorSpaceConverter { private static readonly HsvAndRgbConverter HsvAndRgbConverter = new HsvAndRgbConverter(); @@ -24,6 +28,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToHsv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsv(sp); + } + } + /// /// Converts a into a /// @@ -36,6 +61,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToHsv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsv(sp); + } + } + /// /// Converts a into a /// @@ -48,6 +94,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToHsv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsv(sp); + } + } + /// /// Converts a into a /// @@ -60,6 +127,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToHsv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsv(sp); + } + } + /// /// Converts a into a /// @@ -72,6 +160,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToHsv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsv(sp); + } + } + /// /// Converts a into a /// @@ -84,6 +193,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return HsvAndRgbConverter.Convert(rgb); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsv(sp); + } + } + /// /// Converts a into a /// @@ -96,6 +226,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return HsvAndRgbConverter.Convert(rgb); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsv(sp); + } + } + /// /// Converts a into a /// @@ -108,6 +259,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return HsvAndRgbConverter.Convert(rgb); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsv(sp); + } + } + /// /// Converts a into a /// @@ -120,6 +292,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToHsv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsv(sp); + } + } + /// /// Converts a into a /// @@ -132,6 +325,27 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return HsvAndRgbConverter.Convert(rgb); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsv(sp); + } + } + /// /// Converts a into a /// @@ -144,14 +358,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return this.ToHsv(xyzColor); } + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Lms sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Lms sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsv(sp); + } + } + /// /// Converts a into a /// /// The color to convert. /// The - public Hsv ToHsv(in Rgb color) + public Hsv ToHsv(in Rgb color) => HsvAndRgbConverter.Convert(color); + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) { - return HsvAndRgbConverter.Convert(color); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsv(sp); + } } /// @@ -165,5 +418,26 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion return HsvAndRgbConverter.Convert(rgb); } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); + ref Hsv destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); + ref Hsv dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHsv(sp); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs index f3a64164b1..e5996c238e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs @@ -1,15 +1,290 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Allows conversion to . /// - internal partial class ColorSpaceConverter + public partial class ColorSpaceConverter { + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); + ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); + ref HunterLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHunterLab(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); + ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); + ref HunterLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHunterLab(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); + ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); + ref HunterLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHunterLab(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); + ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); + ref HunterLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHunterLab(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); + ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); + ref HunterLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHunterLab(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); + ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); + ref HunterLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHunterLab(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); + ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); + ref HunterLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHunterLab(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); + ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); + ref HunterLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHunterLab(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); + ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); + ref HunterLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHunterLab(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); + ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); + ref HunterLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHunterLab(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Lms sourceRef = ref MemoryMarshal.GetReference(source); + ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Lms sp = ref Unsafe.Add(ref sourceRef, i); + ref HunterLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHunterLab(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); + ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); + ref HunterLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHunterLab(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); + ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); + ref HunterLab dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToHunterLab(sp); + } + } + /// /// Converts a into a /// @@ -73,12 +348,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion public HunterLab ToHunterLab(in CieXyz color) { // Adaptation - CieXyz adapted = !this.WhitePoint.Equals(this.TargetHunterLabWhitePoint) && this.IsChromaticAdaptationPerformed - ? this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetHunterLabWhitePoint) - : color; + CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetHunterLabWhitePoint); // Conversion - return new CieXyzToHunterLabConverter(this.TargetHunterLabWhitePoint).Convert(adapted); + return this.cieXyzToHunterLabConverter.Convert(adapted); } /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs index 5fdde5c758..eef626be2f 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs @@ -1,18 +1,293 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Allows conversion to . /// - internal partial class ColorSpaceConverter + public partial class ColorSpaceConverter { private static readonly RgbToLinearRgbConverter RgbToLinearRgbConverter = new RgbToLinearRgbConverter(); - private CieXyzToLinearRgbConverter cieXyzToLinearRgbConverter; + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); + ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); + ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLinearRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); + ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); + ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLinearRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); + ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); + ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLinearRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); + ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); + ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLinearRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); + ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); + ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLinearRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); + ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); + ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLinearRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); + ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); + ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLinearRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); + ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); + ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLinearRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); + ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); + ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLinearRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); + ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); + ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLinearRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Lms sourceRef = ref MemoryMarshal.GetReference(source); + ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Lms sp = ref Unsafe.Add(ref sourceRef, i); + ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLinearRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); + ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); + ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLinearRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); + ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); + ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLinearRgb(sp); + } + } /// /// Converts a into a @@ -77,13 +352,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion public LinearRgb ToLinearRgb(in CieXyz color) { // Adaptation - CieXyz adapted = this.TargetRgbWorkingSpace.WhitePoint.Equals(this.WhitePoint) || !this.IsChromaticAdaptationPerformed - ? color - : this.ChromaticAdaptation.Transform(color, this.WhitePoint, this.TargetRgbWorkingSpace.WhitePoint); + CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetRgbWorkingSpace.WhitePoint); // Conversion - CieXyzToLinearRgbConverter xyzConverter = this.GetCieXyxToLinearRgbConverter(this.TargetRgbWorkingSpace); - return xyzConverter.Convert(adapted); + return this.cieXyzToLinearRgbConverter.Convert(adapted); } /// @@ -162,20 +434,5 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion var rgb = this.ToRgb(color); return this.ToLinearRgb(rgb); } - - /// - /// Gets the correct converter for the given rgb working space. - /// - /// The target working space - /// The - private CieXyzToLinearRgbConverter GetCieXyxToLinearRgbConverter(RgbWorkingSpace workingSpace) - { - if (this.cieXyzToLinearRgbConverter != null && this.cieXyzToLinearRgbConverter.TargetWorkingSpace.Equals(workingSpace)) - { - return this.cieXyzToLinearRgbConverter; - } - - return this.cieXyzToLinearRgbConverter = new CieXyzToLinearRgbConverter(workingSpace); - } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs index 3293e2f4a4..3b8638f7d2 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs @@ -1,15 +1,290 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Allows conversion to . /// - internal partial class ColorSpaceConverter + public partial class ColorSpaceConverter { + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); + ref Lms destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); + ref Lms dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLms(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); + ref Lms destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); + ref Lms dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLms(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); + ref Lms destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); + ref Lms dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLms(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); + ref Lms destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); + ref Lms dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLms(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); + ref Lms destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); + ref Lms dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLms(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); + ref Lms destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); + ref Lms dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLms(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); + ref Lms destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); + ref Lms dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLms(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); + ref Lms destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); + ref Lms dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLms(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); + ref Lms destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); + ref Lms dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLms(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); + ref Lms destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); + ref Lms dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLms(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); + ref Lms destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); + ref Lms dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLms(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); + ref Lms destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); + ref Lms dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLms(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); + ref Lms destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); + ref Lms dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToLms(sp); + } + } + /// /// Converts a into a /// @@ -70,10 +345,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// /// The color to convert. /// The - public Lms ToLms(in CieXyz color) - { - return this.cachedCieXyzAndLmsConverter.Convert(color); - } + public Lms ToLms(in CieXyz color) => this.cieXyzAndLmsConverter.Convert(color); /// /// Converts a into a diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs index 5bfb6ee052..fc5665e5c1 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs @@ -1,17 +1,294 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Allows conversion to . /// - internal partial class ColorSpaceConverter + public partial class ColorSpaceConverter { private static readonly LinearRgbToRgbConverter LinearRgbToRgbConverter = new LinearRgbToRgbConverter(); + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Lms sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Lms sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToRgb(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); + ref Rgb destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToRgb(sp); + } + } + /// /// Converts a into a /// diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs index b7fe34f41a..5780f4f545 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs @@ -1,35 +1,279 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// /// Allows conversion to . /// - internal partial class ColorSpaceConverter + public partial class ColorSpaceConverter { private static readonly YCbCrAndRgbConverter YCbCrAndRgbConverter = new YCbCrAndRgbConverter(); /// - /// Converts a into a + /// Performs the bulk conversion from into /// - /// The color to convert. - /// The - public YCbCr ToYCbCr(in CieLab color) + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) { - var xyzColor = this.ToCieXyz(color); + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; - return this.ToYCbCr(xyzColor); + ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); + ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); + ref YCbCr dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToYCbCr(sp); + } } /// - /// Converts a into a + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); + ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); + ref YCbCr dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToYCbCr(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); + ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); + ref YCbCr dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToYCbCr(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); + ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); + ref YCbCr dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToYCbCr(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); + ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); + ref YCbCr dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToYCbCr(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); + ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); + ref YCbCr dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToYCbCr(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); + ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); + ref YCbCr dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToYCbCr(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); + ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); + ref YCbCr dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToYCbCr(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); + ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); + ref YCbCr dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToYCbCr(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); + ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); + ref YCbCr dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToYCbCr(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Lms sourceRef = ref MemoryMarshal.GetReference(source); + ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Lms sp = ref Unsafe.Add(ref sourceRef, i); + ref YCbCr dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToYCbCr(sp); + } + } + + /// + /// Performs the bulk conversion from into + /// + /// The span to the source colors + /// The span to the destination colors + public void Convert(ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); + ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); + ref YCbCr dp = ref Unsafe.Add(ref destRef, i); + dp = this.ToYCbCr(sp); + } + } + + /// + /// Converts a into a /// /// The color to convert. /// The - public YCbCr ToYCbCr(in CieLch color) + public YCbCr ToYCbCr(in CieLab color) { var xyzColor = this.ToCieXyz(color); @@ -37,11 +281,11 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion } /// - /// Converts a into a + /// Converts a into a /// /// The color to convert. /// The - public YCbCr ToYCbCr(in CieLchuv color) + public YCbCr ToYCbCr(in CieLch color) { var xyzColor = this.ToCieXyz(color); @@ -161,9 +405,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// /// The color to convert. /// The - public YCbCr ToYCbCr(in Rgb color) - { - return YCbCrAndRgbConverter.Convert(color); - } + public YCbCr ToYCbCr(in Rgb color) => YCbCrAndRgbConverter.Convert(color); } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs index 7142ab0e8c..fe6a57f7ac 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs @@ -2,100 +2,60 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// - /// Converts between color spaces ensuring that the color is adapted using chromatic adaptation. + /// Provides methods to allow the conversion of color values between different color spaces. /// - internal partial class ColorSpaceConverter + public partial class ColorSpaceConverter { - /// - /// The default whitepoint used for converting to CieLab - /// - public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; - - private Matrix4x4 transformationMatrix; - - private CieXyzAndLmsConverter cachedCieXyzAndLmsConverter; + // Options. + private static readonly ColorSpaceConverterOptions DefaultOptions = new ColorSpaceConverterOptions(); + private readonly Matrix4x4 lmsAdaptationMatrix; + private readonly CieXyz whitePoint; + private readonly CieXyz targetLuvWhitePoint; + private readonly CieXyz targetLabWhitePoint; + private readonly CieXyz targetHunterLabWhitePoint; + private readonly RgbWorkingSpaceBase targetRgbWorkingSpace; + private readonly IChromaticAdaptation chromaticAdaptation; + private readonly bool performChromaticAdaptation; + private readonly CieXyzAndLmsConverter cieXyzAndLmsConverter; + private readonly CieXyzToCieLabConverter cieXyzToCieLabConverter; + private readonly CieXyzToCieLuvConverter cieXyzToCieLuvConverter; + private readonly CieXyzToHunterLabConverter cieXyzToHunterLabConverter; + private readonly CieXyzToLinearRgbConverter cieXyzToLinearRgbConverter; /// /// Initializes a new instance of the class. /// public ColorSpaceConverter() + : this(DefaultOptions) { - // Note the order here this is important. - this.WhitePoint = DefaultWhitePoint; - this.LmsAdaptationMatrix = CieXyzAndLmsConverter.DefaultTransformationMatrix; - this.ChromaticAdaptation = new VonKriesChromaticAdaptation(this.cachedCieXyzAndLmsConverter); - this.TargetLuvWhitePoint = CieLuv.DefaultWhitePoint; - this.TargetLabWhitePoint = CieLab.DefaultWhitePoint; - this.TargetHunterLabWhitePoint = HunterLab.DefaultWhitePoint; - this.TargetRgbWorkingSpace = Rgb.DefaultWorkingSpace; } /// - /// Gets or sets the white point used for chromatic adaptation in conversions from/to XYZ color space. - /// When null, no adaptation will be performed. - /// - public CieXyz WhitePoint { get; set; } - - /// - /// Gets or sets the white point used *when creating* Luv/LChuv colors. (Luv/LChuv colors on the input already contain the white point information) - /// Defaults to: . - /// - public CieXyz TargetLuvWhitePoint { get; set; } - - /// - /// Gets or sets the white point used *when creating* Lab/LChab colors. (Lab/LChab colors on the input already contain the white point information) - /// Defaults to: . - /// - public CieXyz TargetLabWhitePoint { get; set; } - - /// - /// Gets or sets the white point used *when creating* HunterLab colors. (HunterLab colors on the input already contain the white point information) - /// Defaults to: . - /// - public CieXyz TargetHunterLabWhitePoint { get; set; } - - /// - /// Gets or sets the target working space used *when creating* RGB colors. (RGB colors on the input already contain the working space information) - /// Defaults to: . - /// - public RgbWorkingSpace TargetRgbWorkingSpace { get; set; } - - /// - /// Gets or sets the chromatic adaptation method used. When null, no adaptation will be performed. - /// - public IChromaticAdaptation ChromaticAdaptation { get; set; } - - /// - /// Gets or sets transformation matrix used in conversion to , - /// also used in the default Von Kries Chromatic Adaptation method. + /// Initializes a new instance of the class. /// - public Matrix4x4 LmsAdaptationMatrix + /// The configuration options. + public ColorSpaceConverter(ColorSpaceConverterOptions options) { - get => this.transformationMatrix; - - set - { - this.transformationMatrix = value; - if (this.cachedCieXyzAndLmsConverter == null) - { - this.cachedCieXyzAndLmsConverter = new CieXyzAndLmsConverter(value); - } - else - { - this.cachedCieXyzAndLmsConverter.TransformationMatrix = value; - } - } + Guard.NotNull(options, nameof(options)); + this.whitePoint = options.WhitePoint; + this.targetLuvWhitePoint = options.TargetLuvWhitePoint; + this.targetLabWhitePoint = options.TargetLabWhitePoint; + this.targetHunterLabWhitePoint = options.TargetHunterLabWhitePoint; + this.targetRgbWorkingSpace = options.TargetRgbWorkingSpace; + this.chromaticAdaptation = options.ChromaticAdaptation; + this.performChromaticAdaptation = this.chromaticAdaptation != null; + this.lmsAdaptationMatrix = options.LmsAdaptationMatrix; + + this.cieXyzAndLmsConverter = new CieXyzAndLmsConverter(this.lmsAdaptationMatrix); + this.cieXyzToCieLabConverter = new CieXyzToCieLabConverter(this.targetLabWhitePoint); + this.cieXyzToCieLuvConverter = new CieXyzToCieLuvConverter(this.targetLuvWhitePoint); + this.cieXyzToHunterLabConverter = new CieXyzToHunterLabConverter(this.targetHunterLabWhitePoint); + this.cieXyzToLinearRgbConverter = new CieXyzToLinearRgbConverter(this.targetRgbWorkingSpace); } - - /// - /// Gets a value indicating whether chromatic adaptation has been performed. - /// - private bool IsChromaticAdaptationPerformed => this.ChromaticAdaptation != null; } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs new file mode 100644 index 0000000000..fcd031e263 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs @@ -0,0 +1,55 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion +{ + /// + /// Configuration options for the class. + /// + public class ColorSpaceConverterOptions + { + /// + /// Gets or sets the white point used for chromatic adaptation in conversions from/to XYZ color space. + /// When default, no adaptation will be performed. + /// Defaults to: . + /// + public CieXyz WhitePoint { get; set; } = CieLuv.DefaultWhitePoint; + + /// + /// Gets or sets the white point used *when creating* Luv/LChuv colors. (Luv/LChuv colors on the input already contain the white point information) + /// Defaults to: . + /// + public CieXyz TargetLuvWhitePoint { get; set; } = CieLuv.DefaultWhitePoint; + + /// + /// Gets or sets the white point used *when creating* Lab/LChab colors. (Lab/LChab colors on the input already contain the white point information) + /// Defaults to: . + /// + public CieXyz TargetLabWhitePoint { get; set; } = CieLab.DefaultWhitePoint; + + /// + /// Gets or sets the white point used *when creating* HunterLab colors. (HunterLab colors on the input already contain the white point information) + /// Defaults to: . + /// + public CieXyz TargetHunterLabWhitePoint { get; set; } = HunterLab.DefaultWhitePoint; + + /// + /// Gets or sets the target working space used *when creating* RGB colors. (RGB colors on the input already contain the working space information) + /// Defaults to: . + /// + public RgbWorkingSpaceBase TargetRgbWorkingSpace { get; set; } = Rgb.DefaultWorkingSpace; + + /// + /// Gets or sets the chromatic adaptation method used. When null, no adaptation will be performed. + /// + public IChromaticAdaptation ChromaticAdaptation { get; set; } = new VonKriesChromaticAdaptation(); + + /// + /// Gets or sets transformation matrix used in conversion to and from . + /// + public Matrix4x4 LmsAdaptationMatrix { get; set; } = CieXyzAndLmsConverter.DefaultTransformationMatrix; + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs index dfba4b9269..69877d8b55 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/IChromaticAdaptation.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; + namespace SixLabors.ImageSharp.ColorSpaces.Conversion { /// @@ -8,16 +10,30 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// A linear transformation of a source color (XS, YS, ZS) into a destination color (XD, YD, ZD) by a linear transformation [M] /// which is dependent on the source reference white (XWS, YWS, ZWS) and the destination reference white (XWD, YWD, ZWD). /// - internal interface IChromaticAdaptation + public interface IChromaticAdaptation { /// /// Performs a linear transformation of a source color in to the destination color. /// /// Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates). - /// The source color. + /// The source color. /// The source white point. - /// The target white point. + /// The destination white point. /// The - CieXyz Transform(in CieXyz sourceColor, in CieXyz sourceWhitePoint, in CieXyz targetWhitePoint); + CieXyz Transform(in CieXyz source, in CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint); + + /// + /// Performs a bulk linear transformation of a source color in to the destination color. + /// + /// Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates). + /// The span to the source colors. + /// The span to the destination colors. + /// The source white point. + /// The destination white point. + void Transform( + ReadOnlySpan source, + Span destination, + CieXyz sourceWhitePoint, + in CieXyz destinationWhitePoint); } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/IColorConversion.cs b/src/ImageSharp/ColorSpaces/Conversion/IColorConversion.cs deleted file mode 100644 index 009b91c40a..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/IColorConversion.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion -{ - /// - /// Converts color between two color spaces. - /// - /// The input color type. - /// The result color type. - internal interface IColorConversion - where T : struct - where TResult : struct - { - /// - /// Performs the conversion from the input to an instance of the output type. - /// - /// The input color instance. - /// The converted result - TResult Convert(in T input); - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs deleted file mode 100644 index 8691783703..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Cmyk/CmykAndRgbConverter.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CmykColorSapce -{ - /// - /// Color converter between CMYK and Rgb - /// - internal class CmykAndRgbConverter : IColorConversion, IColorConversion - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgb Convert(in Cmyk input) - { - float r = (1F - input.C) * (1F - input.K); - float g = (1F - input.M) * (1F - input.K); - float b = (1F - input.Y) * (1F - input.K); - - return new Rgb(r, g, b); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Cmyk Convert(in Rgb input) - { - // To CMYK - float c = 1F - input.R; - float m = 1F - input.G; - float y = 1F - input.B; - - // To CMYK - float k = MathF.Min(c, MathF.Min(m, y)); - - if (MathF.Abs(k - 1F) < Constants.Epsilon) - { - return new Cmyk(0, 0, 0, 1F); - } - - c = (c - k) / (1F - k); - m = (m - k) / (1F - k); - y = (y - k) / (1F - k); - - return new Cmyk(c, m, y, k); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs similarity index 62% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs index 061d04493d..40d8c5bc69 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CIeLchToCieLabConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs @@ -4,21 +4,25 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// /// Converts from to . /// - internal class CieLchToCieLabConverter : IColorConversion + internal sealed class CieLchToCieLabConverter { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public CieLab Convert(in CieLch input) { // Conversion algorithm described here: // https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC float l = input.L, c = input.C, hDegrees = input.H; - float hRadians = MathFExtensions.DegreeToRadian(hDegrees); + float hRadians = GeometryUtilities.DegreeToRadian(hDegrees); float a = c * MathF.Cos(hRadians); float b = c * MathF.Sin(hRadians); diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs similarity index 66% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs index 105fb2aa11..2b859205a0 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLch/CieLabToCieLchConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs @@ -4,15 +4,19 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// /// Converts from to . /// - internal class CieLabToCieLchConverter : IColorConversion + internal sealed class CieLabToCieLchConverter { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public CieLch Convert(in CieLab input) { // Conversion algorithm described here: @@ -20,10 +24,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchColor float l = input.L, a = input.A, b = input.B; float c = MathF.Sqrt((a * a) + (b * b)); float hRadians = MathF.Atan2(b, a); - float hDegrees = MathFExtensions.RadianToDegree(hRadians); + float hDegrees = GeometryUtilities.RadianToDegree(hRadians); // Wrap the angle round at 360. - hDegrees = hDegrees % 360; + hDegrees %= 360; // Make sure it's not negative. while (hDegrees < 0) diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs similarity index 55% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs index ca8f23c564..dfbbc8f0c7 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieLabToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs @@ -1,18 +1,22 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; +using System.Numerics; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// /// Converts from to . /// - internal class CieLabToCieXyzConverter : IColorConversion + internal sealed class CieLabToCieXyzConverter { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public CieXyz Convert(in CieLab input) { // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html @@ -21,25 +25,20 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColor float fx = (a / 500F) + fy; float fz = fy - (b / 200F); - float fx3 = MathF.Pow(fx, 3F); - float fz3 = MathF.Pow(fz, 3F); + float fx3 = ImageMaths.Pow3(fx); + float fz3 = ImageMaths.Pow3(fz); float xr = fx3 > CieConstants.Epsilon ? fx3 : ((116F * fx) - 16F) / CieConstants.Kappa; - float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? MathF.Pow((l + 16F) / 116F, 3F) : l / CieConstants.Kappa; + float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? ImageMaths.Pow3((l + 16F) / 116F) : l / CieConstants.Kappa; float zr = fz3 > CieConstants.Epsilon ? fz3 : ((116F * fz) - 16F) / CieConstants.Kappa; - float wx = input.WhitePoint.X, wy = input.WhitePoint.Y, wz = input.WhitePoint.Z; + var wxyz = new Vector3(input.WhitePoint.X, input.WhitePoint.Y, input.WhitePoint.Z); // Avoids XYZ coordinates out range (restricted by 0 and XYZ reference white) - xr = xr.Clamp(0, 1F); - yr = yr.Clamp(0, 1F); - zr = zr.Clamp(0, 1F); + var xyzr = Vector3.Clamp(new Vector3(xr, yr, zr), Vector3.Zero, Vector3.One); - float x = xr * wx; - float y = yr * wy; - float z = zr * wz; - - return new CieXyz(x, y, z); + Vector3 xyz = xyzr * wxyz; + return new CieXyz(xyz); } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs similarity index 62% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs index 7f8e0fc1e9..ba5b8bfb79 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLchuvToCieLuvConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs @@ -4,21 +4,25 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// /// Converts from to . /// - internal class CieLchuvToCieLuvConverter : IColorConversion + internal sealed class CieLchuvToCieLuvConverter { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public CieLuv Convert(in CieLchuv input) { // Conversion algorithm described here: // https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 float l = input.L, c = input.C, hDegrees = input.H; - float hRadians = MathFExtensions.DegreeToRadian(hDegrees); + float hRadians = GeometryUtilities.DegreeToRadian(hDegrees); float u = c * MathF.Cos(hRadians); float v = c * MathF.Sin(hRadians); diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs similarity index 66% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs index 7a23e2da1a..3c7d356a5e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuv/CieLuvToCieLchuvConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs @@ -4,15 +4,19 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// /// Converts from to . /// - internal class CieLuvToCieLchuvConverter : IColorConversion + internal sealed class CieLuvToCieLchuvConverter { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public CieLchuv Convert(in CieLuv input) { // Conversion algorithm described here: @@ -20,10 +24,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLchuvCol float l = input.L, a = input.U, b = input.V; float c = MathF.Sqrt((a * a) + (b * b)); float hRadians = MathF.Atan2(b, a); - float hDegrees = MathFExtensions.RadianToDegree(hRadians); + float hDegrees = GeometryUtilities.RadianToDegree(hRadians); // Wrap the angle round at 360. - hDegrees = hDegrees % 360; + hDegrees %= 360; // Make sure it's not negative. while (hDegrees < 0) diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs similarity index 73% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs index cd2ec488d2..33f3ec3d3e 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieLuvToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs @@ -1,18 +1,20 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// /// Converts from to . /// - internal class CieLuvToCieXyzConverter : IColorConversion + internal sealed class CieLuvToCieXyzConverter { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result public CieXyz Convert(in CieLuv input) { // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html @@ -22,12 +24,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor float v0 = ComputeV0(input.WhitePoint); float y = l > CieConstants.Kappa * CieConstants.Epsilon - ? MathF.Pow((l + 16) / 116, 3) + ? ImageMaths.Pow3((l + 16) / 116) : l / CieConstants.Kappa; float a = ((52 * l / (u + (13 * l * u0))) - 1) / 3; float b = -5 * y; - float c = -0.3333333F; + const float c = -0.3333333F; float d = y * ((39 * l / (v + (13 * l * v0))) - 5); float x = (d - b) / (a - c); @@ -56,21 +58,17 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor /// /// The whitepoint /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private static float ComputeU0(in CieXyz input) - { - return (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z)); - } + => (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z)); /// /// Calculates the red-green chromacity based on the given whitepoint. /// /// The whitepoint /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private static float ComputeV0(in CieXyz input) - { - return (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z)); - } + => (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z)); } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs similarity index 61% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs index d15f7360e4..f33d1ddcc9 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyy/CieXyzAndCieXyyConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs @@ -4,16 +4,20 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// /// Color converter between CIE XYZ and CIE xyY /// for formulas. /// - internal class CieXyzAndCieXyyConverter : IColorConversion, IColorConversion + internal sealed class CieXyzAndCieXyyConverter { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public CieXyy Convert(in CieXyz input) { float x = input.X / (input.X + input.Y + input.Z); @@ -27,8 +31,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieXyyColor return new CieXyy(x, y, input.Y); } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public CieXyz Convert(in CieXyy input) { if (MathF.Abs(input.Y) < Constants.Epsilon) diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs similarity index 90% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs index ebf75e0d50..1cd511e819 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzAndHunterLabConverterBase.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs @@ -3,7 +3,7 @@ using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// /// The base class for converting between and color spaces. @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo /// /// The whitepoint /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static float ComputeKa(CieXyz whitePoint) { if (whitePoint.Equals(Illuminants.C)) @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo /// /// The whitepoint /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static float ComputeKb(CieXyz whitePoint) { if (whitePoint == Illuminants.C) diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/CieXyzAndLmsConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs similarity index 56% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/CieXyzAndLmsConverter.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs index c29496c37e..f860652b18 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/CieXyzAndLmsConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs @@ -4,12 +4,12 @@ using System.Numerics; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// - /// Color converter between CIE XYZ and LMS + /// Color converter between and /// - internal class CieXyzAndLmsConverter : IColorConversion, IColorConversion + internal sealed class CieXyzAndLmsConverter { /// /// Default transformation matrix used, when no other is set. (Bradford) @@ -23,7 +23,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap /// /// Initializes a new instance of the class. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyzAndLmsConverter() : this(DefaultTransformationMatrix) { @@ -36,41 +35,35 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap /// Definition of the cone response domain (see ), /// if not set will be used. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyzAndLmsConverter(Matrix4x4 transformationMatrix) { - this.TransformationMatrix = transformationMatrix; + this.transformationMatrix = transformationMatrix; + Matrix4x4.Invert(this.transformationMatrix, out this.inverseTransformationMatrix); } /// - /// Gets or sets the transformation matrix used for the conversion (definition of the cone response domain). - /// + /// Performs the conversion from the input to an instance of type. /// - public Matrix4x4 TransformationMatrix - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.transformationMatrix; - - set - { - this.transformationMatrix = value; - Matrix4x4.Invert(this.transformationMatrix, out this.inverseTransformationMatrix); - } - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public Lms Convert(in CieXyz input) { - Vector3 vector = Vector3.Transform(input.Vector, this.transformationMatrix); + var vector = Vector3.Transform(input.ToVector3(), this.transformationMatrix); + return new Lms(vector); } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public CieXyz Convert(in Lms input) { - Vector3 vector = Vector3.Transform(input.Vector, this.inverseTransformationMatrix); + var vector = Vector3.Transform(input.ToVector3(), this.inverseTransformationMatrix); + return new CieXyz(vector); } } diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs similarity index 79% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs index 0fe52e6af1..c155087ff5 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLab/CieXyzToCieLabConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs @@ -4,17 +4,16 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// /// Converts from to . /// - internal class CieXyzToCieLabConverter : IColorConversion + internal sealed class CieXyzToCieLabConverter { /// /// Initializes a new instance of the class. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyzToCieLabConverter() : this(CieLab.DefaultWhitePoint) { @@ -24,19 +23,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLabColor /// Initializes a new instance of the class. /// /// The target reference lab white point - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public CieXyzToCieLabConverter(CieXyz labWhitePoint) - { - this.LabWhitePoint = labWhitePoint; - } + public CieXyzToCieLabConverter(CieXyz labWhitePoint) => this.LabWhitePoint = labWhitePoint; /// /// Gets the target reference whitepoint. When not set, is used. /// public CieXyz LabWhitePoint { get; } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public CieLab Convert(in CieXyz input) { // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieXyzToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs similarity index 79% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieXyzToCieLuvConverter.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs index c34a2455a7..7f2bb0cf6a 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuv/CieXyzToCieLuvConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs @@ -3,19 +3,17 @@ using System; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// /// Converts from to . /// - internal class CieXyzToCieLuvConverter : IColorConversion + internal sealed class CieXyzToCieLuvConverter { /// /// Initializes a new instance of the class. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyzToCieLuvConverter() : this(CieLuv.DefaultWhitePoint) { @@ -25,19 +23,18 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor /// Initializes a new instance of the class. /// /// The target reference luv white point - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public CieXyzToCieLuvConverter(CieXyz luvWhitePoint) - { - this.LuvWhitePoint = luvWhitePoint; - } + public CieXyzToCieLuvConverter(CieXyz luvWhitePoint) => this.LuvWhitePoint = luvWhitePoint; /// /// Gets the target reference whitepoint. When not set, is used. /// public CieXyz LuvWhitePoint { get; } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result public CieLuv Convert(in CieXyz input) { // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Luv.html @@ -77,18 +74,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.CieLuvColor /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float ComputeUp(in CieXyz input) - { - return (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z)); - } + => (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z)); /// /// Calculates the red-green chromacity based on the given whitepoint. /// /// The whitepoint /// The + [MethodImpl(InliningOptions.ShortMethod)] private static float ComputeVp(in CieXyz input) - { - return (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z)); - } + => (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z)); } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs similarity index 65% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs index af681e981f..f21235d06c 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/CieXyzToHunterLabConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs @@ -4,17 +4,16 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// - /// Color converter between CieXyz and HunterLab + /// Color converter between and /// - internal class CieXyzToHunterLabConverter : CieXyzAndHunterLabConverterBase, IColorConversion + internal sealed class CieXyzToHunterLabConverter : CieXyzAndHunterLabConverterBase { /// /// Initializes a new instance of the class. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyzToHunterLabConverter() : this(HunterLab.DefaultWhitePoint) { @@ -24,19 +23,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo /// Initializes a new instance of the class. /// /// The hunter Lab white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public CieXyzToHunterLabConverter(CieXyz labWhitePoint) - { - this.HunterLabWhitePoint = labWhitePoint; - } + public CieXyzToHunterLabConverter(CieXyz labWhitePoint) => this.HunterLabWhitePoint = labWhitePoint; /// /// Gets the target reference white. When not set, is used. /// public CieXyz HunterLabWhitePoint { get; } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public HunterLab Convert(in CieXyz input) { // Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab @@ -46,9 +45,11 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabCo float ka = ComputeKa(this.HunterLabWhitePoint); float kb = ComputeKb(this.HunterLabWhitePoint); - float l = 100 * MathF.Sqrt(y / yn); - float a = ka * (((x / xn) - (y / yn)) / MathF.Sqrt(y / yn)); - float b = kb * (((y / yn) - (z / zn)) / MathF.Sqrt(y / yn)); + float yByYn = y / yn; + float sqrtYbyYn = MathF.Sqrt(yByYn); + float l = 100 * sqrtYbyYn; + float a = ka * (((x / xn) - yByYn) / sqrtYbyYn); + float b = kb * ((yByYn - (z / zn)) / sqrtYbyYn); if (float.IsNaN(a)) { diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/CieXyzToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs similarity index 64% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/CieXyzToLinearRgbConverter.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs index 217698c23a..3812cdbdd8 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/CieXyzToLinearRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs @@ -2,13 +2,14 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; +using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// - /// Color converter between CieXyz and LinearRgb + /// Color converter between and /// - internal sealed class CieXyzToLinearRgbConverter : LinearRgbAndCieXyzConverterBase, IColorConversion + internal sealed class CieXyzToLinearRgbConverter : LinearRgbAndCieXyzConverterBase { private readonly Matrix4x4 conversionMatrix; @@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// Initializes a new instance of the class. /// /// The target working space. - public CieXyzToLinearRgbConverter(RgbWorkingSpace workingSpace) + public CieXyzToLinearRgbConverter(RgbWorkingSpaceBase workingSpace) { this.TargetWorkingSpace = workingSpace; this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace); @@ -33,13 +34,18 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// /// Gets the target working space /// - public RgbWorkingSpace TargetWorkingSpace { get; } + public RgbWorkingSpaceBase TargetWorkingSpace { get; } - /// + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public LinearRgb Convert(in CieXyz input) { Matrix4x4.Invert(this.conversionMatrix, out Matrix4x4 inverted); - Vector3 vector = Vector3.Transform(input.Vector, inverted); + var vector = Vector3.Transform(input.ToVector3(), inverted); return new LinearRgb(vector, this.TargetWorkingSpace); } } diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs new file mode 100644 index 0000000000..29fd32905b --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +{ + /// + /// Color converter between and + /// + internal sealed class CmykAndRgbConverter + { + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] + public Rgb Convert(in Cmyk input) + { + Vector3 rgb = (Vector3.One - new Vector3(input.C, input.M, input.Y)) * (Vector3.One - new Vector3(input.K)); + return new Rgb(rgb); + } + + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] + public Cmyk Convert(in Rgb input) + { + // To CMY + Vector3 cmy = Vector3.One - input.ToVector3(); + + // To CMYK + var k = new Vector3(MathF.Min(cmy.X, MathF.Min(cmy.Y, cmy.Z))); + + if (MathF.Abs(k.X - 1F) < Constants.Epsilon) + { + return new Cmyk(0, 0, 0, 1F); + } + + cmy = (cmy - k) / (Vector3.One - k); + + return new Cmyk(cmy.X, cmy.Y, cmy.Z, k.X); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs similarity index 82% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs index 1bec834a80..761313b7e0 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsl/HslAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs @@ -4,16 +4,20 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// /// Color converter between HSL and Rgb /// See for formulas. /// - internal class HslAndRgbConverter : IColorConversion, IColorConversion + internal sealed class HslAndRgbConverter { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public Rgb Convert(in Hsl input) { float rangedH = input.H / 360F; @@ -43,8 +47,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSap return new Rgb(r, g, b); } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public Hsl Convert(in Rgb input) { float r = input.R; @@ -103,7 +111,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSap /// /// The . /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private static float GetColorComponent(float first, float second, float third) { third = MoveIntoRange(third); @@ -134,16 +142,16 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HslColorSap /// /// The . /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private static float MoveIntoRange(float value) { if (value < 0F) { - value += 1F; + value++; } else if (value > 1F) { - value -= 1F; + value--; } return value; diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs similarity index 80% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs index f2c4cc188f..20ada7e7dd 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Hsv/HsvAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs @@ -4,16 +4,20 @@ using System; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// /// Color converter between HSV and Rgb /// See for formulas. /// - internal class HsvAndRgbConverter : IColorConversion, IColorConversion + internal sealed class HsvAndRgbConverter { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public Rgb Convert(in Hsv input) { float s = input.S; @@ -75,8 +79,12 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HsvColorSap return new Rgb(r, g, b); } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public Hsv Convert(in Rgb input) { float r = input.R; diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs new file mode 100644 index 0000000000..4d6808e6c0 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs @@ -0,0 +1,39 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +{ + /// + /// Color converter between and + /// + internal sealed class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase + { + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] + public CieXyz Convert(in HunterLab input) + { + // Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab + float l = input.L, a = input.A, b = input.B; + float xn = input.WhitePoint.X, yn = input.WhitePoint.Y, zn = input.WhitePoint.Z; + + float ka = ComputeKa(input.WhitePoint); + float kb = ComputeKb(input.WhitePoint); + + float pow = ImageMaths.Pow2(l / 100F); + float sqrtPow = MathF.Sqrt(pow); + float y = pow * yn; + + float x = (((a / ka) * sqrtPow) + pow) * xn; + float z = (((b / kb) * sqrtPow) - pow) * (-zn); + + return new CieXyz(x, y, z); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs similarity index 68% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs index bc11c51b54..a93773262c 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbAndCieXyzConverterBase.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs @@ -3,10 +3,10 @@ using System.Numerics; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// - /// Provides base methods for converting between Rgb and CieXyz color spaces. + /// Provides base methods for converting between and color spaces. /// internal abstract class LinearRgbAndCieXyzConverterBase { @@ -15,10 +15,9 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// /// The Rgb working space. /// The based on the chromaticity and working space. - public static Matrix4x4 GetRgbToCieXyzMatrix(RgbWorkingSpace workingSpace) + public static Matrix4x4 GetRgbToCieXyzMatrix(RgbWorkingSpaceBase workingSpace) { DebugGuard.NotNull(workingSpace, nameof(workingSpace)); - RgbPrimariesChromaticityCoordinates chromaticity = workingSpace.ChromaticityCoordinates; float xr = chromaticity.R.X; @@ -42,23 +41,35 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap var xyzMatrix = new Matrix4x4 { - M11 = mXr, M21 = mXg, M31 = mXb, - M12 = Yr, M22 = Yg, M32 = Yb, - M13 = mZr, M23 = mZg, M33 = mZb, + M11 = mXr, + M21 = mXg, + M31 = mXb, + M12 = Yr, + M22 = Yg, + M32 = Yb, + M13 = mZr, + M23 = mZg, + M33 = mZb, M44 = 1F }; Matrix4x4.Invert(xyzMatrix, out Matrix4x4 inverseXyzMatrix); - Vector3 vector = Vector3.Transform(workingSpace.WhitePoint.Vector, inverseXyzMatrix); + var vector = Vector3.Transform(workingSpace.WhitePoint.ToVector3(), inverseXyzMatrix); // Use transposed Rows/Columns // TODO: Is there a built in method for this multiplication? return new Matrix4x4 { - M11 = vector.X * mXr, M21 = vector.Y * mXg, M31 = vector.Z * mXb, - M12 = vector.X * Yr, M22 = vector.Y * Yg, M32 = vector.Z * Yb, - M13 = vector.X * mZr, M23 = vector.Y * mZg, M33 = vector.Z * mZb, + M11 = vector.X * mXr, + M21 = vector.Y * mXg, + M31 = vector.Z * mXb, + M12 = vector.X * Yr, + M22 = vector.Y * Yg, + M32 = vector.Z * Yb, + M13 = vector.X * mZr, + M23 = vector.Y * mZg, + M33 = vector.Z * mZb, M44 = 1F }; } diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs similarity index 65% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs index e597b66af0..1030ac9819 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToCieXyzConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs @@ -2,13 +2,14 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; +using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// - /// Color converter between LinearRgb and CieXyz + /// Color converter between and /// - internal sealed class LinearRgbToCieXyzConverter : LinearRgbAndCieXyzConverterBase, IColorConversion + internal sealed class LinearRgbToCieXyzConverter : LinearRgbAndCieXyzConverterBase { private readonly Matrix4x4 conversionMatrix; @@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// Initializes a new instance of the class. /// /// The target working space. - public LinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace) + public LinearRgbToCieXyzConverter(RgbWorkingSpaceBase workingSpace) { this.SourceWorkingSpace = workingSpace; this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace); @@ -33,14 +34,19 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap /// /// Gets the source working space /// - public RgbWorkingSpace SourceWorkingSpace { get; } + public RgbWorkingSpaceBase SourceWorkingSpace { get; } - /// + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public CieXyz Convert(in LinearRgb input) { DebugGuard.IsTrue(input.WorkingSpace.Equals(this.SourceWorkingSpace), nameof(input.WorkingSpace), "Input and source working spaces must be equal."); - Vector3 vector = Vector3.Transform(input.Vector, this.conversionMatrix); + var vector = Vector3.Transform(input.ToVector3(), this.conversionMatrix); return new CieXyz(vector); } } diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs new file mode 100644 index 0000000000..1cc055bee2 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs @@ -0,0 +1,29 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +{ + /// + /// Color converter between and + /// + internal sealed class LinearRgbToRgbConverter + { + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] + public Rgb Convert(in LinearRgb input) + { + var vector = input.ToVector3(); + vector.X = input.WorkingSpace.Compress(vector.X); + vector.Y = input.WorkingSpace.Compress(vector.Y); + vector.Z = input.WorkingSpace.Compress(vector.Z); + + return new Rgb(vector, input.WorkingSpace); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs new file mode 100644 index 0000000000..03912a421e --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs @@ -0,0 +1,29 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +{ + /// + /// Color converter between Rgb and LinearRgb + /// + internal class RgbToLinearRgbConverter + { + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] + public LinearRgb Convert(in Rgb input) + { + var vector = input.ToVector3(); + vector.X = input.WorkingSpace.Expand(vector.X); + vector.Y = input.WorkingSpace.Expand(vector.Y); + vector.Z = input.WorkingSpace.Expand(vector.Z); + + return new LinearRgb(vector, input.WorkingSpace); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs similarity index 62% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs index e8d32572a4..4ac3ad3cf9 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/YCbCr/YCbCrAndRgbConverter.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs @@ -5,18 +5,22 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// - /// Color converter between YCbCr and Rgb + /// Color converter between and /// See for formulas. /// - internal class YCbCrAndRgbConverter : IColorConversion, IColorConversion + internal sealed class YCbCrAndRgbConverter { private static readonly Vector3 MaxBytes = new Vector3(255F); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public Rgb Convert(in YCbCr input) { float y = input.Y; @@ -30,11 +34,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.YCbCrColorS return new Rgb(new Vector3(r, g, b) / MaxBytes); } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + /// Performs the conversion from the input to an instance of type. + /// + /// The input color instance. + /// The converted result + [MethodImpl(InliningOptions.ShortMethod)] public YCbCr Convert(in Rgb input) { - Vector3 rgb = input.Vector * MaxBytes; + Vector3 rgb = input.ToVector3() * MaxBytes; float r = rgb.X; float g = rgb.Y; float b = rgb.Z; diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs deleted file mode 100644 index eba9fe1c83..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/HunterLab/HunterLabToCieXyzConverter.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.HunterLabColorSapce -{ - /// - /// Color converter between HunterLab and CieXyz - /// - internal class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase, IColorConversion - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public CieXyz Convert(in HunterLab input) - { - // Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab - float l = input.L, a = input.A, b = input.B; - float xn = input.WhitePoint.X, yn = input.WhitePoint.Y, zn = input.WhitePoint.Z; - - float ka = ComputeKa(input.WhitePoint); - float kb = ComputeKb(input.WhitePoint); - - float y = MathF.Pow(l / 100F, 2) * yn; - float x = (((a / ka) * MathF.Sqrt(y / yn)) + (y / yn)) * xn; - float z = (((b / kb) * MathF.Sqrt(y / yn)) - (y / yn)) * (-zn); - - return new CieXyz(x, y, z); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs similarity index 55% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs index d535d73342..37e4b1a1a6 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Lms/LmsAdaptationMatrix.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs @@ -3,11 +3,10 @@ using System.Numerics; -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// - /// AdaptionMatrix3X3 used for transformation from XYZ to LMS, defining the cone response domain. + /// Matrices used for transformation from to , defining the cone response domain. /// Used in /// /// @@ -17,7 +16,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap /// DISCo, Department of Informatics, Systems and Communication, University of Milan-Bicocca, viale Sarca 336, 20126 Milan, Italy /// https://web.stanford.edu/~sujason/ColorBalancing/Papers/Two%20New%20von%20Kries%20Based%20Chromatic%20Adaptation.pdf /// - internal static class LmsAdaptationMatrix + public static class LmsAdaptationMatrix { /// /// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65) @@ -25,9 +24,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap public static readonly Matrix4x4 VonKriesHPEAdjusted = Matrix4x4.Transpose(new Matrix4x4 { - M11 = 0.40024F, M12 = 0.7076F, M13 = -0.08081F, - M21 = -0.2263F, M22 = 1.16532F, M23 = 0.0457F, - M31 = 0, M32 = 0, M33 = 0.91822F, + M11 = 0.40024F, + M12 = 0.7076F, + M13 = -0.08081F, + M21 = -0.2263F, + M22 = 1.16532F, + M23 = 0.0457F, + M31 = 0, + M32 = 0, + M33 = 0.91822F, M44 = 1F // Important for inverse transforms. }); @@ -37,9 +42,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap public static readonly Matrix4x4 VonKriesHPE = Matrix4x4.Transpose(new Matrix4x4 { - M11 = 0.3897F, M12 = 0.6890F, M13 = -0.0787F, - M21 = -0.2298F, M22 = 1.1834F, M23 = 0.0464F, - M31 = 0, M32 = 0, M33 = 1F, + M11 = 0.3897F, + M12 = 0.6890F, + M13 = -0.0787F, + M21 = -0.2298F, + M22 = 1.1834F, + M23 = 0.0464F, + M31 = 0, + M32 = 0, + M33 = 1F, M44 = 1F }); @@ -54,9 +65,15 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap public static readonly Matrix4x4 Bradford = Matrix4x4.Transpose(new Matrix4x4 { - M11 = 0.8951F, M12 = 0.2664F, M13 = -0.1614F, - M21 = -0.7502F, M22 = 1.7135F, M23 = 0.0367F, - M31 = 0.0389F, M32 = -0.0685F, M33 = 1.0296F, + M11 = 0.8951F, + M12 = 0.2664F, + M13 = -0.1614F, + M21 = -0.7502F, + M22 = 1.7135F, + M23 = 0.0367F, + M31 = 0.0389F, + M32 = -0.0685F, + M33 = 1.0296F, M44 = 1F }); @@ -65,35 +82,53 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSap /// public static readonly Matrix4x4 BradfordSharp = Matrix4x4.Transpose(new Matrix4x4 - { - M11 = 1.2694F, M12 = -0.0988F, M13 = -0.1706F, - M21 = -0.8364F, M22 = 1.8006F, M23 = 0.0357F, - M31 = 0.0297F, M32 = -0.0315F, M33 = 1.0018F, - M44 = 1F - }); + { + M11 = 1.2694F, + M12 = -0.0988F, + M13 = -0.1706F, + M21 = -0.8364F, + M22 = 1.8006F, + M23 = 0.0357F, + M31 = 0.0297F, + M32 = -0.0315F, + M33 = 1.0018F, + M44 = 1F + }); /// /// CMCCAT2000 (fitted from all available color data sets) /// public static readonly Matrix4x4 CMCCAT2000 = Matrix4x4.Transpose(new Matrix4x4 - { - M11 = 0.7982F, M12 = 0.3389F, M13 = -0.1371F, - M21 = -0.5918F, M22 = 1.5512F, M23 = 0.0406F, - M31 = 0.0008F, M32 = 0.239F, M33 = 0.9753F, - M44 = 1F - }); + { + M11 = 0.7982F, + M12 = 0.3389F, + M13 = -0.1371F, + M21 = -0.5918F, + M22 = 1.5512F, + M23 = 0.0406F, + M31 = 0.0008F, + M32 = 0.239F, + M33 = 0.9753F, + M44 = 1F + }); /// /// CAT02 (optimized for minimizing CIELAB differences) /// public static readonly Matrix4x4 CAT02 = Matrix4x4.Transpose(new Matrix4x4 - { - M11 = 0.7328F, M12 = 0.4296F, M13 = -0.1624F, - M21 = -0.7036F, M22 = 1.6975F, M23 = 0.0061F, - M31 = 0.0030F, M32 = 0.0136F, M33 = 0.9834F, - M44 = 1F - }); + { + M11 = 0.7328F, + M12 = 0.4296F, + M13 = -0.1624F, + M21 = -0.7036F, + M22 = 1.6975F, + M23 = 0.0061F, + M31 = 0.0030F, + M32 = 0.0136F, + M33 = 0.9834F, + M44 = 1F + }); } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs similarity index 78% rename from src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs rename to src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs index 4359d666e6..68b4d95fc6 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RGBPrimariesChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs @@ -3,13 +3,13 @@ using System; -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation { /// /// Represents the chromaticity coordinates of RGB primaries. - /// One of the specifiers of . + /// One of the specifiers of . /// - internal readonly struct RgbPrimariesChromaticityCoordinates : IEquatable + public readonly struct RgbPrimariesChromaticityCoordinates : IEquatable { /// /// Initializes a new instance of the struct. @@ -40,13 +40,13 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap public CieXyChromaticityCoordinates B { get; } /// - /// Compares two objects for equality. + /// Compares two objects for equality. /// /// - /// The on the left side of the operand. + /// The on the left side of the operand. /// /// - /// The on the right side of the operand. + /// The on the right side of the operand. /// /// /// True if the current left is equal to the parameter; otherwise, false. @@ -57,13 +57,13 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap } /// - /// Compares two objects for inequality + /// Compares two objects for inequality /// /// - /// The on the left side of the operand. + /// The on the left side of the operand. /// /// - /// The on the right side of the operand. + /// The on the right side of the operand. /// /// /// True if the current left is unequal to the parameter; otherwise, false. @@ -92,8 +92,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSap { int hashCode = this.R.GetHashCode(); hashCode = (hashCode * 397) ^ this.G.GetHashCode(); - hashCode = (hashCode * 397) ^ this.B.GetHashCode(); - return hashCode; + return (hashCode * 397) ^ this.B.GetHashCode(); } } } diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs deleted file mode 100644 index a7b0ecc984..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/GammaCompanding.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Implements gamma companding - /// - /// - /// - /// - /// - internal class GammaCompanding : ICompanding - { - /// - /// Initializes a new instance of the class. - /// - /// The gamma value. - public GammaCompanding(float gamma) - { - this.Gamma = gamma; - } - - /// - /// Gets the gamma value - /// - public float Gamma { get; } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Expand(float channel) - { - return MathF.Pow(channel, this.Gamma); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Compress(float channel) - { - return MathF.Pow(channel, 1 / this.Gamma); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs deleted file mode 100644 index 309ae21833..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LCompanding.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Implements L* companding - /// - /// - /// For more info see: - /// - /// - /// - internal class LCompanding : ICompanding - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Expand(float channel) - { - return channel <= 0.08 ? 100 * channel / CieConstants.Kappa : MathF.Pow((channel + 0.16F) / 1.16F, 3); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Compress(float channel) - { - return channel <= CieConstants.Epsilon - ? channel * CieConstants.Kappa / 100F - : MathF.Pow(1.16F * channel, 0.3333333F) - 0.16F; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs deleted file mode 100644 index 34873c1f5f..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/LinearRgbToRgbConverter.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Color converter between LinearRgb and Rgb - /// - internal class LinearRgbToRgbConverter : IColorConversion - { - /// - public Rgb Convert(in LinearRgb input) - { - Vector3 vector = input.Vector; - vector.X = input.WorkingSpace.Companding.Compress(vector.X); - vector.Y = input.WorkingSpace.Companding.Compress(vector.Y); - vector.Z = input.WorkingSpace.Companding.Compress(vector.Z); - - return new Rgb(vector, input.WorkingSpace); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs deleted file mode 100644 index 0b2b28b2d2..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec2020Companding.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Implements Rec. 2020 companding function (for 12-bits). - /// - /// - /// - /// For 10-bits, companding is identical to - /// - internal class Rec2020Companding : ICompanding - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Expand(float channel) - { - return channel < 0.08145F ? channel / 4.5F : MathF.Pow((channel + 0.0993F) / 1.0993F, 2.222222F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Compress(float channel) - { - return channel < 0.0181F ? 4500F * channel : (1.0993F * channel) - 0.0993F; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs deleted file mode 100644 index 439cb29018..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/Rec709Companding.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Implements the Rec. 709 companding function - /// - /// - /// http://en.wikipedia.org/wiki/Rec._709 - /// - internal class Rec709Companding : ICompanding - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Expand(float channel) - { - return channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, 2.222222F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Compress(float channel) - { - return channel < 0.018F ? 4500F * channel : (1.099F * channel) - 0.099F; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs deleted file mode 100644 index 4cc3d607f6..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbToLinearRgbConverter.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Color converter between Rgb and LinearRgb - /// - internal class RgbToLinearRgbConverter : IColorConversion - { - /// - public LinearRgb Convert(in Rgb input) - { - Vector3 vector = input.Vector; - vector.X = input.WorkingSpace.Companding.Expand(vector.X); - vector.Y = input.WorkingSpace.Companding.Expand(vector.Y); - vector.Z = input.WorkingSpace.Companding.Expand(vector.Z); - - return new LinearRgb(vector, input.WorkingSpace); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs deleted file mode 100644 index f4a79c744e..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/RgbWorkingSpace.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Trivial implementation of - /// - internal class RgbWorkingSpace - { - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The function pair for converting to and back. - /// The chromaticity of the rgb primaries. - public RgbWorkingSpace(CieXyz referenceWhite, ICompanding companding, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - { - this.WhitePoint = referenceWhite; - this.Companding = companding; - this.ChromaticityCoordinates = chromaticityCoordinates; - } - - /// - /// Gets the reference white point - /// - public CieXyz WhitePoint { get; } - - /// - /// Gets the function pair for converting to and back. - /// - public ICompanding Companding { get; } - - /// - /// Gets the chromaticity of the rgb primaries. - /// - public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } - - /// - /// 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. - /// - public static bool operator ==(RgbWorkingSpace left, RgbWorkingSpace right) - { - return Equals(left, 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. - /// - public static bool operator !=(RgbWorkingSpace left, RgbWorkingSpace right) - { - return !Equals(left, right); - } - - public override bool Equals(object obj) - { - return obj is RgbWorkingSpace other && this.Equals(other); - } - - public bool Equals(RgbWorkingSpace other) - { - // TODO: Object.Equals for ICompanding will be slow. - return this.WhitePoint.Equals(other.WhitePoint) - && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates) - && Equals(this.Companding, other.Companding); - } - - /// - public override int GetHashCode() - { - unchecked - { - int hashCode = this.WhitePoint.GetHashCode(); - hashCode = (hashCode * 397) ^ this.ChromaticityCoordinates.GetHashCode(); - hashCode = (hashCode * 397) ^ (this.Companding?.GetHashCode() ?? 0); - return hashCode; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs deleted file mode 100644 index bde1b9123f..0000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Rgb/SRgbCompanding.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce -{ - /// - /// Implements sRGB companding - /// - /// - /// For more info see: - /// - /// - /// - internal class SRgbCompanding : ICompanding - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Expand(float channel) - { - return channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Compress(float channel) - { - return channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs new file mode 100644 index 0000000000..73aa60b6cb --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs @@ -0,0 +1,66 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces.Companding; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +{ + /// + /// The gamma working space. + /// + public class GammaWorkingSpace : RgbWorkingSpaceBase + { + /// + /// Initializes a new instance of the class. + /// + /// The gamma value. + /// The reference white point. + /// The chromaticity of the rgb primaries. + public GammaWorkingSpace(float gamma, CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + : base(referenceWhite, chromaticityCoordinates) => this.Gamma = gamma; + + /// + /// Gets the gamma value. + /// + public float Gamma { get; } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override float Compress(float channel) => GammaCompanding.Compress(channel, this.Gamma); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override float Expand(float channel) => GammaCompanding.Expand(channel, this.Gamma); + + /// + public override bool Equals(object obj) + { + if (obj is null) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj is GammaWorkingSpace other) + { + return this.Gamma.Equals(other.Gamma) + && this.WhitePoint.Equals(other.WhitePoint) + && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); + } + + return false; + } + + /// + public override int GetHashCode() + { + int hash = base.GetHashCode(); + return HashHelpers.Combine(hash, this.Gamma.GetHashCode()); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs new file mode 100644 index 0000000000..16617ea242 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces.Companding; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +{ + /// + /// L* working space. + /// + public sealed class LWorkingSpace : RgbWorkingSpaceBase + { + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + public LWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + : base(referenceWhite, chromaticityCoordinates) + { + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override float Compress(float channel) => LCompanding.Compress(channel); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override float Expand(float channel) => LCompanding.Expand(channel); + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs new file mode 100644 index 0000000000..9ba1ff8811 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces.Companding; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +{ + /// + /// Rec. 2020 (ITU-R Recommendation BT.2020F) working space. + /// + public sealed class Rec2020WorkingSpace : RgbWorkingSpaceBase + { + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + public Rec2020WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + : base(referenceWhite, chromaticityCoordinates) + { + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override float Compress(float channel) => Rec2020Companding.Compress(channel); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override float Expand(float channel) => Rec2020Companding.Expand(channel); + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs new file mode 100644 index 0000000000..88623e958d --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces.Companding; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +{ + /// + /// Rec. 709 (ITU-R Recommendation BT.709) working space. + /// + public sealed class Rec709WorkingSpace : RgbWorkingSpaceBase + { + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + public Rec709WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + : base(referenceWhite, chromaticityCoordinates) + { + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override float Compress(float channel) => Rec709Companding.Compress(channel); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override float Expand(float channel) => Rec709Companding.Expand(channel); + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs new file mode 100644 index 0000000000..5a89321c8f --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpaceBase.cs @@ -0,0 +1,83 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +{ + /// + /// Base class for all implementations of . + /// + public abstract class RgbWorkingSpaceBase + { + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + protected RgbWorkingSpaceBase(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + { + this.WhitePoint = referenceWhite; + this.ChromaticityCoordinates = chromaticityCoordinates; + } + + /// + /// Gets the reference white point + /// + public CieXyz WhitePoint { get; } + + /// + /// Gets the chromaticity of the rgb primaries. + /// + public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } + + /// + /// Expands a companded channel to its linear equivalent with respect to the energy. + /// + /// + /// For more info see: + /// + /// + /// The channel value. + /// The representing the linear channel value. + public abstract float Expand(float channel); + + /// + /// Compresses an uncompanded channel (linear) to its nonlinear equivalent (depends on the RGB color system). + /// + /// + /// For more info see: + /// + /// + /// The channel value. + /// The representing the nonlinear channel value. + public abstract float Compress(float channel); + + /// + public override bool Equals(object obj) + { + if (obj is null) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj is RgbWorkingSpaceBase other) + { + return this.WhitePoint.Equals(other.WhitePoint) + && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); + } + + return false; + } + + /// + public override int GetHashCode() + { + int hash = this.WhitePoint.GetHashCode(); + return HashHelpers.Combine(hash, this.ChromaticityCoordinates.GetHashCode()); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs new file mode 100644 index 0000000000..b44db06817 --- /dev/null +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorSpaces.Companding; + +namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation +{ + /// + /// The sRgb working space. + /// + public sealed class SRgbWorkingSpace : RgbWorkingSpaceBase + { + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + public SRgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + : base(referenceWhite, chromaticityCoordinates) + { + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override float Compress(float channel) => SRgbCompanding.Compress(channel); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override float Expand(float channel) => SRgbCompanding.Expand(channel); + } +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs index 0ab194af2f..85a36331b2 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/VonKriesChromaticAdaptation.cs @@ -1,8 +1,11 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces.Conversion { @@ -13,7 +16,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// Transformation described here: /// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html /// - internal class VonKriesChromaticAdaptation : IChromaticAdaptation + public class VonKriesChromaticAdaptation : IChromaticAdaptation { private readonly CieXyzAndLmsConverter converter; @@ -41,27 +44,59 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion /// Initializes a new instance of the class. /// /// The color converter - public VonKriesChromaticAdaptation(CieXyzAndLmsConverter converter) - { - this.converter = converter; - } + internal VonKriesChromaticAdaptation(CieXyzAndLmsConverter converter) => this.converter = converter; /// - public CieXyz Transform(in CieXyz sourceColor, in CieXyz sourceWhitePoint, in CieXyz targetWhitePoint) + public CieXyz Transform(in CieXyz source, in CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint) { - if (sourceWhitePoint.Equals(targetWhitePoint)) + if (sourceWhitePoint.Equals(destinationWhitePoint)) { - return sourceColor; + return source; } - Lms sourceColorLms = this.converter.Convert(sourceColor); + Lms sourceColorLms = this.converter.Convert(source); Lms sourceWhitePointLms = this.converter.Convert(sourceWhitePoint); - Lms targetWhitePointLms = this.converter.Convert(targetWhitePoint); + Lms targetWhitePointLms = this.converter.Convert(destinationWhitePoint); - var vector = new Vector3(targetWhitePointLms.L / sourceWhitePointLms.L, targetWhitePointLms.M / sourceWhitePointLms.M, targetWhitePointLms.S / sourceWhitePointLms.S); - var targetColorLms = new Lms(Vector3.Multiply(vector, sourceColorLms.Vector)); + Vector3 vector = targetWhitePointLms.ToVector3() / sourceWhitePointLms.ToVector3(); + var targetColorLms = new Lms(Vector3.Multiply(vector, sourceColorLms.ToVector3())); return this.converter.Convert(targetColorLms); } + + /// + public void Transform( + ReadOnlySpan source, + Span destination, + CieXyz sourceWhitePoint, + in CieXyz destinationWhitePoint) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + if (sourceWhitePoint.Equals(destinationWhitePoint)) + { + source.CopyTo(destination.Slice(0, count)); + return; + } + + ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); + ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); + + for (int i = 0; i < count; i++) + { + ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); + ref CieXyz dp = ref Unsafe.Add(ref destRef, i); + + Lms sourceColorLms = this.converter.Convert(sp); + Lms sourceWhitePointLms = this.converter.Convert(sourceWhitePoint); + Lms targetWhitePointLms = this.converter.Convert(destinationWhitePoint); + + Vector3 vector = targetWhitePointLms.ToVector3() / sourceWhitePointLms.ToVector3(); + var targetColorLms = new Lms(Vector3.Multiply(vector, sourceColorLms.ToVector3())); + + dp = this.converter.Convert(targetColorLms); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Hsl.cs b/src/ImageSharp/ColorSpaces/Hsl.cs index 8ed4067539..f6e531df35 100644 --- a/src/ImageSharp/ColorSpaces/Hsl.cs +++ b/src/ImageSharp/ColorSpaces/Hsl.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; @@ -11,17 +10,28 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// Represents a Hsl (hue, saturation, lightness) color. /// - internal readonly struct Hsl : IColorVector, IEquatable, IAlmostEquatable + public readonly struct Hsl : IEquatable { + private static readonly Vector3 Min = Vector3.Zero; + private static readonly Vector3 Max = new Vector3(360, 1, 1); + /// - /// Max range used for clamping. + /// Gets the hue component. + /// A value ranging between 0 and 360. /// - private static readonly Vector3 VectorMax = new Vector3(360, 1, 1); + public readonly float H; /// - /// The backing vector for SIMD support. + /// Gets the saturation component. + /// A value ranging between 0 and 1. /// - private readonly Vector3 backingVector; + public readonly float S; + + /// + /// Gets the lightness component. + /// A value ranging between 0 and 1. + /// + public readonly float L; /// /// Initializes a new instance of the struct. @@ -29,7 +39,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The h hue component. /// The s saturation component. /// The l value (lightness) component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Hsl(float h, float s, float l) : this(new Vector3(h, s, l)) { @@ -39,118 +49,61 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Initializes a new instance of the struct. /// /// The vector representing the h, s, l components. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Hsl(Vector3 vector) { - this.backingVector = Vector3.Clamp(vector, Vector3.Zero, VectorMax); - } - - /// - /// Gets the hue component. - /// A value ranging between 0 and 360. - /// - public float H - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.X; + vector = Vector3.Clamp(vector, Min, Max); + this.H = vector.X; + this.S = vector.Y; + this.L = vector.Z; } - /// - /// Gets the saturation component. - /// A value ranging between 0 and 1. - /// - public float S - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Y; - } - - /// - /// Gets the lightness component. - /// A value ranging between 0 and 1. - /// - public float L - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Z; - } - - /// - public Vector3 Vector => this.backingVector; - /// /// Compares two objects for equality. /// /// /// The on the left side of the operand. /// - /// - /// The on the right side of the operand. - /// + /// The on the right side of the operand. /// /// True if the current left is equal to the parameter; otherwise, false. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Hsl left, Hsl right) - { - return left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Hsl left, Hsl 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 !=(Hsl left, Hsl right) - { - return !left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Hsl left, Hsl right) => !left.Equals(right); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public override int GetHashCode() { - return this.backingVector.GetHashCode(); + int hash = this.H.GetHashCode(); + hash = HashHelpers.Combine(hash, this.S.GetHashCode()); + return HashHelpers.Combine(hash, this.L.GetHashCode()); } /// - public override string ToString() - { - return this.Equals(default) - ? "Hsl [ Empty ]" - : $"Hsl [ H={this.H:#0.##}, S={this.S:#0.##}, L={this.L:#0.##} ]"; - } + public override string ToString() => FormattableString.Invariant($"Hsl({this.H:#0.##}, {this.S:#0.##}, {this.L:#0.##})"); /// - public override bool Equals(object obj) - { - return obj is Hsl other && this.Equals(other); - } + public override bool Equals(object obj) => obj is Hsl other && this.Equals(other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(Hsl other) { - return this.backingVector.Equals(other.backingVector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool AlmostEquals(Hsl other, float precision) - { - var result = Vector3.Abs(this.backingVector - other.backingVector); - - return result.X <= precision - && result.Y <= precision - && result.Z <= precision; + return this.H.Equals(other.H) + && this.S.Equals(other.S) + && this.L.Equals(other.L); } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Hsv.cs b/src/ImageSharp/ColorSpaces/Hsv.cs index 78a49097ed..631f03d09f 100644 --- a/src/ImageSharp/ColorSpaces/Hsv.cs +++ b/src/ImageSharp/ColorSpaces/Hsv.cs @@ -2,28 +2,36 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.ColorSpaces { /// /// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness). /// - internal readonly struct Hsv : IColorVector, IEquatable, IAlmostEquatable + public readonly struct Hsv : IEquatable { + private static readonly Vector3 Min = Vector3.Zero; + private static readonly Vector3 Max = new Vector3(360, 1, 1); + /// - /// Max range used for clamping. + /// Gets the hue component. + /// A value ranging between 0 and 360. /// - private static readonly Vector3 VectorMax = new Vector3(360, 1, 1); + public readonly float H; /// - /// The backing vector for SIMD support. + /// Gets the saturation component. + /// A value ranging between 0 and 1. /// - private readonly Vector3 backingVector; + public readonly float S; + + /// + /// Gets the value (brightness) component. + /// A value ranging between 0 and 1. + /// + public readonly float V; /// /// Initializes a new instance of the struct. @@ -31,7 +39,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The h hue component. /// The s saturation component. /// The v value (brightness) component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Hsv(float h, float s, float v) : this(new Vector3(h, s, v)) { @@ -41,168 +49,59 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Initializes a new instance of the struct. /// /// The vector representing the h, s, v components. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Hsv(Vector3 vector) { - this.backingVector = Vector3.Clamp(vector, Vector3.Zero, VectorMax); - } - - /// - /// Gets the hue component. - /// A value ranging between 0 and 360. - /// - public float H - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.X; - } - - /// - /// Gets the saturation component. - /// A value ranging between 0 and 1. - /// - public float S - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Y; - } - - /// - /// Gets the value (brightness) component. - /// A value ranging between 0 and 1. - /// - public float V - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Z; - } - - /// - public Vector3 Vector => this.backingVector; - - /// - /// Allows the implicit conversion of an instance of to a - /// . - /// - /// The instance of to convert. - /// - /// An instance of . - /// - public static implicit operator Hsv(Rgba32 color) - { - float r = color.R / 255F; - float g = color.G / 255F; - float b = color.B / 255F; - - float max = MathF.Max(r, MathF.Max(g, b)); - float min = MathF.Min(r, MathF.Min(g, b)); - float chroma = max - min; - float h = 0; - float s = 0; - float v = max; - - if (MathF.Abs(chroma) < Constants.Epsilon) - { - return new Hsv(0, s, v); - } - - if (MathF.Abs(r - max) < Constants.Epsilon) - { - h = (g - b) / chroma; - } - else if (MathF.Abs(g - max) < Constants.Epsilon) - { - h = 2 + ((b - r) / chroma); - } - else if (MathF.Abs(b - max) < Constants.Epsilon) - { - h = 4 + ((r - g) / chroma); - } - - h *= 60; - if (h < 0.0) - { - h += 360; - } - - s = chroma / v; - - return new Hsv(h, s, v); + vector = Vector3.Clamp(vector, Min, Max); + this.H = vector.X; + this.S = vector.Y; + this.V = vector.Z; } /// /// 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 ==(Hsv left, Hsv right) - { - return left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Hsv left, Hsv 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 !=(Hsv left, Hsv right) - { - return !left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Hsv left, Hsv right) => !left.Equals(right); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public override int GetHashCode() { - return this.backingVector.GetHashCode(); + int hash = this.H.GetHashCode(); + hash = HashHelpers.Combine(hash, this.S.GetHashCode()); + return HashHelpers.Combine(hash, this.V.GetHashCode()); } /// - public override string ToString() - { - return this.Equals(default) - ? "Hsv [ Empty ]" - : $"Hsv [ H={this.H:#0.##}, S={this.S:#0.##}, V={this.V:#0.##} ]"; - } + public override string ToString() => FormattableString.Invariant($"Hsv({this.H:#0.##}, {this.S:#0.##}, {this.V:#0.##})"); /// - public override bool Equals(object obj) - { - return obj is Hsv other && this.Equals(other); - } + public override bool Equals(object obj) => obj is Hsv other && this.Equals(other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(Hsv other) { - return this.backingVector.Equals(other.backingVector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool AlmostEquals(Hsv other, float precision) - { - var result = Vector3.Abs(this.backingVector - other.backingVector); - - return result.X <= precision - && result.Y <= precision - && result.Z <= precision; + return this.H.Equals(other.H) + && this.S.Equals(other.S) + && this.V.Equals(other.V); } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/HunterLab.cs b/src/ImageSharp/ColorSpaces/HunterLab.cs index 44f31bc295..f4fa29d314 100644 --- a/src/ImageSharp/ColorSpaces/HunterLab.cs +++ b/src/ImageSharp/ColorSpaces/HunterLab.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; @@ -12,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Represents an Hunter LAB color. /// /// - internal readonly struct HunterLab : IColorVector, IEquatable, IAlmostEquatable + public readonly struct HunterLab : IEquatable { /// /// D50 standard illuminant. @@ -21,9 +20,27 @@ namespace SixLabors.ImageSharp.ColorSpaces public static readonly CieXyz DefaultWhitePoint = Illuminants.C; /// - /// The backing vector for SIMD support. + /// Gets the lightness dimension. + /// A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white). + /// + public readonly float L; + + /// + /// Gets the a color component. + /// A value usually ranging from -100 to 100. Negative is green, positive magenta. + /// + public readonly float A; + + /// + /// Gets the b color component. + /// A value usually ranging from -100 to 100. Negative is blue, positive is yellow + /// + public readonly float B; + + /// + /// Gets the reference white point of this color /// - private readonly Vector3 backingVector; + public readonly CieXyz WhitePoint; /// /// Initializes a new instance of the struct. @@ -32,7 +49,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The a (green - magenta) component. /// The b (blue - yellow) component. /// Uses as white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public HunterLab(float l, float a, float b) : this(new Vector3(l, a, b), DefaultWhitePoint) { @@ -45,7 +62,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The a (green - magenta) component. /// The b (blue - yellow) component. /// The reference white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public HunterLab(float l, float a, float b, CieXyz whitePoint) : this(new Vector3(l, a, b), whitePoint) { @@ -56,7 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// The vector representing the l, a, b components. /// Uses as white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public HunterLab(Vector3 vector) : this(vector, DefaultWhitePoint) { @@ -67,126 +84,61 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// The vector representing the l a b components. /// The reference white point. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public HunterLab(Vector3 vector, CieXyz whitePoint) - : this() { - this.backingVector = vector; + // Not clamping as documentation about this space only indicates "usual" ranges + this.L = vector.X; + this.A = vector.Y; + this.B = vector.Z; this.WhitePoint = whitePoint; } - /// - /// Gets the reference white point of this color - /// - public CieXyz WhitePoint { get; } - - /// - /// Gets the lightness dimension. - /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). - /// - public float L - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.X; - } - - /// - /// Gets the a color component. - /// A value ranging from -100 to 100. Negative is green, positive magenta. - /// - public float A - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Y; - } - - /// - /// Gets the b color component. - /// A value ranging from -100 to 100. Negative is blue, positive is yellow - /// - public float B - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Z; - } - - /// - public Vector3 Vector => this.backingVector; - /// /// 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. /// - public static bool operator ==(HunterLab left, HunterLab right) - { - return left.Equals(right); - } + public static bool operator ==(HunterLab left, HunterLab 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 !=(HunterLab left, HunterLab right) - { - return !left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(HunterLab left, HunterLab right) => !left.Equals(right); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public override int GetHashCode() { - return HashHelpers.Combine(this.WhitePoint.GetHashCode(), this.backingVector.GetHashCode()); + int hash = this.L.GetHashCode(); + hash = HashHelpers.Combine(hash, this.A.GetHashCode()); + hash = HashHelpers.Combine(hash, this.B.GetHashCode()); + return HashHelpers.Combine(hash, this.WhitePoint.GetHashCode()); } /// - public override string ToString() - { - return this.Equals(default) - ? "HunterLab [Empty]" - : $"HunterLab [ L={this.L:#0.##}, A={this.A:#0.##}, B={this.B:#0.##}]"; - } + public override string ToString() => FormattableString.Invariant($"HunterLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); /// - public override bool Equals(object obj) - { - return obj is HunterLab other && this.Equals(other); - } + public override bool Equals(object obj) => obj is HunterLab other && this.Equals(other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(HunterLab other) { - return this.backingVector.Equals(other.backingVector) + return this.L.Equals(other.L) + && this.A.Equals(other.A) + && this.B.Equals(other.B) && this.WhitePoint.Equals(other.WhitePoint); } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool AlmostEquals(HunterLab other, float precision) - { - var result = Vector3.Abs(this.backingVector - other.backingVector); - - return this.WhitePoint.Equals(other.WhitePoint) - && result.X <= precision - && result.Y <= precision - && result.Z <= precision; - } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/IAlmostEquatable.cs b/src/ImageSharp/ColorSpaces/IAlmostEquatable.cs deleted file mode 100644 index 08c2dafbc6..0000000000 --- a/src/ImageSharp/ColorSpaces/IAlmostEquatable.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; - -namespace SixLabors.ImageSharp.ColorSpaces -{ - /// - /// Defines a generalized method that a value type or class implements to create - /// a type-specific method for determining approximate equality of instances. - /// - /// The type of objects to compare. - /// The object specifying the type to specify precision with. - internal interface IAlmostEquatable - where TPrecision : struct, IComparable - { - /// - /// Indicates whether the current object is equal to another object of the same type - /// when compared to the specified precision level. - /// - /// An object to compare with this object. - /// The object specifying the level of precision. - /// - /// true if the current object is equal to the other parameter; otherwise, false. - /// - bool AlmostEquals(TPixel other, TPrecision precision); - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/IColorVector.cs b/src/ImageSharp/ColorSpaces/IColorVector.cs deleted file mode 100644 index 85c040b868..0000000000 --- a/src/ImageSharp/ColorSpaces/IColorVector.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; - -namespace SixLabors.ImageSharp.ColorSpaces -{ - /// - /// Color represented as a vector in its color space - /// - internal interface IColorVector - { - /// - /// Gets the vector representation of the color - /// - Vector3 Vector { get; } - } -} \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/ICompanding.cs b/src/ImageSharp/ColorSpaces/ICompanding.cs deleted file mode 100644 index 053c8d17b2..0000000000 --- a/src/ImageSharp/ColorSpaces/ICompanding.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; - -namespace SixLabors.ImageSharp.ColorSpaces -{ - /// - /// Pair of companding functions for . - /// Used for conversion to and backwards. - /// See also: - /// - internal interface ICompanding - { - /// - /// Expands a companded channel to its linear equivalent with respect to the energy. - /// - /// - /// For more info see: - /// - /// - /// The channel value - /// The linear channel value - float Expand(float channel); - - /// - /// Compresses an uncompanded channel (linear) to its nonlinear equivalent (depends on the RGB color system). - /// - /// - /// For more info see: - /// - /// - /// The channel value - /// The nonlinear channel value - float Compress(float channel); - } -} diff --git a/src/ImageSharp/ColorSpaces/Illuminants.cs b/src/ImageSharp/ColorSpaces/Illuminants.cs index 85c4063bf4..ed385e02cd 100644 --- a/src/ImageSharp/ColorSpaces/Illuminants.cs +++ b/src/ImageSharp/ColorSpaces/Illuminants.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Descriptions taken from: /// http://en.wikipedia.org/wiki/Standard_illuminant /// - internal static class Illuminants + public static class Illuminants { /// /// Incandescent / Tungsten diff --git a/src/ImageSharp/ColorSpaces/LinearRgb.cs b/src/ImageSharp/ColorSpaces/LinearRgb.cs index aaf05e0358..ec6d18be2b 100644 --- a/src/ImageSharp/ColorSpaces/LinearRgb.cs +++ b/src/ImageSharp/ColorSpaces/LinearRgb.cs @@ -4,24 +4,45 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; namespace SixLabors.ImageSharp.ColorSpaces { /// - /// Represents an linear Rgb color with specified working space + /// Represents an linear Rgb color with specified working space /// - internal readonly struct LinearRgb : IColorVector, IEquatable, IAlmostEquatable + public readonly struct LinearRgb : IEquatable { + private static readonly Vector3 Min = Vector3.Zero; + private static readonly Vector3 Max = Vector3.One; + /// /// The default LinearRgb working space. /// - public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; + public static readonly RgbWorkingSpaceBase DefaultWorkingSpace = RgbWorkingSpaces.SRgb; + + /// + /// Gets the red component. + /// A value usually ranging between 0 and 1. + /// + public readonly float R; + + /// + /// Gets the green component. + /// A value usually ranging between 0 and 1. + /// + public readonly float G; + + /// + /// Gets the blue component. + /// A value usually ranging between 0 and 1. + /// + public readonly float B; /// - /// The backing vector for SIMD support. + /// Gets the LinearRgb color space /// - private readonly Vector3 backingVector; + public readonly RgbWorkingSpaceBase WorkingSpace; /// /// Initializes a new instance of the struct. @@ -29,9 +50,9 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The red component ranging between 0 and 1. /// The green component ranging between 0 and 1. /// The blue component ranging between 0 and 1. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public LinearRgb(float r, float g, float b) - : this(new Vector3(r, g, b)) + : this(r, g, b, DefaultWorkingSpace) { } @@ -42,8 +63,8 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The green component ranging between 0 and 1. /// The blue component ranging between 0 and 1. /// The rgb working space. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public LinearRgb(float r, float g, float b, RgbWorkingSpace workingSpace) + [MethodImpl(InliningOptions.ShortMethod)] + public LinearRgb(float r, float g, float b, RgbWorkingSpaceBase workingSpace) : this(new Vector3(r, g, b), workingSpace) { } @@ -52,7 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Initializes a new instance of the struct. /// /// The vector representing the r, g, b components. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public LinearRgb(Vector3 vector) : this(vector, DefaultWorkingSpace) { @@ -63,126 +84,68 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// The vector representing the r, g, b components. /// The LinearRgb working space. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public LinearRgb(Vector3 vector, RgbWorkingSpace workingSpace) - : this() + [MethodImpl(InliningOptions.ShortMethod)] + public LinearRgb(Vector3 vector, RgbWorkingSpaceBase workingSpace) { // Clamp to 0-1 range. - this.backingVector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); + vector = Vector3.Clamp(vector, Min, Max); + this.R = vector.X; + this.G = vector.Y; + this.B = vector.Z; this.WorkingSpace = workingSpace; } - /// - /// Gets the red component. - /// A value usually ranging between 0 and 1. - /// - public float R - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.X; - } - - /// - /// Gets the green component. - /// A value usually ranging between 0 and 1. - /// - public float G - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Y; - } - - /// - /// Gets the blue component. - /// A value usually ranging between 0 and 1. - /// - public float B - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Z; - } - - /// - /// Gets the LinearRgb color space - /// - public RgbWorkingSpace WorkingSpace { get; } - - /// - public Vector3 Vector => this.backingVector; - /// /// 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 ==(LinearRgb left, LinearRgb right) - { - return left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(LinearRgb left, LinearRgb 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 !=(LinearRgb left, LinearRgb right) - { - return !left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(LinearRgb left, LinearRgb right) => !left.Equals(right); + + /// + /// Returns a new representing this instance. + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Vector3 ToVector3() => new Vector3(this.R, this.G, this.B); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public override int GetHashCode() { - return this.backingVector.GetHashCode(); + int hash = this.R.GetHashCode(); + hash = HashHelpers.Combine(hash, this.G.GetHashCode()); + return HashHelpers.Combine(hash, this.B.GetHashCode()); } /// - public override string ToString() - { - return this.Equals(default) - ? "LinearRgb [ Empty ]" - : $"LinearRgb [ R={this.R:#0.##}, G={this.G:#0.##}, B={this.B:#0.##} ]"; - } + public override string ToString() => FormattableString.Invariant($"LinearRgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})"); /// - public override bool Equals(object obj) - { - return obj is LinearRgb other && this.Equals(other); - } + public override bool Equals(object obj) => obj is LinearRgb other && this.Equals(other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(LinearRgb other) { - return this.backingVector.Equals(other.backingVector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool AlmostEquals(LinearRgb other, float precision) - { - var result = Vector3.Abs(this.backingVector - other.backingVector); - - return result.X <= precision - && result.Y <= precision - && result.Z <= precision; + return this.R.Equals(other.R) + && this.G.Equals(other.G) + && this.B.Equals(other.B); } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Lms.cs b/src/ImageSharp/ColorSpaces/Lms.cs index 9b0331e0bc..0a8b7aa7b9 100644 --- a/src/ImageSharp/ColorSpaces/Lms.cs +++ b/src/ImageSharp/ColorSpaces/Lms.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; @@ -13,12 +12,25 @@ namespace SixLabors.ImageSharp.ColorSpaces /// named after their responsivity (sensitivity) at long, medium and short wavelengths. /// /// - internal readonly struct Lms : IColorVector, IEquatable, IAlmostEquatable + public readonly struct Lms : IEquatable { /// - /// The backing vector for SIMD support. + /// Gets the L long component. + /// A value usually ranging between -1 and 1. + /// + public readonly float L; + + /// + /// Gets the M medium component. + /// A value usually ranging between -1 and 1. /// - private readonly Vector3 backingVector; + public readonly float M; + + /// + /// Gets the S short component. + /// A value usually ranging between -1 and 1. + /// + public readonly float S; /// /// Initializes a new instance of the struct. @@ -26,7 +38,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// L represents the responsivity at long wavelengths. /// M represents the responsivity at medium wavelengths. /// S represents the responsivity at short wavelengths. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Lms(float l, float m, float s) : this(new Vector3(l, m, s)) { @@ -36,119 +48,65 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Initializes a new instance of the struct. /// /// The vector representing the l, m, s components. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Lms(Vector3 vector) - : this() - { - // Not clamping as documentation about this space seems to indicate "usual" ranges - this.backingVector = vector; - } - - /// - /// Gets the L long component. - /// A value usually ranging between -1 and 1. - /// - public float L - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.X; - } - - /// - /// Gets the M medium component. - /// A value usually ranging between -1 and 1. - /// - public float M { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Y; + // Not clamping as documentation about this space only indicates "usual" ranges + this.L = vector.X; + this.M = vector.Y; + this.S = vector.Z; } - /// - /// Gets the S short component. - /// A value usually ranging between -1 and 1. - /// - public float S - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Z; - } - - /// - public Vector3 Vector => this.backingVector; - /// /// 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 ==(Lms left, Lms right) - { - return left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Lms left, Lms 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 !=(Lms left, Lms right) - { - return !left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Lms left, Lms right) => !left.Equals(right); + + /// + /// Returns a new representing this instance. + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Vector3 ToVector3() => new Vector3(this.L, this.M, this.S); /// public override int GetHashCode() { - return this.backingVector.GetHashCode(); + int hash = this.L.GetHashCode(); + hash = HashHelpers.Combine(hash, this.M.GetHashCode()); + return HashHelpers.Combine(hash, this.S.GetHashCode()); } /// - public override string ToString() - { - return this.Equals(default) - ? "Lms [ Empty ]" - : $"Lms [ L={this.L:#0.##}, M={this.M:#0.##}, S={this.S:#0.##} ]"; - } + public override string ToString() => FormattableString.Invariant($"Lms({this.L:#0.##}, {this.M:#0.##}, {this.S:#0.##})"); /// - public override bool Equals(object obj) - { - return obj is Lms other && this.Equals(other); - } + public override bool Equals(object obj) => obj is Lms other && this.Equals(other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(Lms other) { - return this.backingVector.Equals(other.backingVector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool AlmostEquals(Lms other, float precision) - { - var result = Vector3.Abs(this.backingVector - other.backingVector); - - return result.X <= precision - && result.Y <= precision - && result.Z <= precision; + return this.L.Equals(other.L) + && this.M.Equals(other.M) + && this.S.Equals(other.S); } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/Rgb.cs b/src/ImageSharp/ColorSpaces/Rgb.cs index ccfa1760fd..97fafbaf37 100644 --- a/src/ImageSharp/ColorSpaces/Rgb.cs +++ b/src/ImageSharp/ColorSpaces/Rgb.cs @@ -4,25 +4,46 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.ColorSpaces { /// - /// Represents an RGB color with specified working space + /// Represents an RGB color with specified working space. /// - internal readonly struct Rgb : IColorVector, IEquatable, IAlmostEquatable + public readonly struct Rgb : IEquatable { + private static readonly Vector3 Min = Vector3.Zero; + private static readonly Vector3 Max = Vector3.One; + /// /// The default rgb working space /// - public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; + public static readonly RgbWorkingSpaceBase DefaultWorkingSpace = RgbWorkingSpaces.SRgb; /// - /// The backing vector for SIMD support. + /// Gets the red component. + /// A value usually ranging between 0 and 1. + /// + public readonly float R; + + /// + /// Gets the green component. + /// A value usually ranging between 0 and 1. + /// + public readonly float G; + + /// + /// Gets the blue component. + /// A value usually ranging between 0 and 1. + /// + public readonly float B; + + /// + /// Gets the Rgb color space /// - private readonly Vector3 backingVector; + public readonly RgbWorkingSpaceBase WorkingSpace; /// /// Initializes a new instance of the struct. @@ -30,9 +51,9 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The red component ranging between 0 and 1. /// The green component ranging between 0 and 1. /// The blue component ranging between 0 and 1. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Rgb(float r, float g, float b) - : this(new Vector3(r, g, b)) + : this(r, g, b, DefaultWorkingSpace) { } @@ -43,8 +64,8 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The green component ranging between 0 and 1. /// The blue component ranging between 0 and 1. /// The rgb working space. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgb(float r, float g, float b, RgbWorkingSpace workingSpace) + [MethodImpl(InliningOptions.ShortMethod)] + public Rgb(float r, float g, float b, RgbWorkingSpaceBase workingSpace) : this(new Vector3(r, g, b), workingSpace) { } @@ -53,7 +74,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Initializes a new instance of the struct. /// /// The vector representing the r, g, b components. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Rgb(Vector3 vector) : this(vector, DefaultWorkingSpace) { @@ -64,68 +85,33 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// The vector representing the r, g, b components. /// The rgb working space. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgb(Vector3 vector, RgbWorkingSpace workingSpace) - : this() + [MethodImpl(InliningOptions.ShortMethod)] + public Rgb(Vector3 vector, RgbWorkingSpaceBase workingSpace) { - // Clamp to 0-1 range. - this.backingVector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); + vector = Vector3.Clamp(vector, Min, Max); + this.R = vector.X; + this.G = vector.Y; + this.B = vector.Z; this.WorkingSpace = workingSpace; } /// - /// Gets the red component. - /// A value usually ranging between 0 and 1. - /// - public float R - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.X; - } - - /// - /// Gets the green component. - /// A value usually ranging between 0 and 1. - /// - public float G - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Y; - } - - /// - /// Gets the blue component. - /// A value usually ranging between 0 and 1. - /// - public float B - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Z; - } - - /// - /// Gets the Rgb color space + /// Allows the implicit conversion of an instance of to a + /// . /// - public RgbWorkingSpace WorkingSpace { get; } - - /// - public Vector3 Vector => this.backingVector; + /// The instance of to convert. + /// An instance of . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Rgb(Rgb24 color) => new Rgb(color.R / 255F, color.G / 255F, color.B / 255F); /// /// Allows the implicit conversion of an instance of to a /// . /// - /// - /// The instance of to convert. - /// - /// - /// An instance of . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Rgb(Rgba32 color) - { - return new Rgb(color.R / 255F, color.G / 255F, color.B / 255F); - } + /// The instance of to convert. + /// An instance of . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Rgb(Rgba32 color) => new Rgb(color.R / 255F, color.G / 255F, color.B / 255F); /// /// Compares two objects for equality. @@ -139,66 +125,48 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// True if the current left is equal to the parameter; otherwise, false. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rgb left, Rgb right) - { - return left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Rgb left, Rgb 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 !=(Rgb left, Rgb right) - { - return !left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Rgb left, Rgb right) => !left.Equals(right); + + /// + /// Returns a new representing this instance. + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Vector3 ToVector3() => new Vector3(this.R, this.G, this.B); /// public override int GetHashCode() { - return this.backingVector.GetHashCode(); + int hash = this.R.GetHashCode(); + hash = HashHelpers.Combine(hash, this.G.GetHashCode()); + return HashHelpers.Combine(hash, this.B.GetHashCode()); } /// - public override string ToString() - { - return this.Equals(default) - ? "Rgb [ Empty ]" - : $"Rgb [ R={this.R:#0.##}, G={this.G:#0.##}, B={this.B:#0.##} ]"; - } + public override string ToString() => FormattableString.Invariant($"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})"); /// - public override bool Equals(object obj) - { - return obj is Rgb other && this.Equals(other); - } + public override bool Equals(object obj) => obj is Rgb other && this.Equals(other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(Rgb other) { - return this.backingVector.Equals(other.backingVector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool AlmostEquals(Rgb other, float precision) - { - var result = Vector3.Abs(this.backingVector - other.backingVector); - - return result.X <= precision - && result.Y <= precision - && result.Z <= precision; + return this.R.Equals(other.R) + && this.G.Equals(other.G) + && this.B.Equals(other.B); } } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs index 978a35725f..ee3822c152 100644 --- a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs +++ b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs @@ -1,7 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; +using SixLabors.ImageSharp.ColorSpaces.Companding; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.ColorSpaces @@ -10,7 +11,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Chromaticity coordinates taken from: /// /// - internal static class RgbWorkingSpaces + public static class RgbWorkingSpaces { /// /// sRgb working space. @@ -19,97 +20,97 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Uses proper companding function, according to: /// /// - public static readonly RgbWorkingSpace SRgb = new RgbWorkingSpace(Illuminants.D65, new SRgbCompanding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpaceBase SRgb = new SRgbWorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// Simplified sRgb working space (uses gamma companding instead of ). /// See also . /// - public static readonly RgbWorkingSpace SRgbSimplified = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpaceBase SRgbSimplified = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// Rec. 709 (ITU-R Recommendation BT.709) working space. /// - public static readonly RgbWorkingSpace Rec709 = new RgbWorkingSpace(Illuminants.D65, new Rec709Companding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); + public static readonly RgbWorkingSpaceBase Rec709 = new Rec709WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); /// /// Rec. 2020 (ITU-R Recommendation BT.2020F) working space. /// - public static readonly RgbWorkingSpace Rec2020 = new RgbWorkingSpace(Illuminants.D65, new Rec2020Companding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); + public static readonly RgbWorkingSpaceBase Rec2020 = new Rec2020WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); /// /// ECI Rgb v2 working space. /// - public static readonly RgbWorkingSpace ECIRgbv2 = new RgbWorkingSpace(Illuminants.D50, new LCompanding(), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); + public static readonly RgbWorkingSpaceBase ECIRgbv2 = new LWorkingSpace(Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); /// /// Adobe Rgb (1998) working space. /// - public static readonly RgbWorkingSpace AdobeRgb1998 = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpaceBase AdobeRgb1998 = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// Apple sRgb working space. /// - public static readonly RgbWorkingSpace ApplesRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); + public static readonly RgbWorkingSpaceBase ApplesRgb = new GammaWorkingSpace(1.8F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); /// /// Best Rgb working space. /// - public static readonly RgbWorkingSpace BestRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); + public static readonly RgbWorkingSpaceBase BestRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); /// /// Beta Rgb working space. /// - public static readonly RgbWorkingSpace BetaRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); + public static readonly RgbWorkingSpaceBase BetaRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); /// /// Bruce Rgb working space. /// - public static readonly RgbWorkingSpace BruceRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpaceBase BruceRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// CIE Rgb working space. /// - public static readonly RgbWorkingSpace CIERgb = new RgbWorkingSpace(Illuminants.E, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); + public static readonly RgbWorkingSpaceBase CIERgb = new GammaWorkingSpace(2.2F, Illuminants.E, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); /// /// ColorMatch Rgb working space. /// - public static readonly RgbWorkingSpace ColorMatchRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); + public static readonly RgbWorkingSpaceBase ColorMatchRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); /// /// Don Rgb 4 working space. /// - public static readonly RgbWorkingSpace DonRgb4 = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); + public static readonly RgbWorkingSpaceBase DonRgb4 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); /// /// Ekta Space PS5 working space. /// - public static readonly RgbWorkingSpace EktaSpacePS5 = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); + public static readonly RgbWorkingSpaceBase EktaSpacePS5 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); /// /// NTSC Rgb working space. /// - public static readonly RgbWorkingSpace NTSCRgb = new RgbWorkingSpace(Illuminants.C, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); + public static readonly RgbWorkingSpaceBase NTSCRgb = new GammaWorkingSpace(2.2F, Illuminants.C, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); /// /// PAL/SECAM Rgb working space. /// - public static readonly RgbWorkingSpace PALSECAMRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpaceBase PALSECAMRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// ProPhoto Rgb working space. /// - public static readonly RgbWorkingSpace ProPhotoRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(1.8F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); + public static readonly RgbWorkingSpaceBase ProPhotoRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); /// /// SMPTE-C Rgb working space. /// - public static readonly RgbWorkingSpace SMPTECRgb = new RgbWorkingSpace(Illuminants.D65, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); + public static readonly RgbWorkingSpaceBase SMPTECRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); /// /// Wide Gamut Rgb working space. /// - public static readonly RgbWorkingSpace WideGamutRgb = new RgbWorkingSpace(Illuminants.D50, new GammaCompanding(2.2F), new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); + public static readonly RgbWorkingSpaceBase WideGamutRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); } } \ No newline at end of file diff --git a/src/ImageSharp/ColorSpaces/YCbCr.cs b/src/ImageSharp/ColorSpaces/YCbCr.cs index 00533c6991..6aa191c2de 100644 --- a/src/ImageSharp/ColorSpaces/YCbCr.cs +++ b/src/ImageSharp/ColorSpaces/YCbCr.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; @@ -13,17 +12,28 @@ namespace SixLabors.ImageSharp.ColorSpaces /// /// /// - internal readonly struct YCbCr : IColorVector, IEquatable, IAlmostEquatable + public readonly struct YCbCr : IEquatable { + private static readonly Vector3 Min = Vector3.Zero; + private static readonly Vector3 Max = new Vector3(255); + + /// + /// Gets the Y luminance component. + /// A value ranging between 0 and 255. + /// + public readonly float Y; + /// - /// Vector which is used in clamping to the max value. + /// Gets the Cb chroma component. + /// A value ranging between 0 and 255. /// - private static readonly Vector3 VectorMax = new Vector3(255F); + public readonly float Cb; /// - /// The backing vector for SIMD support. + /// Gets the Cr chroma component. + /// A value ranging between 0 and 255. /// - private readonly Vector3 backingVector; + public readonly float Cr; /// /// Initializes a new instance of the struct. @@ -31,7 +41,7 @@ namespace SixLabors.ImageSharp.ColorSpaces /// The y luminance component. /// The cb chroma component. /// The cr chroma component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public YCbCr(float y, float cb, float cr) : this(new Vector3(y, cb, cr)) { @@ -41,117 +51,58 @@ namespace SixLabors.ImageSharp.ColorSpaces /// Initializes a new instance of the struct. /// /// The vector representing the y, cb, cr components. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public YCbCr(Vector3 vector) { - this.backingVector = Vector3.Clamp(vector, Vector3.Zero, VectorMax); - } - - /// - /// Gets the Y luminance component. - /// A value ranging between 0 and 255. - /// - public float Y - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.X; - } - - /// - /// Gets the Cb chroma component. - /// A value ranging between 0 and 255. - /// - public float Cb - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Y; - } - - /// - /// Gets the Cr chroma component. - /// A value ranging between 0 and 255. - /// - public float Cr - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.backingVector.Z; + vector = Vector3.Clamp(vector, Min, Max); + this.Y = vector.X; + this.Cb = vector.Y; + this.Cr = vector.Z; } - /// - public Vector3 Vector => this.backingVector; - /// /// 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. /// - public static bool operator ==(YCbCr left, YCbCr right) - { - return left.Equals(right); - } + public static bool operator ==(YCbCr left, YCbCr 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 !=(YCbCr left, YCbCr right) - { - return !left.Equals(right); - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(YCbCr left, YCbCr right) => !left.Equals(right); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public override int GetHashCode() { - return this.backingVector.GetHashCode(); + int hash = this.Y.GetHashCode(); + hash = HashHelpers.Combine(hash, this.Cb.GetHashCode()); + return HashHelpers.Combine(hash, this.Cr.GetHashCode()); } /// - public override string ToString() - { - return this.Equals(default) - ? "YCbCr [ Empty ]" - : $"YCbCr [ Y={this.Y}, Cb={this.Cb}, Cr={this.Cr} ]"; - } + public override string ToString() => FormattableString.Invariant($"YCbCr({this.Y}, {this.Cb}, {this.Cr})"); /// - public override bool Equals(object obj) - { - return obj is YCbCr other && this.Equals(other); - } + public override bool Equals(object obj) => obj is YCbCr other && this.Equals(other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(YCbCr other) { - return this.backingVector.Equals(other.backingVector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool AlmostEquals(YCbCr other, float precision) - { - var result = Vector3.Abs(this.backingVector - other.backingVector); - - return result.X <= precision - && result.Y <= precision - && result.Z <= precision; + return this.Y.Equals(other.Y) + && this.Cb.Equals(other.Cb) + && this.Cr.Equals(other.Cr); } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Extensions/ComparableExtensions.cs b/src/ImageSharp/Common/Extensions/ComparableExtensions.cs index 1b0f8ad095..3c8570a2a4 100644 --- a/src/ImageSharp/Common/Extensions/ComparableExtensions.cs +++ b/src/ImageSharp/Common/Extensions/ComparableExtensions.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp /// /// The representing the clamped value. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static byte Clamp(this byte value, byte min, byte max) { // Order is important here as someone might set min to higher than max. @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp /// /// The representing the clamped value. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static uint Clamp(this uint value, uint min, uint max) { if (value >= max) @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp /// /// The representing the clamped value. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static int Clamp(this int value, int min, int max) { if (value >= max) @@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp /// /// The representing the clamped value. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static float Clamp(this float value, float min, float max) { if (value >= max) @@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp /// /// The representing the clamped value. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static double Clamp(this double value, double min, double max) { if (value >= max) @@ -136,27 +136,5 @@ namespace SixLabors.ImageSharp return value; } - - /// - /// Converts an to a first restricting the value between the - /// minimum and maximum allowable ranges. - /// - /// The this method extends. - /// The - public static byte ToByte(this float value) - { - return (byte)value.Clamp(0, 255); - } - - /// - /// Converts an to a first restricting the value between the - /// minimum and maximum allowable ranges. - /// - /// The this method extends. - /// The - public static byte ToByte(this double value) - { - return (byte)value.Clamp(0, 255); - } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Extensions/EncoderExtensions.cs b/src/ImageSharp/Common/Extensions/EncoderExtensions.cs new file mode 100644 index 0000000000..59c878485d --- /dev/null +++ b/src/ImageSharp/Common/Extensions/EncoderExtensions.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +#if !NETCOREAPP2_1 +using System; +using System.Text; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for the type. + /// + internal static unsafe class EncoderExtensions + { + /// + /// Gets a string from the provided buffer data. + /// + /// The encoding. + /// The buffer. + /// The string. + public static string GetString(this Encoding encoding, ReadOnlySpan buffer) + { + if (buffer.Length == 0) + { + return string.Empty; + } + + fixed (byte* bytes = buffer) + { + return encoding.GetString(bytes, buffer.Length); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/ImageSharp/Common/Extensions/SimdUtils.cs b/src/ImageSharp/Common/Extensions/SimdUtils.cs deleted file mode 100644 index 7b77fefcac..0000000000 --- a/src/ImageSharp/Common/Extensions/SimdUtils.cs +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Diagnostics; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp -{ - /// - /// Various extension and utility methods for and utilizing SIMD capabilities - /// - internal static class SimdUtils - { - /// - /// Gets a value indicating whether the code is being executed on AVX2 CPU where both float and integer registers are of size 256 byte. - /// - public static bool IsAvx2CompatibleArchitecture => Vector.Count == 8 && Vector.Count == 8; - - internal static void GuardAvx2(string operation) - { - if (!IsAvx2CompatibleArchitecture) - { - throw new NotSupportedException($"{operation} is supported only on AVX2 CPU!"); - } - } - - /// - /// Transform all scalars in 'v' in a way that converting them to would have rounding semantics. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vector4 PseudoRound(this Vector4 v) - { - var sign = Vector4.Clamp(v, new Vector4(-1), new Vector4(1)); - - return v + (sign * 0.5f); - } - - /// - /// Rounds all values in 'v' to the nearest integer following semantics. - /// Source: - /// - /// https://github.com/g-truc/glm/blob/master/glm/simd/common.h#L110 - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Vector FastRound(this Vector x) - { - Vector magic0 = new Vector(int.MinValue); // 0x80000000 - Vector sgn0 = Vector.AsVectorSingle(magic0); - Vector and0 = Vector.BitwiseAnd(sgn0, x); - Vector or0 = Vector.BitwiseOr(and0, new Vector(8388608.0f)); - Vector add0 = Vector.Add(x, or0); - Vector sub0 = Vector.Subtract(add0, or0); - return sub0; - } - - /// - /// Convert 'source.Length' values normalized into [0..1] from 'source' into 'dest' buffer of values. - /// The values gonna be scaled up into [0-255] and rounded. - /// Based on: - /// - /// http://lolengine.net/blog/2011/3/20/understanding-fast-float-integer-conversions - /// - /// - internal static void BulkConvertNormalizedFloatToByte(ReadOnlySpan source, Span dest) - { - GuardAvx2(nameof(BulkConvertNormalizedFloatToByte)); - - DebugGuard.IsTrue((source.Length % Vector.Count) == 0, nameof(source), "source.Length should be divisable by Vector.Count!"); - - if (source.Length == 0) - { - return; - } - - ref Vector srcBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref Octet.OfByte destBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); - int n = source.Length / 8; - - Vector magick = new Vector(32768.0f); - Vector scale = new Vector(255f) / new Vector(256f); - - // need to copy to a temporary struct, because - // SimdUtils.Octet.OfUInt32 temp = Unsafe.As, SimdUtils.Octet.OfUInt32>(ref x) - // does not work. TODO: This might be a CoreClr bug, need to ask/report - var temp = default(Octet.OfUInt32); - ref Vector tempRef = ref Unsafe.As>(ref temp); - - for (int i = 0; i < n; i++) - { - // union { float f; uint32_t i; } u; - // u.f = 32768.0f + x * (255.0f / 256.0f); - // return (uint8_t)u.i; - Vector x = Unsafe.Add(ref srcBase, i); - x = (x * scale) + magick; - tempRef = x; - - ref Octet.OfByte d = ref Unsafe.Add(ref destBase, i); - d.LoadFrom(ref temp); - } - } - - /// - /// Same as but clamps overflown values before conversion. - /// - internal static void BulkConvertNormalizedFloatToByteClampOverflows(ReadOnlySpan source, Span dest) - { - GuardAvx2(nameof(BulkConvertNormalizedFloatToByte)); - - DebugGuard.IsTrue((source.Length % Vector.Count) == 0, nameof(source), "source.Length should be divisable by Vector.Count!"); - - if (source.Length == 0) - { - return; - } - - ref Vector srcBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); - ref Octet.OfByte destBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); - int n = source.Length / 8; - - Vector magick = new Vector(32768.0f); - Vector scale = new Vector(255f) / new Vector(256f); - - // need to copy to a temporary struct, because - // SimdUtils.Octet.OfUInt32 temp = Unsafe.As, SimdUtils.Octet.OfUInt32>(ref x) - // does not work. TODO: This might be a CoreClr bug, need to ask/report - var temp = default(Octet.OfUInt32); - ref Vector tempRef = ref Unsafe.As>(ref temp); - - for (int i = 0; i < n; i++) - { - // union { float f; uint32_t i; } u; - // u.f = 32768.0f + x * (255.0f / 256.0f); - // return (uint8_t)u.i; - Vector x = Unsafe.Add(ref srcBase, i); - x = Vector.Max(x, Vector.Zero); - x = Vector.Min(x, Vector.One); - - x = (x * scale) + magick; - tempRef = x; - - ref Octet.OfByte d = ref Unsafe.Add(ref destBase, i); - d.LoadFrom(ref temp); - } - } - - // TODO: Replace these with T4-d library level tuples! - internal static class Octet - { - [StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(uint))] - public struct OfUInt32 - { - [FieldOffset(0 * sizeof(uint))] - public uint V0; - - [FieldOffset(1 * sizeof(uint))] - public uint V1; - - [FieldOffset(2 * sizeof(uint))] - public uint V2; - - [FieldOffset(3 * sizeof(uint))] - public uint V3; - - [FieldOffset(4 * sizeof(uint))] - public uint V4; - - [FieldOffset(5 * sizeof(uint))] - public uint V5; - - [FieldOffset(6 * sizeof(uint))] - public uint V6; - - [FieldOffset(7 * sizeof(uint))] - public uint V7; - - public override string ToString() - { - return $"[{this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7}]"; - } - } - - [StructLayout(LayoutKind.Explicit, Size = 8)] - public struct OfByte - { - [FieldOffset(0)] - public byte V0; - - [FieldOffset(1)] - public byte V1; - - [FieldOffset(2)] - public byte V2; - - [FieldOffset(3)] - public byte V3; - - [FieldOffset(4)] - public byte V4; - - [FieldOffset(5)] - public byte V5; - - [FieldOffset(6)] - public byte V6; - - [FieldOffset(7)] - public byte V7; - - public override string ToString() - { - return $"[{this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7}]"; - } - - public void LoadFrom(ref OfUInt32 i) - { - this.V0 = (byte)i.V0; - this.V1 = (byte)i.V1; - this.V2 = (byte)i.V2; - this.V3 = (byte)i.V3; - this.V4 = (byte)i.V4; - this.V5 = (byte)i.V5; - this.V6 = (byte)i.V6; - this.V7 = (byte)i.V7; - } - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs b/src/ImageSharp/Common/Extensions/Vector4Extensions.cs deleted file mode 100644 index b88c229c5d..0000000000 --- a/src/ImageSharp/Common/Extensions/Vector4Extensions.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp -{ - /// - /// Extension methods for the struct. - /// - internal static class Vector4Extensions - { - /// - /// Pre-multiplies the "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact. - /// - /// The to premultiply - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Premultiply(this Vector4 source) - { - float w = source.W; - Vector4 premultiplied = source * w; - premultiplied.W = w; - return premultiplied; - } - - /// - /// Reverses the result of premultiplying a vector via . - /// - /// The to premultiply - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 UnPremultiply(this Vector4 source) - { - float w = source.W; - Vector4 unpremultiplied = source / w; - unpremultiplied.W = w; - return unpremultiplied; - } - - /// - /// Compresses a linear color signal to its sRGB equivalent. - /// - /// - /// - /// The whose signal to compress. - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Compress(this Vector4 linear) - { - // TODO: Is there a faster way to do this? - return new Vector4(Compress(linear.X), Compress(linear.Y), Compress(linear.Z), linear.W); - } - - /// - /// Expands an sRGB color signal to its linear equivalent. - /// - /// - /// - /// The whose signal to expand. - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector4 Expand(this Vector4 gamma) - { - // TODO: Is there a faster way to do this? - return new Vector4(Expand(gamma.X), Expand(gamma.Y), Expand(gamma.Z), gamma.W); - } - - /// - /// Gets the compressed sRGB value from an linear signal. - /// - /// - /// - /// The signal value to compress. - /// - /// The . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static float Compress(float signal) - { - if (signal <= 0.0031308F) - { - return signal * 12.92F; - } - - return (1.055F * MathF.Pow(signal, 0.41666666F)) - 0.055F; - } - - /// - /// Gets the expanded linear value from an sRGB signal. - /// - /// - /// - /// The signal value to expand. - /// - /// The . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static float Expand(float signal) - { - if (signal <= 0.04045F) - { - return signal / 12.92F; - } - - return MathF.Pow((signal + 0.055F) / 1.055F, 2.4F); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Common/Helpers/DebugGuard.cs b/src/ImageSharp/Common/Helpers/DebugGuard.cs index 2cf18b2456..43eebeac87 100644 --- a/src/ImageSharp/Common/Helpers/DebugGuard.cs +++ b/src/ImageSharp/Common/Helpers/DebugGuard.cs @@ -163,6 +163,20 @@ namespace SixLabors.ImageSharp } } + /// + /// Verifies whether a specific condition is met, throwing an exception if it's false. + /// + /// The condition + /// The error message + [Conditional("DEBUG")] + public static void IsTrue(bool target, string message) + { + if (!target) + { + throw new InvalidOperationException(message); + } + } + /// /// Verifies, that the method parameter with specified target value is false /// and throws an exception if it is found to be so. diff --git a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs new file mode 100644 index 0000000000..2e700c9d67 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs @@ -0,0 +1,124 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp +{ + /// + /// Extension methods for . + /// + internal static class DenseMatrixUtils + { + /// + /// Computes the sum of vectors in weighted by the kernel weight values. + /// + /// The pixel format. + /// The dense matrix. + /// The source frame. + /// The target row. + /// The current row. + /// The current column. + /// The maximum working area row. + /// The maximum working area column. + /// The column offset to apply to source sampling. + public static void Convolve( + in DenseMatrix matrix, + Buffer2D sourcePixels, + Span targetRow, + int row, + int column, + int maxRow, + int maxColumn, + int offsetColumn) + where TPixel : struct, IPixel + { + Vector4 vector = default; + int matrixHeight = matrix.Rows; + int matrixWidth = matrix.Columns; + int radiusY = matrixHeight >> 1; + int radiusX = matrixWidth >> 1; + int sourceOffsetColumnBase = column + offsetColumn; + + for (int y = 0; y < matrixHeight; y++) + { + int offsetY = (row + y - radiusY).Clamp(0, maxRow); + Span sourceRowSpan = sourcePixels.GetRowSpan(offsetY); + + for (int x = 0; x < matrixWidth; x++) + { + int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(offsetColumn, maxColumn); + var currentColor = sourceRowSpan[offsetX].ToVector4(); + Vector4Utils.Premultiply(ref currentColor); + + vector += matrix[y, x] * currentColor; + } + } + + ref Vector4 target = ref targetRow[column]; + vector.W = target.W; + Vector4Utils.UnPremultiply(ref vector); + target = vector; + } + + /// + /// Computes the sum of vectors in weighted by the two kernel weight values. + /// + /// The pixel format. + /// The vertical dense matrix. + /// The horizontal dense matrix. + /// The source frame. + /// The target row. + /// The current row. + /// The current column. + /// The maximum working area row. + /// The maximum working area column. + /// The column offset to apply to source sampling. + public static void Convolve2D( + in DenseMatrix matrixY, + in DenseMatrix matrixX, + Buffer2D sourcePixels, + Span targetRow, + int row, + int column, + int maxRow, + int maxColumn, + int offsetColumn) + where TPixel : struct, IPixel + { + Vector4 vectorY = default; + Vector4 vectorX = default; + int matrixHeight = matrixY.Rows; + int matrixWidth = matrixY.Columns; + int radiusY = matrixHeight >> 1; + int radiusX = matrixWidth >> 1; + int sourceOffsetColumnBase = column + offsetColumn; + + for (int y = 0; y < matrixHeight; y++) + { + int offsetY = (row + y - radiusY).Clamp(0, maxRow); + Span sourceRowSpan = sourcePixels.GetRowSpan(offsetY); + + for (int x = 0; x < matrixWidth; x++) + { + int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(offsetColumn, maxColumn); + var currentColor = sourceRowSpan[offsetX].ToVector4(); + Vector4Utils.Premultiply(ref currentColor); + + vectorX += matrixX[y, x] * currentColor; + vectorY += matrixY[y, x] * currentColor; + } + } + + var vector = Vector4.SquareRoot((vectorX * vectorX) + (vectorY * vectorY)); + ref Vector4 target = ref targetRow[column]; + vector.W = target.W; + Vector4Utils.UnPremultiply(ref vector); + target = vector; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs index b4a29f9fb5..d8cf69a52e 100644 --- a/src/ImageSharp/Common/Helpers/Guard.cs +++ b/src/ImageSharp/Common/Helpers/Guard.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp { @@ -19,12 +20,13 @@ namespace SixLabors.ImageSharp /// The target object, which cannot be null. /// The name of the parameter that is to be checked. /// is null + [MethodImpl(InliningOptions.ShortMethod)] public static void NotNull(T value, string parameterName) where T : class { if (value is null) { - throw new ArgumentNullException(parameterName); + ThrowArgumentNullException(parameterName); } } @@ -35,16 +37,17 @@ namespace SixLabors.ImageSharp /// Name of the parameter. /// is null. /// is empty or contains only blanks. + [MethodImpl(InliningOptions.ShortMethod)] public static void NotNullOrWhiteSpace(string value, string parameterName) { if (value is null) { - throw new ArgumentNullException(parameterName); + ThrowArgumentNullException(parameterName); } if (string.IsNullOrWhiteSpace(value)) { - throw new ArgumentException("Must not be empty or whitespace.", parameterName); + ThrowArgumentException("Must not be empty or whitespace.", parameterName); } } @@ -56,16 +59,17 @@ namespace SixLabors.ImageSharp /// Name of the parameter. /// is null. /// is empty. + [MethodImpl(InliningOptions.ShortMethod)] public static void NotNullOrEmpty(ICollection value, string parameterName) { if (value is null) { - throw new ArgumentNullException(parameterName); + ThrowArgumentNullException(parameterName); } if (value.Count == 0) { - throw new ArgumentException("Must not be empty.", parameterName); + ThrowArgumentException("Must not be empty.", parameterName); } } @@ -79,12 +83,13 @@ namespace SixLabors.ImageSharp /// /// is greater than the maximum value. /// + [MethodImpl(InliningOptions.ShortMethod)] public static void MustBeLessThan(TValue value, TValue max, string parameterName) where TValue : IComparable { if (value.CompareTo(max) >= 0) { - throw new ArgumentOutOfRangeException(parameterName, $"Value {value} must be less than {max}."); + ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be less than {max}."); } } @@ -99,12 +104,13 @@ namespace SixLabors.ImageSharp /// /// is greater than the maximum value. /// + [MethodImpl(InliningOptions.ShortMethod)] public static void MustBeLessThanOrEqualTo(TValue value, TValue max, string parameterName) where TValue : IComparable { if (value.CompareTo(max) > 0) { - throw new ArgumentOutOfRangeException(parameterName, $"Value {value} must be less than or equal to {max}."); + ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be less than or equal to {max}."); } } @@ -119,12 +125,13 @@ namespace SixLabors.ImageSharp /// /// is less than the minimum value. /// + [MethodImpl(InliningOptions.ShortMethod)] public static void MustBeGreaterThan(TValue value, TValue min, string parameterName) where TValue : IComparable { if (value.CompareTo(min) <= 0) { - throw new ArgumentOutOfRangeException( + ThrowArgumentOutOfRangeException( parameterName, $"Value {value} must be greater than {min}."); } @@ -141,12 +148,13 @@ namespace SixLabors.ImageSharp /// /// is less than the minimum value. /// + [MethodImpl(InliningOptions.ShortMethod)] public static void MustBeGreaterThanOrEqualTo(TValue value, TValue min, string parameterName) where TValue : IComparable { if (value.CompareTo(min) < 0) { - throw new ArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min}."); + ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min}."); } } @@ -162,12 +170,13 @@ namespace SixLabors.ImageSharp /// /// is less than the minimum value of greater than the maximum value. /// + [MethodImpl(InliningOptions.ShortMethod)] public static void MustBeBetweenOrEqualTo(TValue value, TValue min, TValue max, string parameterName) where TValue : IComparable { if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0) { - throw new ArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min} and less than or equal to {max}."); + ThrowArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min} and less than or equal to {max}."); } } @@ -181,11 +190,12 @@ namespace SixLabors.ImageSharp /// /// is false /// + [MethodImpl(InliningOptions.ShortMethod)] public static void IsTrue(bool target, string parameterName, string message) { if (!target) { - throw new ArgumentException(message, parameterName); + ThrowArgumentException(message, parameterName); } } @@ -199,11 +209,12 @@ namespace SixLabors.ImageSharp /// /// is true /// + [MethodImpl(InliningOptions.ShortMethod)] public static void IsFalse(bool target, string parameterName, string message) { if (target) { - throw new ArgumentException(message, parameterName); + ThrowArgumentException(message, parameterName); } } @@ -217,11 +228,32 @@ namespace SixLabors.ImageSharp /// /// has less than items /// + [MethodImpl(InliningOptions.ShortMethod)] public static void MustBeSizedAtLeast(ReadOnlySpan source, int minLength, string parameterName) { if (source.Length < minLength) { - throw new ArgumentException($"Span-s must be at least of length {minLength}!", parameterName); + ThrowArgumentException($"Span-s must be at least of length {minLength}!", parameterName); + } + } + + /// + /// Verifies that the 'destination' span is not shorter than 'source'. + /// + /// The source element type + /// The destination element type + /// The source span + /// The destination span + /// The name of the argument for 'destination' + [MethodImpl(InliningOptions.ShortMethod)] + public static void DestinationShouldNotBeTooShort( + ReadOnlySpan source, + Span destination, + string destinationParamName) + { + if (destination.Length < source.Length) + { + ThrowArgumentException($"Destination span is too short!", destinationParamName); } } @@ -235,12 +267,31 @@ namespace SixLabors.ImageSharp /// /// has less than items /// + [MethodImpl(InliningOptions.ShortMethod)] public static void MustBeSizedAtLeast(Span source, int minLength, string parameterName) { if (source.Length < minLength) { - throw new ArgumentException($"Span-s must be at least of length {minLength}!", parameterName); + ThrowArgumentException($"Span-s must be at least of length {minLength}!", parameterName); } } + + [MethodImpl(InliningOptions.ColdPath)] + private static void ThrowArgumentException(string message, string parameterName) + { + throw new ArgumentException(message, parameterName); + } + + [MethodImpl(InliningOptions.ColdPath)] + private static void ThrowArgumentOutOfRangeException(string parameterName, string message) + { + throw new ArgumentOutOfRangeException(parameterName, message); + } + + [MethodImpl(InliningOptions.ColdPath)] + private static void ThrowArgumentNullException(string parameterName) + { + throw new ArgumentNullException(parameterName); + } } } diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs index c15e0a7329..0c5b051809 100644 --- a/src/ImageSharp/Common/Helpers/ImageMaths.cs +++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs @@ -3,7 +3,9 @@ using System; using System.Runtime.CompilerServices; + using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.Primitives; namespace SixLabors.ImageSharp @@ -13,6 +15,118 @@ namespace SixLabors.ImageSharp /// internal static class ImageMaths { + /// + /// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static byte Get8BitBT709Luminance(byte r, byte g, byte b) => + (byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5f); + + /// + /// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b) => + (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F)); + + /// + /// Scales a value from a 16 bit to it's 8 bit equivalent. + /// + /// The 8 bit component value. + /// The + [MethodImpl(InliningOptions.ShortMethod)] + public static byte DownScaleFrom16BitTo8Bit(ushort component) + { + // To scale to 8 bits From a 16-bit value V the required value (from the PNG specification) is: + // + // (V * 255) / 65535 + // + // This reduces to round(V / 257), or floor((V + 128.5)/257) + // + // Represent V as the two byte value vhi.vlo. Make a guess that the + // result is the top byte of V, vhi, then the correction to this value + // is: + // + // error = floor(((V-vhi.vhi) + 128.5) / 257) + // = floor(((vlo-vhi) + 128.5) / 257) + // + // This can be approximated using integer arithmetic (and a signed + // shift): + // + // error = (vlo-vhi+128) >> 8; + // + // The approximate differs from the exact answer only when (vlo-vhi) is + // 128; it then gives a correction of +1 when the exact correction is + // 0. This gives 128 errors. The exact answer (correct for all 16-bit + // input values) is: + // + // error = (vlo-vhi+128)*65535 >> 24; + // + // An alternative arithmetic calculation which also gives no errors is: + // + // (V * 255 + 32895) >> 16 + return (byte)(((component * 255) + 32895) >> 16); + } + + /// + /// Scales a value from an 8 bit to it's 16 bit equivalent. + /// + /// The 8 bit component value. + /// The + [MethodImpl(InliningOptions.ShortMethod)] + public static ushort UpscaleFrom8BitTo16Bit(byte component) => (ushort)(component * 257); + + /// + /// Determine the Greatest CommonDivisor (GCD) of two numbers. + /// + public static int GreatestCommonDivisor(int a, int b) + { + while (b != 0) + { + int temp = b; + b = a % b; + a = temp; + } + + return a; + } + + /// + /// Determine the Least Common Multiple (LCM) of two numbers. + /// + public static int LeastCommonMultiple(int a, int b) + { + // https://en.wikipedia.org/wiki/Least_common_multiple#Reduction_by_the_greatest_common_divisor + return (a / GreatestCommonDivisor(a, b)) * b; + } + + /// + /// Calculates % 4 + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static int Modulo4(int x) => x & 3; + + /// + /// Calculates % 8 + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static int Modulo8(int x) => x & 7; + + /// + /// Fast (x mod m) calculator, with the restriction that + /// should be power of 2. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static int ModuloP2(int x, int m) => x & (m - 1); + /// /// Returns the absolute value of a 32-bit signed integer. Uses bit shifting to speed up the operation. /// @@ -20,13 +134,29 @@ namespace SixLabors.ImageSharp /// A number that is greater than , but less than or equal to /// /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static int FastAbs(int x) { int y = x >> 31; return (x ^ y) - y; } + /// + /// Returns a specified number raised to the power of 2 + /// + /// A single-precision floating-point number + /// The number raised to the power of 2. + [MethodImpl(InliningOptions.ShortMethod)] + public static float Pow2(float x) => x * x; + + /// + /// Returns a specified number raised to the power of 3 + /// + /// A single-precision floating-point number + /// The number raised to the power of 3. + [MethodImpl(InliningOptions.ShortMethod)] + public static float Pow3(float x) => x * x * x; + /// /// Returns how many bits are required to store the specified number of colors. /// Performs a Log2() on the value. @@ -35,7 +165,7 @@ namespace SixLabors.ImageSharp /// /// The /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static int GetBitsNeededForColorDepth(int colors) => Math.Max(1, (int)Math.Ceiling(Math.Log(colors, 2))); /// @@ -43,7 +173,7 @@ namespace SixLabors.ImageSharp /// /// The bit depth. /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static int GetColorCountForBitDepth(int bitDepth) => 1 << bitDepth; /// @@ -52,14 +182,14 @@ namespace SixLabors.ImageSharp /// The x provided to G(x). /// The spread of the blur. /// The Gaussian G(x) - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static float Gaussian(float x, float sigma) { const float Numerator = 1.0f; float denominator = MathF.Sqrt(2 * MathF.PI) * sigma; float exponentNumerator = -x * x; - float exponentDenominator = (float)(2 * Math.Pow(sigma, 2)); + float exponentDenominator = 2 * Pow2(sigma); float left = Numerator / denominator; float right = MathF.Exp(exponentNumerator / exponentDenominator); @@ -75,7 +205,7 @@ namespace SixLabors.ImageSharp /// /// The sine cardinal of . /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static float SinC(float f) { if (MathF.Abs(f) > Constants.Epsilon) @@ -98,17 +228,15 @@ namespace SixLabors.ImageSharp /// /// The . /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static float GetBcValue(float x, float b, float c) { - float temp; - if (x < 0F) { x = -x; } - temp = x * x; + float temp = x * x; if (x < 1F) { x = ((12 - (9 * b) - (6 * c)) * (x * temp)) + ((-18 + (12 * b) + (6 * c)) * temp) + (6 - (2 * b)); @@ -136,7 +264,7 @@ namespace SixLabors.ImageSharp /// /// The bounding . /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight) => new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y); /// diff --git a/src/ImageSharp/Common/Helpers/InliningOptions.cs b/src/ImageSharp/Common/Helpers/InliningOptions.cs index e1d51da8d4..069a426d75 100644 --- a/src/ImageSharp/Common/Helpers/InliningOptions.cs +++ b/src/ImageSharp/Common/Helpers/InliningOptions.cs @@ -1,19 +1,19 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// Uncomment this for verbose profiler results: +// Uncomment this for verbose profiler results. DO NOT PUSH TO MAIN! // #define PROFILING using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp { /// - /// Global inlining options. Helps temporarily disable inling for better profiler output. + /// Global inlining options. Helps temporarily disable inlining for better profiler output. /// internal static class InliningOptions { #if PROFILING - public const MethodImplOptions ShortMethod = 0; + public const MethodImplOptions ShortMethod = MethodImplOptions.NoInlining; #else public const MethodImplOptions ShortMethod = MethodImplOptions.AggressiveInlining; #endif diff --git a/src/ImageSharp/Common/Helpers/ParallelFor.cs b/src/ImageSharp/Common/Helpers/ParallelFor.cs deleted file mode 100644 index 4c14bb6e3d..0000000000 --- a/src/ImageSharp/Common/Helpers/ParallelFor.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Buffers; -using System.Threading.Tasks; -using SixLabors.Memory; - -namespace SixLabors.ImageSharp -{ - /// - /// Utility methods for Parallel.For() execution. Use this instead of raw calls! - /// - internal static class ParallelFor - { - /// - /// Helper method to execute Parallel.For using the settings in - /// - public static void WithConfiguration(int fromInclusive, int toExclusive, Configuration configuration, Action body) - { - Parallel.For(fromInclusive, toExclusive, configuration.GetParallelOptions(), body); - } - - /// - /// Helper method to execute Parallel.For with temporary worker buffer shared between executing tasks. - /// The buffer is not guaranteed to be clean! - /// - /// The value type of the buffer - /// The start index, inclusive. - /// The end index, exclusive. - /// The used for getting the and - /// The length of the requested parallel buffer - /// The delegate that is invoked once per iteration. - public static void WithTemporaryBuffer( - int fromInclusive, - int toExclusive, - Configuration configuration, - int bufferLength, - Action> body) - where T : struct - { - MemoryAllocator memoryAllocator = configuration.MemoryAllocator; - ParallelOptions parallelOptions = configuration.GetParallelOptions(); - - IMemoryOwner InitBuffer() - { - return memoryAllocator.Allocate(bufferLength); - } - - void CleanUpBuffer(IMemoryOwner buffer) - { - buffer.Dispose(); - } - - IMemoryOwner BodyFunc(int i, ParallelLoopState state, IMemoryOwner buffer) - { - body(i, buffer); - return buffer; - } - - Parallel.For(fromInclusive, toExclusive, parallelOptions, InitBuffer, BodyFunc, CleanUpBuffer); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs new file mode 100644 index 0000000000..85c9f00748 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs @@ -0,0 +1,215 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Tuples; + +// ReSharper disable MemberHidesStaticFromOuterClass +namespace SixLabors.ImageSharp +{ + internal static partial class SimdUtils + { + /// + /// Implementation with 256bit / AVX2 intrinsics NOT depending on newer API-s (Vector.Widen etc.) + /// + public static class BasicIntrinsics256 + { + public static bool IsAvailable { get; } = IsAvx2CompatibleArchitecture; + + /// + /// as many elements as possible, slicing them down (keeping the remainder). + /// + [MethodImpl(InliningOptions.ShortMethod)] + internal static void BulkConvertByteToNormalizedFloatReduce( + ref ReadOnlySpan source, + ref Span dest) + { + DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); + + if (!IsAvailable) + { + return; + } + + int remainder = ImageMaths.Modulo8(source.Length); + int adjustedCount = source.Length - remainder; + + if (adjustedCount > 0) + { + BulkConvertByteToNormalizedFloat( + source.Slice(0, adjustedCount), + dest.Slice(0, adjustedCount)); + + source = source.Slice(adjustedCount); + dest = dest.Slice(adjustedCount); + } + } + + /// + /// as many elements as possible, slicing them down (keeping the remainder). + /// + [MethodImpl(InliningOptions.ShortMethod)] + internal static void BulkConvertNormalizedFloatToByteClampOverflowsReduce( + ref ReadOnlySpan source, + ref Span dest) + { + DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); + + if (!IsAvailable) + { + return; + } + + int remainder = ImageMaths.Modulo8(source.Length); + int adjustedCount = source.Length - remainder; + + if (adjustedCount > 0) + { + BulkConvertNormalizedFloatToByteClampOverflows(source.Slice(0, adjustedCount), dest.Slice(0, adjustedCount)); + + source = source.Slice(adjustedCount); + dest = dest.Slice(adjustedCount); + } + } + + /// + /// SIMD optimized implementation for . + /// Works only with span Length divisible by 8. + /// Implementation adapted from: + /// http://lolengine.net/blog/2011/3/20/understanding-fast-float-integer-conversions + /// http://stackoverflow.com/a/536278 + /// + internal static void BulkConvertByteToNormalizedFloat(ReadOnlySpan source, Span dest) + { + VerifyIsAvx2Compatible(nameof(BulkConvertByteToNormalizedFloat)); + VerifySpanInput(source, dest, 8); + + var bVec = new Vector(256.0f / 255.0f); + var magicFloat = new Vector(32768.0f); + var magicInt = new Vector(1191182336); // reinterpreted value of 32768.0f + var mask = new Vector(255); + + ref Octet.OfByte sourceBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref Octet.OfUInt32 destBaseAsWideOctet = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); + + ref Vector destBaseAsFloat = ref Unsafe.As>(ref destBaseAsWideOctet); + + int n = dest.Length / 8; + + for (int i = 0; i < n; i++) + { + ref Octet.OfByte s = ref Unsafe.Add(ref sourceBase, i); + ref Octet.OfUInt32 d = ref Unsafe.Add(ref destBaseAsWideOctet, i); + d.LoadFrom(ref s); + } + + for (int i = 0; i < n; i++) + { + ref Vector df = ref Unsafe.Add(ref destBaseAsFloat, i); + + var vi = Vector.AsVectorUInt32(df); + vi &= mask; + vi |= magicInt; + + var vf = Vector.AsVectorSingle(vi); + vf = (vf - magicFloat) * bVec; + + df = vf; + } + } + + /// + /// Implementation of which is faster on older runtimes. + /// + internal static void BulkConvertNormalizedFloatToByteClampOverflows(ReadOnlySpan source, Span dest) + { + VerifyIsAvx2Compatible(nameof(BulkConvertNormalizedFloatToByteClampOverflows)); + VerifySpanInput(source, dest, 8); + + if (source.Length == 0) + { + return; + } + + ref Vector srcBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Octet.OfByte destBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); + int n = source.Length / 8; + + Vector magick = new Vector(32768.0f); + Vector scale = new Vector(255f) / new Vector(256f); + + // need to copy to a temporary struct, because + // SimdUtils.Octet.OfUInt32 temp = Unsafe.As, SimdUtils.Octet.OfUInt32>(ref x) + // does not work. TODO: This might be a CoreClr bug, need to ask/report + var temp = default(Octet.OfUInt32); + ref Vector tempRef = ref Unsafe.As>(ref temp); + + for (int i = 0; i < n; i++) + { + // union { float f; uint32_t i; } u; + // u.f = 32768.0f + x * (255.0f / 256.0f); + // return (uint8_t)u.i; + Vector x = Unsafe.Add(ref srcBase, i); + x = Vector.Max(x, Vector.Zero); + x = Vector.Min(x, Vector.One); + + x = (x * scale) + magick; + tempRef = x; + + ref Octet.OfByte d = ref Unsafe.Add(ref destBase, i); + d.LoadFrom(ref temp); + } + } + + /// + /// Convert all values normalized into [0..1] from 'source' + /// into 'dest' buffer of . The values are scaled up into [0-255] and rounded. + /// This implementation is SIMD optimized and works only when span Length is divisible by 8. + /// Based on: + /// + /// http://lolengine.net/blog/2011/3/20/understanding-fast-float-integer-conversions + /// + /// + internal static void BulkConvertNormalizedFloatToByte(ReadOnlySpan source, Span dest) + { + VerifyIsAvx2Compatible(nameof(BulkConvertNormalizedFloatToByte)); + VerifySpanInput(source, dest, 8); + + if (source.Length == 0) + { + return; + } + + ref Vector srcBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Octet.OfByte destBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); + int n = source.Length / 8; + + Vector magick = new Vector(32768.0f); + Vector scale = new Vector(255f) / new Vector(256f); + + // need to copy to a temporary struct, because + // SimdUtils.Octet.OfUInt32 temp = Unsafe.As, SimdUtils.Octet.OfUInt32>(ref x) + // does not work. TODO: This might be a CoreClr bug, need to ask/report + var temp = default(Octet.OfUInt32); + ref Vector tempRef = ref Unsafe.As>(ref temp); + + for (int i = 0; i < n; i++) + { + // union { float f; uint32_t i; } u; + // u.f = 32768.0f + x * (255.0f / 256.0f); + // return (uint8_t)u.i; + Vector x = Unsafe.Add(ref srcBase, i); + x = (x * scale) + magick; + tempRef = x; + + ref Octet.OfByte d = ref Unsafe.Add(ref destBase, i); + d.LoadFrom(ref temp); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs new file mode 100644 index 0000000000..9aeb209319 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs @@ -0,0 +1,190 @@ +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// ReSharper disable MemberHidesStaticFromOuterClass +namespace SixLabors.ImageSharp +{ + internal static partial class SimdUtils + { + /// + /// Implementation methods based on newer API-s (Vector.Widen, Vector.Narrow, Vector.ConvertTo*). + /// Only accelerated only on RyuJIT having dotnet/coreclr#10662 merged (.NET Core 2.1+ .NET 4.7.2+) + /// See: + /// https://github.com/dotnet/coreclr/pull/10662 + /// API Proposal: + /// https://github.com/dotnet/corefx/issues/15957 + /// + public static class ExtendedIntrinsics + { + public static bool IsAvailable { get; } = +#if SUPPORTS_EXTENDED_INTRINSICS + Vector.IsHardwareAccelerated; +#else + false; +#endif + + /// + /// Widen and convert a vector of values into 2 vectors of -s. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void ConvertToSingle( + Vector source, + out Vector dest1, + out Vector dest2) + { + Vector.Widen(source, out Vector i1, out Vector i2); + dest1 = Vector.ConvertToSingle(i1); + dest2 = Vector.ConvertToSingle(i2); + } + + /// + /// as many elements as possible, slicing them down (keeping the remainder). + /// + [MethodImpl(InliningOptions.ShortMethod)] + internal static void BulkConvertByteToNormalizedFloatReduce( + ref ReadOnlySpan source, + ref Span dest) + { + DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); + + if (!IsAvailable) + { + return; + } + + int remainder = ImageMaths.ModuloP2(source.Length, Vector.Count); + int adjustedCount = source.Length - remainder; + + if (adjustedCount > 0) + { + BulkConvertByteToNormalizedFloat(source.Slice(0, adjustedCount), dest.Slice(0, adjustedCount)); + + source = source.Slice(adjustedCount); + dest = dest.Slice(adjustedCount); + } + } + + /// + /// as many elements as possible, slicing them down (keeping the remainder). + /// + [MethodImpl(InliningOptions.ShortMethod)] + internal static void BulkConvertNormalizedFloatToByteClampOverflowsReduce( + ref ReadOnlySpan source, + ref Span dest) + { + DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); + + if (!IsAvailable) + { + return; + } + + int remainder = ImageMaths.ModuloP2(source.Length, Vector.Count); + int adjustedCount = source.Length - remainder; + + if (adjustedCount > 0) + { + BulkConvertNormalizedFloatToByteClampOverflows( + source.Slice(0, adjustedCount), + dest.Slice(0, adjustedCount)); + + source = source.Slice(adjustedCount); + dest = dest.Slice(adjustedCount); + } + } + + /// + /// Implementation , which is faster on new RyuJIT runtime. + /// + internal static void BulkConvertByteToNormalizedFloat(ReadOnlySpan source, Span dest) + { + VerifySpanInput(source, dest, Vector.Count); + + int n = dest.Length / Vector.Count; + + ref Vector sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); + + for (int i = 0; i < n; i++) + { + Vector b = Unsafe.Add(ref sourceBase, i); + + Vector.Widen(b, out Vector s0, out Vector s1); + Vector.Widen(s0, out Vector w0, out Vector w1); + Vector.Widen(s1, out Vector w2, out Vector w3); + + Vector f0 = ConvertToSingle(w0); + Vector f1 = ConvertToSingle(w1); + Vector f2 = ConvertToSingle(w2); + Vector f3 = ConvertToSingle(w3); + + ref Vector d = ref Unsafe.Add(ref destBase, i * 4); + d = f0; + Unsafe.Add(ref d, 1) = f1; + Unsafe.Add(ref d, 2) = f2; + Unsafe.Add(ref d, 3) = f3; + } + } + + /// + /// Implementation of , which is faster on new .NET runtime. + /// + internal static void BulkConvertNormalizedFloatToByteClampOverflows( + ReadOnlySpan source, + Span dest) + { + VerifySpanInput(source, dest, Vector.Count); + + int n = dest.Length / Vector.Count; + + ref Vector sourceBase = + ref Unsafe.As>(ref MemoryMarshal.GetReference(source)); + ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest)); + + for (int i = 0; i < n; i++) + { + ref Vector s = ref Unsafe.Add(ref sourceBase, i * 4); + + Vector f0 = s; + Vector f1 = Unsafe.Add(ref s, 1); + Vector f2 = Unsafe.Add(ref s, 2); + Vector f3 = Unsafe.Add(ref s, 3); + + Vector w0 = ConvertToUInt32(f0); + Vector w1 = ConvertToUInt32(f1); + Vector w2 = ConvertToUInt32(f2); + Vector w3 = ConvertToUInt32(f3); + + Vector u0 = Vector.Narrow(w0, w1); + Vector u1 = Vector.Narrow(w2, w3); + + Vector b = Vector.Narrow(u0, u1); + + Unsafe.Add(ref destBase, i) = b; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector ConvertToUInt32(Vector vf) + { + Vector maxBytes = new Vector(255f); + vf *= maxBytes; + vf += new Vector(0.5f); + vf = Vector.Min(Vector.Max(vf, Vector.Zero), maxBytes); + Vector vi = Vector.ConvertToInt32(vf); + return Vector.AsVectorUInt32(vi); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector ConvertToSingle(Vector u) + { + Vector vi = Vector.AsVectorInt32(u); + Vector v = Vector.ConvertToSingle(vi); + v *= new Vector(1f / 255f); + return v; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs new file mode 100644 index 0000000000..565ea08f5d --- /dev/null +++ b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs @@ -0,0 +1,151 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// ReSharper disable MemberHidesStaticFromOuterClass +namespace SixLabors.ImageSharp +{ + internal static partial class SimdUtils + { + /// + /// Fallback implementation based on (128bit). + /// For , efficient software fallback implementations are present, + /// and we hope that even mono's JIT is able to emit SIMD instructions for that type :P + /// + public static class FallbackIntrinsics128 + { + /// + /// as many elements as possible, slicing them down (keeping the remainder). + /// + [MethodImpl(InliningOptions.ShortMethod)] + internal static void BulkConvertByteToNormalizedFloatReduce( + ref ReadOnlySpan source, + ref Span dest) + { + DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); + + int remainder = ImageMaths.Modulo4(source.Length); + int adjustedCount = source.Length - remainder; + + if (adjustedCount > 0) + { + BulkConvertByteToNormalizedFloat( + source.Slice(0, adjustedCount), + dest.Slice(0, adjustedCount)); + + source = source.Slice(adjustedCount); + dest = dest.Slice(adjustedCount); + } + } + + /// + /// as many elements as possible, slicing them down (keeping the remainder). + /// + [MethodImpl(InliningOptions.ShortMethod)] + internal static void BulkConvertNormalizedFloatToByteClampOverflowsReduce( + ref ReadOnlySpan source, + ref Span dest) + { + DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); + + int remainder = ImageMaths.Modulo4(source.Length); + int adjustedCount = source.Length - remainder; + + if (adjustedCount > 0) + { + BulkConvertNormalizedFloatToByteClampOverflows( + source.Slice(0, adjustedCount), + dest.Slice(0, adjustedCount)); + + source = source.Slice(adjustedCount); + dest = dest.Slice(adjustedCount); + } + } + + /// + /// Implementation of using . + /// + [MethodImpl(InliningOptions.ColdPath)] + internal static void BulkConvertByteToNormalizedFloat(ReadOnlySpan source, Span dest) + { + VerifySpanInput(source, dest, 4); + + int count = dest.Length / 4; + if (count == 0) + { + return; + } + + ref ByteVector4 sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref Vector4 dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); + + const float Scale = 1f / 255f; + Vector4 d = default; + + for (int i = 0; i < count; i++) + { + ref ByteVector4 s = ref Unsafe.Add(ref sBase, i); + d.X = s.X; + d.Y = s.Y; + d.Z = s.Z; + d.W = s.W; + d *= Scale; + Unsafe.Add(ref dBase, i) = d; + } + } + + /// + /// Implementation of using . + /// + [MethodImpl(InliningOptions.ColdPath)] + internal static void BulkConvertNormalizedFloatToByteClampOverflows( + ReadOnlySpan source, + Span dest) + { + VerifySpanInput(source, dest, 4); + + int count = source.Length / 4; + if (count == 0) + { + return; + } + + ref Vector4 sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); + ref ByteVector4 dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); + + var half = new Vector4(0.5f); + var maxBytes = new Vector4(255f); + + for (int i = 0; i < count; i++) + { + Vector4 s = Unsafe.Add(ref sBase, i); + s *= maxBytes; + s += half; + + // I'm not sure if Vector4.Clamp() is properly implemented with intrinsics. + s = Vector4.Max(Vector4.Zero, s); + s = Vector4.Min(maxBytes, s); + + ref ByteVector4 d = ref Unsafe.Add(ref dBase, i); + d.X = (byte)s.X; + d.Y = (byte)s.Y; + d.Z = (byte)s.Z; + d.W = (byte)s.W; + } + } + + [StructLayout(LayoutKind.Sequential)] + private struct ByteVector4 + { + public byte X; + public byte Y; + public byte Z; + public byte W; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.cs b/src/ImageSharp/Common/Helpers/SimdUtils.cs new file mode 100644 index 0000000000..867e7b9de1 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/SimdUtils.cs @@ -0,0 +1,185 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tuples; + +namespace SixLabors.ImageSharp +{ + /// + /// Various extension and utility methods for and utilizing SIMD capabilities + /// + internal static partial class SimdUtils + { + /// + /// Gets a value indicating whether the code is being executed on AVX2 CPU where both float and integer registers are of size 256 byte. + /// + public static bool IsAvx2CompatibleArchitecture { get; } = + Vector.IsHardwareAccelerated && Vector.Count == 8 && Vector.Count == 8; + + /// + /// Transform all scalars in 'v' in a way that converting them to would have rounding semantics. + /// + /// The vector + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector4 PseudoRound(this Vector4 v) + { + var sign = Vector4.Clamp(v, new Vector4(-1), new Vector4(1)); + + return v + (sign * 0.5f); + } + + /// + /// Rounds all values in 'v' to the nearest integer following semantics. + /// Source: + /// + /// https://github.com/g-truc/glm/blob/master/glm/simd/common.h#L110 + /// + /// + /// The vector + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Vector FastRound(this Vector v) + { + Vector magic0 = new Vector(int.MinValue); // 0x80000000 + Vector sgn0 = Vector.AsVectorSingle(magic0); + Vector and0 = Vector.BitwiseAnd(sgn0, v); + Vector or0 = Vector.BitwiseOr(and0, new Vector(8388608.0f)); + Vector add0 = Vector.Add(v, or0); + Vector sub0 = Vector.Subtract(add0, or0); + return sub0; + } + + /// + /// Converts all input -s to -s normalized into [0..1]. + /// should be the of the same size as , + /// but there are no restrictions on the span's length. + /// + /// The source span of bytes + /// The destination span of floats + [MethodImpl(InliningOptions.ShortMethod)] + internal static void BulkConvertByteToNormalizedFloat(ReadOnlySpan source, Span dest) + { + DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); + +#if SUPPORTS_EXTENDED_INTRINSICS + ExtendedIntrinsics.BulkConvertByteToNormalizedFloatReduce(ref source, ref dest); +#else + BasicIntrinsics256.BulkConvertByteToNormalizedFloatReduce(ref source, ref dest); +#endif + FallbackIntrinsics128.BulkConvertByteToNormalizedFloatReduce(ref source, ref dest); + + // Deal with the remainder: + if (source.Length > 0) + { + ConvertByteToNormalizedFloatRemainder(source, dest); + } + } + + /// + /// Convert all values normalized into [0..1] from 'source' into 'dest' buffer of . + /// The values are scaled up into [0-255] and rounded, overflows are clamped. + /// should be the of the same size as , + /// but there are no restrictions on the span's length. + /// + /// The source span of floats + /// The destination span of bytes + [MethodImpl(InliningOptions.ShortMethod)] + internal static void BulkConvertNormalizedFloatToByteClampOverflows(ReadOnlySpan source, Span dest) + { + DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); + +#if SUPPORTS_EXTENDED_INTRINSICS + ExtendedIntrinsics.BulkConvertNormalizedFloatToByteClampOverflowsReduce(ref source, ref dest); +#else + BasicIntrinsics256.BulkConvertNormalizedFloatToByteClampOverflowsReduce(ref source, ref dest); +#endif + FallbackIntrinsics128.BulkConvertNormalizedFloatToByteClampOverflowsReduce(ref source, ref dest); + + // Deal with the remainder: + if (source.Length > 0) + { + ConvertNormalizedFloatToByteRemainder(source, dest); + } + } + + [MethodImpl(InliningOptions.ColdPath)] + private static void ConvertByteToNormalizedFloatRemainder(ReadOnlySpan source, Span dest) + { + ref byte sBase = ref MemoryMarshal.GetReference(source); + ref float dBase = ref MemoryMarshal.GetReference(dest); + + // There are at most 3 elements at this point, having a for loop is overkill. + // Let's minimize the no. of instructions! + switch (source.Length) + { + case 3: + Unsafe.Add(ref dBase, 2) = Unsafe.Add(ref sBase, 2) / 255f; + goto case 2; + case 2: + Unsafe.Add(ref dBase, 1) = Unsafe.Add(ref sBase, 1) / 255f; + goto case 1; + case 1: + dBase = sBase / 255f; + break; + } + } + + [MethodImpl(InliningOptions.ColdPath)] + private static void ConvertNormalizedFloatToByteRemainder(ReadOnlySpan source, Span dest) + { + ref float sBase = ref MemoryMarshal.GetReference(source); + ref byte dBase = ref MemoryMarshal.GetReference(dest); + + switch (source.Length) + { + case 3: + Unsafe.Add(ref dBase, 2) = ConvertToByte(Unsafe.Add(ref sBase, 2)); + goto case 2; + case 2: + Unsafe.Add(ref dBase, 1) = ConvertToByte(Unsafe.Add(ref sBase, 1)); + goto case 1; + case 1: + dBase = ConvertToByte(sBase); + break; + } + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static byte ConvertToByte(float f) => (byte)ComparableExtensions.Clamp((f * 255f) + 0.5f, 0, 255f); + + [Conditional("DEBUG")] + private static void VerifyIsAvx2Compatible(string operation) + { + if (!IsAvx2CompatibleArchitecture) + { + throw new NotSupportedException($"{operation} is supported only on AVX2 CPU!"); + } + } + + [Conditional("DEBUG")] + private static void VerifySpanInput(ReadOnlySpan source, Span dest, int shouldBeDivisibleBy) + { + DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); + DebugGuard.IsTrue( + ImageMaths.ModuloP2(dest.Length, shouldBeDivisibleBy) == 0, + nameof(source), + $"length should be divisible by {shouldBeDivisibleBy}!"); + } + + [Conditional("DEBUG")] + private static void VerifySpanInput(ReadOnlySpan source, Span dest, int shouldBeDivisibleBy) + { + DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); + DebugGuard.IsTrue( + ImageMaths.ModuloP2(dest.Length, shouldBeDivisibleBy) == 0, + nameof(source), + $"length should be divisible by {shouldBeDivisibleBy}!"); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Helpers/TestHelpers.cs b/src/ImageSharp/Common/Helpers/TestHelpers.cs index 14e5835b49..fd16a577ab 100644 --- a/src/ImageSharp/Common/Helpers/TestHelpers.cs +++ b/src/ImageSharp/Common/Helpers/TestHelpers.cs @@ -13,10 +13,12 @@ namespace SixLabors.ImageSharp.Common.Helpers /// Only intended to be used in tests! /// internal const string ImageSharpBuiltAgainst = -#if NETSTANDARD1_1 - "netstandard1.1"; +#if NET472 + "netfx4.7.2"; #elif NETCOREAPP2_1 "netcoreapp2.1"; +#elif NETSTANDARD1_3 + "netstandard1.3"; #else "netstandard2.0"; #endif diff --git a/src/ImageSharp/Common/Helpers/TolerantMath.cs b/src/ImageSharp/Common/Helpers/TolerantMath.cs new file mode 100644 index 0000000000..5347efcc09 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/TolerantMath.cs @@ -0,0 +1,101 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp +{ + /// + /// Implements basic math operations using tolerant comparison + /// whenever an equality check is needed. + /// + internal readonly struct TolerantMath + { + private readonly double epsilon; + + private readonly double negEpsilon; + + public TolerantMath(double epsilon) + { + DebugGuard.MustBeGreaterThan(epsilon, 0, nameof(epsilon)); + + this.epsilon = epsilon; + this.negEpsilon = -epsilon; + } + + public static TolerantMath Default { get; } = new TolerantMath(1e-8); + + /// + /// == 0 + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool IsZero(double a) => a > this.negEpsilon && a < this.epsilon; + + /// + /// > 0 + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool IsPositive(double a) => a > this.epsilon; + + /// + /// < 0 + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool IsNegative(double a) => a < this.negEpsilon; + + /// + /// == + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool AreEqual(double a, double b) => this.IsZero(a - b); + + /// + /// > + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool IsGreater(double a, double b) => a > b + this.epsilon; + + /// + /// < + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool IsLess(double a, double b) => a < b - this.epsilon; + + /// + /// >= + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool IsGreaterOrEqual(double a, double b) => a >= b - this.epsilon; + + /// + /// <= + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool IsLessOrEqual(double a, double b) => b >= a - this.epsilon; + + [MethodImpl(InliningOptions.ShortMethod)] + public double Ceiling(double a) + { + double rem = Math.IEEERemainder(a, 1); + if (this.IsZero(rem)) + { + return Math.Round(a); + } + + return Math.Ceiling(a); + } + + [MethodImpl(InliningOptions.ShortMethod)] + public double Floor(double a) + { + double rem = Math.IEEERemainder(a, 1); + if (this.IsZero(rem)) + { + return Math.Round(a); + } + + return Math.Floor(a); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Helpers/Vector4Utils.cs b/src/ImageSharp/Common/Helpers/Vector4Utils.cs new file mode 100644 index 0000000000..75bb00b6a5 --- /dev/null +++ b/src/ImageSharp/Common/Helpers/Vector4Utils.cs @@ -0,0 +1,74 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp +{ + /// + /// Utility methods for the struct. + /// + internal static class Vector4Utils + { + /// + /// Pre-multiplies the "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact. + /// + /// The to premultiply + [MethodImpl(InliningOptions.ShortMethod)] + public static void Premultiply(ref Vector4 source) + { + float w = source.W; + source *= w; + source.W = w; + } + + /// + /// Reverses the result of premultiplying a vector via . + /// + /// The to premultiply + [MethodImpl(InliningOptions.ShortMethod)] + public static void UnPremultiply(ref Vector4 source) + { + float w = source.W; + source /= w; + source.W = w; + } + + /// + /// Bulk variant of + /// + /// The span of vectors + [MethodImpl(InliningOptions.ShortMethod)] + public static void Premultiply(Span vectors) + { + // TODO: This method can be AVX2 optimized using Vector + ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); + + for (int i = 0; i < vectors.Length; i++) + { + ref Vector4 v = ref Unsafe.Add(ref baseRef, i); + Premultiply(ref v); + } + } + + /// + /// Bulk variant of + /// + /// The span of vectors + [MethodImpl(InliningOptions.ShortMethod)] + public static void UnPremultiply(Span vectors) + { + // TODO: This method can be AVX2 optimized using Vector + ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); + + for (int i = 0; i < vectors.Length; i++) + { + ref Vector4 v = ref Unsafe.Add(ref baseRef, i); + UnPremultiply(ref v); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs b/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs new file mode 100644 index 0000000000..0b45719c38 --- /dev/null +++ b/src/ImageSharp/Common/ParallelUtils/ParallelExecutionSettings.cs @@ -0,0 +1,71 @@ +// Copyright(c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Threading.Tasks; + +using SixLabors.Memory; + +namespace SixLabors.ImageSharp.ParallelUtils +{ + /// + /// Defines execution settings for methods in . + /// + internal readonly struct ParallelExecutionSettings + { + /// + /// Default value for . + /// + public const int DefaultMinimumPixelsProcessedPerTask = 4096; + + /// + /// Initializes a new instance of the struct. + /// + public ParallelExecutionSettings( + int maxDegreeOfParallelism, + int minimumPixelsProcessedPerTask, + MemoryAllocator memoryAllocator) + { + this.MaxDegreeOfParallelism = maxDegreeOfParallelism; + this.MinimumPixelsProcessedPerTask = minimumPixelsProcessedPerTask; + this.MemoryAllocator = memoryAllocator; + } + + /// + /// Initializes a new instance of the struct. + /// + public ParallelExecutionSettings(int maxDegreeOfParallelism, MemoryAllocator memoryAllocator) + : this(maxDegreeOfParallelism, DefaultMinimumPixelsProcessedPerTask, memoryAllocator) + { + } + + /// + /// Gets the MemoryAllocator + /// + public MemoryAllocator MemoryAllocator { get; } + + /// + /// Gets the value used for initializing when using TPL. + /// + public int MaxDegreeOfParallelism { get; } + + /// + /// Gets the minimum number of pixels being processed by a single task when parallelizing operations with TPL. + /// Launching tasks for pixel regions below this limit is not worth the overhead. + /// Initialized with by default, + /// the optimum value is operation specific. (The cheaper the operation, the larger the value is.) + /// + public int MinimumPixelsProcessedPerTask { get; } + + /// + /// Creates a new instance of + /// having multiplied by + /// + public ParallelExecutionSettings MultiplyMinimumPixelsPerTask(int multiplier) + { + return new ParallelExecutionSettings( + this.MaxDegreeOfParallelism, + this.MinimumPixelsProcessedPerTask * multiplier, + this.MemoryAllocator); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs b/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs new file mode 100644 index 0000000000..1d1734a863 --- /dev/null +++ b/src/ImageSharp/Common/ParallelUtils/ParallelHelper.cs @@ -0,0 +1,146 @@ +// Copyright(c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +using SixLabors.ImageSharp.Memory; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.ParallelUtils +{ + /// + /// Utility methods for batched processing of pixel row intervals. + /// Parallel execution is optimized for image processing. + /// Use this instead of direct calls! + /// + internal static class ParallelHelper + { + /// + /// Get the default for a + /// + public static ParallelExecutionSettings GetParallelSettings(this Configuration configuration) + { + return new ParallelExecutionSettings(configuration.MaxDegreeOfParallelism, configuration.MemoryAllocator); + } + + /// + /// Iterate through the rows of a rectangle in optimized batches defined by -s. + /// + public static void IterateRows(Rectangle rectangle, Configuration configuration, Action body) + { + ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings(); + + IterateRows(rectangle, parallelSettings, body); + } + + /// + /// Iterate through the rows of a rectangle in optimized batches defined by -s. + /// + public static void IterateRows( + Rectangle rectangle, + in ParallelExecutionSettings parallelSettings, + Action body) + { + DebugGuard.MustBeGreaterThan(rectangle.Width, 0, nameof(rectangle)); + + int maxSteps = DivideCeil(rectangle.Width * rectangle.Height, parallelSettings.MinimumPixelsProcessedPerTask); + + int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); + + // Avoid TPL overhead in this trivial case: + if (numOfSteps == 1) + { + var rows = new RowInterval(rectangle.Top, rectangle.Bottom); + body(rows); + return; + } + + int verticalStep = DivideCeil(rectangle.Height, numOfSteps); + + var parallelOptions = new ParallelOptions() { MaxDegreeOfParallelism = numOfSteps }; + + Parallel.For( + 0, + numOfSteps, + parallelOptions, + i => + { + int yMin = rectangle.Top + (i * verticalStep); + int yMax = Math.Min(yMin + verticalStep, rectangle.Bottom); + + var rows = new RowInterval(yMin, yMax); + body(rows); + }); + } + + /// + /// Iterate through the rows of a rectangle in optimized batches defined by -s + /// instantiating a temporary buffer for each invocation. + /// + public static void IterateRowsWithTempBuffer( + Rectangle rectangle, + in ParallelExecutionSettings parallelSettings, + Action> body) + where T : struct + { + int maxSteps = DivideCeil(rectangle.Width * rectangle.Height, parallelSettings.MinimumPixelsProcessedPerTask); + + int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps); + + MemoryAllocator memoryAllocator = parallelSettings.MemoryAllocator; + + // Avoid TPL overhead in this trivial case: + if (numOfSteps == 1) + { + var rows = new RowInterval(rectangle.Top, rectangle.Bottom); + using (IMemoryOwner buffer = memoryAllocator.Allocate(rectangle.Width)) + { + body(rows, buffer.Memory); + } + + return; + } + + int verticalStep = DivideCeil(rectangle.Height, numOfSteps); + + var parallelOptions = new ParallelOptions() { MaxDegreeOfParallelism = numOfSteps }; + + Parallel.For( + 0, + numOfSteps, + parallelOptions, + i => + { + int yMin = rectangle.Top + (i * verticalStep); + int yMax = Math.Min(yMin + verticalStep, rectangle.Bottom); + + var rows = new RowInterval(yMin, yMax); + + using (IMemoryOwner buffer = memoryAllocator.Allocate(rectangle.Width)) + { + body(rows, buffer.Memory); + } + }); + } + + /// + /// Iterate through the rows of a rectangle in optimized batches defined by -s + /// instantiating a temporary buffer for each invocation. + /// + public static void IterateRowsWithTempBuffer( + Rectangle rectangle, + Configuration configuration, + Action> body) + where T : struct + { + IterateRowsWithTempBuffer(rectangle, configuration.GetParallelSettings(), body); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int DivideCeil(int dividend, int divisor) => 1 + ((dividend - 1) / divisor); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Tuples/Octet.cs b/src/ImageSharp/Common/Tuples/Octet.cs new file mode 100644 index 0000000000..539b74e324 --- /dev/null +++ b/src/ImageSharp/Common/Tuples/Octet.cs @@ -0,0 +1,109 @@ +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Tuples +{ + /// + /// Contains 8 element value tuples of various types. + /// + internal static class Octet + { + /// + /// Value tuple of -s + /// + [StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(uint))] + public struct OfUInt32 + { + [FieldOffset(0 * sizeof(uint))] + public uint V0; + + [FieldOffset(1 * sizeof(uint))] + public uint V1; + + [FieldOffset(2 * sizeof(uint))] + public uint V2; + + [FieldOffset(3 * sizeof(uint))] + public uint V3; + + [FieldOffset(4 * sizeof(uint))] + public uint V4; + + [FieldOffset(5 * sizeof(uint))] + public uint V5; + + [FieldOffset(6 * sizeof(uint))] + public uint V6; + + [FieldOffset(7 * sizeof(uint))] + public uint V7; + + public override string ToString() + { + return $"{nameof(Octet)}.{nameof(OfUInt32)}({this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7})"; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public void LoadFrom(ref OfByte src) + { + this.V0 = src.V0; + this.V1 = src.V1; + this.V2 = src.V2; + this.V3 = src.V3; + this.V4 = src.V4; + this.V5 = src.V5; + this.V6 = src.V6; + this.V7 = src.V7; + } + } + + /// + /// Value tuple of -s + /// + [StructLayout(LayoutKind.Explicit, Size = 8)] + public struct OfByte + { + [FieldOffset(0)] + public byte V0; + + [FieldOffset(1)] + public byte V1; + + [FieldOffset(2)] + public byte V2; + + [FieldOffset(3)] + public byte V3; + + [FieldOffset(4)] + public byte V4; + + [FieldOffset(5)] + public byte V5; + + [FieldOffset(6)] + public byte V6; + + [FieldOffset(7)] + public byte V7; + + public override string ToString() + { + return $"{nameof(Octet)}.{nameof(OfByte)}({this.V0},{this.V1},{this.V2},{this.V3},{this.V4},{this.V5},{this.V6},{this.V7})"; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public void LoadFrom(ref OfUInt32 src) + { + this.V0 = (byte)src.V0; + this.V1 = (byte)src.V1; + this.V2 = (byte)src.V2; + this.V3 = (byte)src.V3; + this.V4 = (byte)src.V4; + this.V5 = (byte)src.V5; + this.V6 = (byte)src.V6; + this.V7 = (byte)src.V7; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Tuples/Vector4Pair.cs b/src/ImageSharp/Common/Tuples/Vector4Pair.cs index 309d5e2e56..cae283d628 100644 --- a/src/ImageSharp/Common/Tuples/Vector4Pair.cs +++ b/src/ImageSharp/Common/Tuples/Vector4Pair.cs @@ -2,11 +2,12 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.Common.Tuples +namespace SixLabors.ImageSharp.Tuples { /// /// Its faster to process multiple Vector4-s together, so let's pair them! /// On AVX2 this pair should be convertible to of ! + /// TODO: Investigate defining this as union with an Octet.OfSingle type. /// [StructLayout(LayoutKind.Sequential)] internal struct Vector4Pair @@ -15,8 +16,6 @@ namespace SixLabors.ImageSharp.Common.Tuples public Vector4 B; - private static readonly Vector4 Scale = new Vector4(1 / 255f); - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void MultiplyInplace(float value) { @@ -52,8 +51,9 @@ namespace SixLabors.ImageSharp.Common.Tuples b = b.FastRound(); // Downscale by 1/255 - this.A *= Scale; - this.B *= Scale; + var scale = new Vector4(1 / 255f); + this.A *= scale; + this.B *= scale; } /// @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Common.Tuples public override string ToString() { - return $"{this.A}, {this.B}"; + return $"{nameof(Vector4Pair)}({this.A}, {this.B})"; } } } \ No newline at end of file diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 354701747a..c0064d1877 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -3,15 +3,12 @@ using System; using System.Collections.Generic; -using System.Threading.Tasks; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; -#if !NETSTANDARD1_1 using SixLabors.ImageSharp.IO; -#endif using SixLabors.ImageSharp.Processing; using SixLabors.Memory; @@ -59,6 +56,7 @@ namespace SixLabors.ImageSharp /// /// Gets or sets the maximum number of concurrent tasks enabled in ImageSharp algorithms /// configured with this instance. + /// Initialized with by default. /// public int MaxDegreeOfParallelism { @@ -99,12 +97,10 @@ namespace SixLabors.ImageSharp /// internal int MaxHeaderSize => this.ImageFormatsManager.MaxHeaderSize; -#if !NETSTANDARD1_1 /// /// Gets or sets the filesystem helper for accessing the local file system. /// internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); -#endif /// /// Gets or sets the image operations provider factory. @@ -134,10 +130,7 @@ namespace SixLabors.ImageSharp MemoryAllocator = this.MemoryAllocator, ImageOperationsProvider = this.ImageOperationsProvider, ReadOrigin = this.ReadOrigin, - -#if !NETSTANDARD1_1 FileSystem = this.FileSystem -#endif }; } diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 71852acddd..ef3ca24ee8 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -207,7 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Looks up color values and builds the image from de-compressed RLE8 data. - /// Compresssed RLE8 stream is uncompressed by + /// Compressed RLE8 stream is uncompressed by /// /// The pixel format. /// The to assign the palette to. @@ -219,8 +219,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp where TPixel : struct, IPixel { TPixel color = default; - var rgba = new Rgba32(0, 0, 0, 255); - using (Buffer2D buffer = this.memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean)) { this.UncompressRle8(width, buffer.GetSpan()); @@ -233,8 +231,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp for (int x = 0; x < width; x++) { - rgba.Bgr = Unsafe.As(ref colors[bufferRow[x] * 4]); - color.PackFromRgba32(rgba); + color.FromBgr24(Unsafe.As(ref colors[bufferRow[x] * 4])); pixelRow[x] = color; } } @@ -313,9 +310,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp } else { - for (int i = 0; i < cmd[0]; i++) + int max = count + cmd[0]; // as we start at the current count in the following loop, max is count + cmd[0] + byte cmd1 = cmd[1]; // store the value to avoid the repeated indexer access inside the loop + + for (; count < max; count++) { - buffer[count++] = cmd[1]; + buffer[count] = cmd1; } } } @@ -352,8 +352,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp using (IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(arrayWidth + padding, AllocationOptions.Clean)) { TPixel color = default; - var rgba = new Rgba32(0, 0, 0, 255); - Span rowSpan = row.GetSpan(); for (int y = 0; y < height; y++) @@ -363,7 +361,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp int offset = 0; Span pixelRow = pixels.GetRowSpan(newY); - // TODO: Could use PixelOperations here! for (int x = 0; x < arrayWidth; x++) { int colOffset = x * ppb; @@ -371,9 +368,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { int colorIndex = ((rowSpan[offset] >> (8 - bits - (shift * bits))) & mask) * 4; - // Stored in b-> g-> r order. - rgba.Bgr = Unsafe.As(ref colors[colorIndex]); - color.PackFromRgba32(rgba); + color.FromBgr24(Unsafe.As(ref colors[colorIndex])); pixelRow[newX] = color; } @@ -397,7 +392,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp int padding = CalculatePadding(width, 2); int stride = (width * 2) + padding; TPixel color = default; - var rgba = new Rgba32(0, 0, 0, 255); using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride)) { @@ -412,11 +406,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp { short temp = BitConverter.ToInt16(buffer.Array, offset); - rgba.R = GetBytesFrom5BitValue((temp & Rgb16RMask) >> 10); - rgba.G = GetBytesFrom5BitValue((temp & Rgb16GMask) >> 5); - rgba.B = GetBytesFrom5BitValue(temp & Rgb16BMask); + var rgb = new Rgb24( + GetBytesFrom5BitValue((temp & Rgb16RMask) >> 10), + GetBytesFrom5BitValue((temp & Rgb16GMask) >> 5), + GetBytesFrom5BitValue(temp & Rgb16BMask)); - color.PackFromRgba32(rgba); + color.FromRgb24(rgb); pixelRow[x] = color; offset += 2; } @@ -444,7 +439,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.stream.Read(row); int newY = Invert(y, height, inverted); Span pixelSpan = pixels.GetRowSpan(newY); - PixelOperations.Instance.PackFromBgr24Bytes(row.GetSpan(), pixelSpan, width); + PixelOperations.Instance.FromBgr24Bytes( + this.configuration, + row.GetSpan(), + pixelSpan, + width); } } } @@ -469,7 +468,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.stream.Read(row); int newY = Invert(y, height, inverted); Span pixelSpan = pixels.GetRowSpan(newY); - PixelOperations.Instance.PackFromBgra32Bytes(row.GetSpan(), pixelSpan, width); + PixelOperations.Instance.FromBgra32Bytes( + this.configuration, + row.GetSpan(), + pixelSpan, + width); } } } @@ -537,7 +540,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.metaData = meta; short bitsPerPixel = this.infoHeader.BitsPerPixel; - var bmpMetaData = this.metaData.GetFormatMetaData(BmpFormat.Instance); + BmpMetaData bmpMetaData = this.metaData.GetFormatMetaData(BmpFormat.Instance); // We can only encode at these bit rates so far. if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 44e42528cf..a67c581eb0 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -3,6 +3,8 @@ using System; using System.IO; + +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.MetaData; @@ -23,6 +25,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp private readonly MemoryAllocator memoryAllocator; + private Configuration configuration; + private BmpBitsPerPixel? bitsPerPixel; /// @@ -48,6 +52,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); + this.configuration = image.GetConfiguration(); ImageMetaData metaData = image.MetaData; BmpMetaData bmpMetaData = metaData.GetFormatMetaData(BmpFormat.Instance); this.bitsPerPixel = this.bitsPerPixel ?? bmpMetaData.BitsPerPixel; @@ -101,9 +106,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp var fileHeader = new BmpFileHeader( type: 19778, // BM - offset: 54, + fileSize: 54 + infoHeader.ImageSize, reserved: 0, - fileSize: 54 + infoHeader.ImageSize); + offset: 54); #if NETCOREAPP2_1 Span buffer = stackalloc byte[40]; @@ -163,7 +168,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp for (int y = pixels.Height - 1; y >= 0; y--) { Span pixelSpan = pixels.GetRowSpan(y); - PixelOperations.Instance.ToBgra32Bytes(pixelSpan, row.GetSpan(), pixelSpan.Length); + PixelOperations.Instance.ToBgra32Bytes( + this.configuration, + pixelSpan, + row.GetSpan(), + pixelSpan.Length); stream.Write(row.Array, 0, row.Length()); } } @@ -183,7 +192,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp for (int y = pixels.Height - 1; y >= 0; y--) { Span pixelSpan = pixels.GetRowSpan(y); - PixelOperations.Instance.ToBgr24Bytes(pixelSpan, row.GetSpan(), pixelSpan.Length); + PixelOperations.Instance.ToBgr24Bytes( + this.configuration, + pixelSpan, + row.GetSpan(), + pixelSpan.Length); stream.Write(row.Array, 0, row.Length()); } } diff --git a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs index c44ca73f2e..219d37ca62 100644 --- a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs @@ -8,6 +8,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// internal interface IBmpDecoderOptions { - // added this for consistancy so we can add stuff as required, no options currently availible + // added this for consistency so we can add stuff as required, no options currently available } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/GifConstants.cs b/src/ImageSharp/Formats/Gif/GifConstants.cs index 8167d0d2e0..288c3dfa19 100644 --- a/src/ImageSharp/Formats/Gif/GifConstants.cs +++ b/src/ImageSharp/Formats/Gif/GifConstants.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// The ASCII encoded bytes used to identify the GIF file. /// - internal static readonly byte[] MagicNumber = Encoding.UTF8.GetBytes(FileType + FileVersion); + internal static readonly byte[] MagicNumber = Encoding.ASCII.GetBytes(FileType + FileVersion); /// /// The extension block introducer !. @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// The ASCII encoded application identification bytes. /// - internal static readonly byte[] NetscapeApplicationIdentificationBytes = Encoding.UTF8.GetBytes(NetscapeApplicationIdentification); + internal static readonly byte[] NetscapeApplicationIdentificationBytes = Encoding.ASCII.GetBytes(NetscapeApplicationIdentification); /// /// The Netscape looping application sub block size. @@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// /// Gets the default encoding to use when reading comments. /// - public static readonly Encoding DefaultEncoding = Encoding.GetEncoding("ASCII"); + public static readonly Encoding DefaultEncoding = Encoding.ASCII; /// /// The list of mimetypes that equate to a gif. diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 207f126f9e..bfa91416aa 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Formats.Gif private GifGraphicControlExtension graphicsControlExtension; /// - /// The image desciptor. + /// The image descriptor. /// private GifImageDescriptor imageDescriptor; @@ -142,8 +142,7 @@ namespace SixLabors.ImageSharp.Formats.Gif this.ReadApplicationExtension(); break; case GifConstants.PlainTextLabel: - int plainLength = stream.ReadByte(); - this.Skip(plainLength); // Not supported by any known decoder. + this.SkipBlock(); // Not supported by any known decoder. break; } } @@ -190,9 +189,7 @@ namespace SixLabors.ImageSharp.Formats.Gif switch (stream.ReadByte()) { case GifConstants.GraphicControlLabel: - - // Skip graphic control extension block - this.Skip(0); + this.SkipBlock(); // Skip graphic control extension block break; case GifConstants.CommentLabel: this.ReadComments(); @@ -201,8 +198,7 @@ namespace SixLabors.ImageSharp.Formats.Gif this.ReadApplicationExtension(); break; case GifConstants.PlainTextLabel: - int plainLength = stream.ReadByte(); - this.Skip(plainLength); // Not supported by any known decoder. + this.SkipBlock(); // Not supported by any known decoder. break; } } @@ -288,24 +284,27 @@ namespace SixLabors.ImageSharp.Formats.Gif // Could be XMP or something else not supported yet. // Back up and skip. this.stream.Position -= appLength + 1; - this.Skip(appLength); + this.SkipBlock(appLength); return; } - this.Skip(appLength); // Not supported by any known decoder. + this.SkipBlock(appLength); // Not supported by any known decoder. } /// - /// Skips the designated number of bytes in the stream. + /// Skips over a block or reads its terminator. + /// The length of the block to skip. /// - /// The number of bytes to skip. - private void Skip(int length) + private void SkipBlock(int blockSize = 0) { - this.stream.Skip(length); + if (blockSize > 0) + { + this.stream.Skip(blockSize); + } int flag; - while ((flag = this.stream.ReadByte()) != 0) + while ((flag = this.stream.ReadByte()) > 0) { this.stream.Skip(flag); } @@ -370,7 +369,7 @@ namespace SixLabors.ImageSharp.Formats.Gif this.ReadFrameColors(ref image, ref previousFrame, indices.GetSpan(), colorTable, this.imageDescriptor); // Skip any remaining blocks - this.Skip(0); + this.SkipBlock(); } finally { @@ -481,22 +480,36 @@ namespace SixLabors.ImageSharp.Formats.Gif } ref TPixel rowRef = ref MemoryMarshal.GetReference(imageFrame.GetPixelRowSpan(writeY)); - var rgba = new Rgba32(0, 0, 0, 255); + bool transFlag = this.graphicsControlExtension.TransparencyFlag; - // #403 The left + width value can be larger than the image width - for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < imageWidth; x++) + if (!transFlag) { - int index = Unsafe.Add(ref indicesRef, i); - - if (!this.graphicsControlExtension.TransparencyFlag - || this.graphicsControlExtension.TransparencyIndex != index) + // #403 The left + width value can be larger than the image width + for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < imageWidth; x++) { + int index = Unsafe.Add(ref indicesRef, i); ref TPixel pixel = ref Unsafe.Add(ref rowRef, x); - rgba.Rgb = colorTable[index]; - pixel.PackFromRgba32(rgba); + Rgb24 rgb = colorTable[index]; + pixel.FromRgb24(rgb); + + i++; } + } + else + { + byte transIndex = this.graphicsControlExtension.TransparencyIndex; + for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < imageWidth; x++) + { + int index = Unsafe.Add(ref indicesRef, i); + if (transIndex != index) + { + ref TPixel pixel = ref Unsafe.Add(ref rowRef, x); + Rgb24 rgb = colorTable[index]; + pixel.FromRgb24(rgb); + } - i++; + i++; + } } } diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index ae0366e6e6..a922f30117 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -2,11 +2,13 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; @@ -21,10 +23,15 @@ namespace SixLabors.ImageSharp.Formats.Gif internal sealed class GifEncoderCore { /// - /// Used for allocating memory during procesing operations. + /// Used for allocating memory during processing operations. /// private readonly MemoryAllocator memoryAllocator; + /// + /// Configuration bound to the encoding operation. + /// + private Configuration configuration; + /// /// A reusable buffer used to reduce allocations. /// @@ -80,6 +87,8 @@ namespace SixLabors.ImageSharp.Formats.Gif Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); + this.configuration = image.GetConfiguration(); + ImageMetaData metaData = image.MetaData; this.gifMetaData = metaData.GetFormatMetaData(GifFormat.Instance); this.colorTableMode = this.colorTableMode ?? this.gifMetaData.ColorTableMode; @@ -87,7 +96,7 @@ namespace SixLabors.ImageSharp.Formats.Gif // Quantize the image returning a palette. QuantizedFrame quantized = - this.quantizer.CreateFrameQuantizer().QuantizeFrame(image.Frames.RootFrame); + this.quantizer.CreateFrameQuantizer(image.GetConfiguration()).QuantizeFrame(image.Frames.RootFrame); // Get the number of bits. this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8); @@ -133,7 +142,7 @@ namespace SixLabors.ImageSharp.Formats.Gif private void EncodeGlobal(Image image, QuantizedFrame quantized, int transparencyIndex, Stream stream) where TPixel : struct, IPixel { - var palleteQuantizer = new PaletteQuantizer(this.quantizer.Diffuser); + var palleteQuantizer = new PaletteQuantizer(quantized.Palette, this.quantizer.Diffuser); for (int i = 0; i < image.Frames.Count; i++) { @@ -149,8 +158,7 @@ namespace SixLabors.ImageSharp.Formats.Gif } else { - using (QuantizedFrame paletteQuantized - = palleteQuantizer.CreateFrameQuantizer(() => quantized.Palette).QuantizeFrame(frame)) + using (QuantizedFrame paletteQuantized = palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration()).QuantizeFrame(frame)) { this.WriteImageData(paletteQuantized, stream); } @@ -170,15 +178,17 @@ namespace SixLabors.ImageSharp.Formats.Gif if (quantized is null) { // Allow each frame to be encoded at whatever color depth the frame designates if set. - if (previousFrame != null - && previousMeta.ColorTableLength != frameMetaData.ColorTableLength - && frameMetaData.ColorTableLength > 0) + if (previousFrame != null && previousMeta.ColorTableLength != frameMetaData.ColorTableLength + && frameMetaData.ColorTableLength > 0) { - quantized = this.quantizer.CreateFrameQuantizer(frameMetaData.ColorTableLength).QuantizeFrame(frame); + quantized = this.quantizer.CreateFrameQuantizer( + image.GetConfiguration(), + frameMetaData.ColorTableLength).QuantizeFrame(frame); } else { - quantized = this.quantizer.CreateFrameQuantizer().QuantizeFrame(frame); + quantized = this.quantizer.CreateFrameQuantizer(image.GetConfiguration()) + .QuantizeFrame(frame); } } @@ -210,16 +220,20 @@ namespace SixLabors.ImageSharp.Formats.Gif { // Transparent pixels are much more likely to be found at the end of a palette int index = -1; - Rgba32 trans = default; + int length = quantized.Palette.Length; - ref TPixel paletteRef = ref MemoryMarshal.GetReference(quantized.Palette.AsSpan()); - for (int i = quantized.Palette.Length - 1; i >= 0; i--) + using (IMemoryOwner rgbaBuffer = this.memoryAllocator.Allocate(length)) { - ref TPixel entry = ref Unsafe.Add(ref paletteRef, i); - entry.ToRgba32(ref trans); - if (trans.Equals(default)) + Span rgbaSpan = rgbaBuffer.GetSpan(); + ref Rgba32 paletteRef = ref MemoryMarshal.GetReference(rgbaSpan); + PixelOperations.Instance.ToRgba32(this.configuration, quantized.Palette, rgbaSpan); + + for (int i = quantized.Palette.Length - 1; i >= 0; i--) { - index = i; + if (Unsafe.Add(ref paletteRef, i).Equals(default)) + { + index = i; + } } } @@ -314,7 +328,8 @@ namespace SixLabors.ImageSharp.Formats.Gif /// The stream to write to. private void WriteComments(ImageMetaData metadata, Stream stream) { - if (!metadata.TryGetProperty(GifConstants.Comments, out ImageProperty property) || string.IsNullOrEmpty(property.Value)) + if (!metadata.TryGetProperty(GifConstants.Comments, out ImageProperty property) + || string.IsNullOrEmpty(property.Value)) { return; } @@ -406,24 +421,17 @@ namespace SixLabors.ImageSharp.Formats.Gif private void WriteColorTable(QuantizedFrame image, Stream stream) where TPixel : struct, IPixel { - int pixelCount = image.Palette.Length; - - // The maximium number of colors for the bit depth + // The maximum number of colors for the bit depth int colorTableLength = ImageMaths.GetColorCountForBitDepth(this.bitDepth) * 3; - Rgb24 rgb = default; + int pixelCount = image.Palette.Length; using (IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength)) { - ref TPixel paletteRef = ref MemoryMarshal.GetReference(image.Palette.AsSpan()); - ref Rgb24 rgb24Ref = ref Unsafe.As(ref MemoryMarshal.GetReference(colorTable.GetSpan())); - for (int i = 0; i < pixelCount; i++) - { - ref TPixel entry = ref Unsafe.Add(ref paletteRef, i); - entry.ToRgb24(ref rgb); - Unsafe.Add(ref rgb24Ref, i) = rgb; - } - - // Write the palette to the stream + PixelOperations.Instance.ToRgb24Bytes( + this.configuration, + image.Palette.AsSpan(), + colorTable.GetSpan(), + pixelCount); stream.Write(colorTable.Array, 0, colorTableLength); } } diff --git a/src/ImageSharp/Formats/Gif/LzwEncoder.cs b/src/ImageSharp/Formats/Gif/LzwEncoder.cs index e390dfd54c..2d32fd23aa 100644 --- a/src/ImageSharp/Formats/Gif/LzwEncoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwEncoder.cs @@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Formats.Gif }; /// - /// The maximium number of bits/code. + /// The maximum number of bits/code. /// private const int MaxBits = 12; @@ -210,7 +210,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// flush the packet to disk. /// /// The character to add. - /// The reference to the storage for packat accumulators + /// The reference to the storage for packet accumulators /// The stream to write to. [MethodImpl(MethodImplOptions.AggressiveInlining)] private void AddCharacter(byte c, ref byte accumulatorsRef, Stream stream) @@ -309,10 +309,10 @@ namespace SixLabors.ImageSharp.Formats.Gif // Non-empty slot if (Unsafe.Add(ref hashTableRef, i) >= 0) { - int disp = hsizeReg - i; - if (i == 0) + int disp = 1; + if (i != 0) { - disp = 1; + disp = hsizeReg - i; } do diff --git a/src/ImageSharp/Formats/IImageFormat.cs b/src/ImageSharp/Formats/IImageFormat.cs index 94191c1493..3cd289df76 100644 --- a/src/ImageSharp/Formats/IImageFormat.cs +++ b/src/ImageSharp/Formats/IImageFormat.cs @@ -16,12 +16,12 @@ namespace SixLabors.ImageSharp.Formats string Name { get; } /// - /// Gets the default mimetype that the image foramt uses + /// Gets the default mimetype that the image format uses /// string DefaultMimeType { get; } /// - /// Gets all the mimetypes that have been used by this image foramt. + /// Gets all the mimetypes that have been used by this image format. /// IEnumerable MimeTypes { get; } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs index 5601a94366..60fec25d29 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs @@ -10,7 +10,7 @@ using System.Text; namespace SixLabors.ImageSharp.Formats.Jpeg.Components { /// - /// Represents a Jpeg block with coefficiens. + /// Represents a Jpeg block with coefficients. /// // ReSharper disable once InconsistentNaming internal unsafe struct Block8x8 : IEquatable @@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } /// - /// Gets or sets a value in a row+coulumn of the 8x8 block + /// Gets or sets a value in a row+column of the 8x8 block /// /// The x position index in the row /// The column index @@ -283,7 +283,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } /// - /// Calculate the total sum of absoulute differences of elements in 'a' and 'b'. + /// Calculate the total sum of absolute differences of elements in 'a' and 'b'. /// public static long TotalDifference(ref Block8x8 a, ref Block8x8 b) { diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs index b7dd125a88..6bf9c8483a 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs @@ -3,64 +3,41 @@ using System.Numerics; using System.Runtime.CompilerServices; - using SixLabors.ImageSharp.Memory; +// ReSharper disable UseObjectOrCollectionInitializer // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal partial struct Block8x8F { /// - /// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical. + /// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical scale factors. /// + [MethodImpl(InliningOptions.ShortMethod)] public void CopyTo(in BufferArea area, int horizontalScale, int verticalScale) { if (horizontalScale == 1 && verticalScale == 1) { - this.CopyTo(area); + this.Copy1x1Scale(area); return; } - else if (horizontalScale == 2 && verticalScale == 2) + + if (horizontalScale == 2 && verticalScale == 2) { - this.CopyTo2x2(area); + this.Copy2x2Scale(area); return; } - ref float destBase = ref area.GetReferenceToOrigin(); - - // TODO: Optimize: implement all the cases with loopless special code! (T4?) - for (int y = 0; y < 8; y++) - { - int yy = y * verticalScale; - int y8 = y * 8; - - for (int x = 0; x < 8; x++) - { - int xx = x * horizontalScale; - - float value = this[y8 + x]; - - for (int i = 0; i < verticalScale; i++) - { - int baseIdx = ((yy + i) * area.Stride) + xx; - - for (int j = 0; j < horizontalScale; j++) - { - // area[xx + j, yy + i] = value; - Unsafe.Add(ref destBase, baseIdx + j) = value; - } - } - } - } + // TODO: Optimize: implement all cases with scale-specific, loopless code! + this.CopyArbitraryScale(area, horizontalScale, verticalScale); } - // [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyTo(in BufferArea area) + public void Copy1x1Scale(in BufferArea destination) { ref byte selfBase = ref Unsafe.As(ref this); - ref byte destBase = ref Unsafe.As(ref area.GetReferenceToOrigin()); - int destStride = area.Stride * sizeof(float); + ref byte destBase = ref Unsafe.As(ref destination.GetReferenceToOrigin()); + int destStride = destination.Stride * sizeof(float); CopyRowImpl(ref selfBase, ref destBase, destStride, 0); CopyRowImpl(ref selfBase, ref destBase, destStride, 1); @@ -80,76 +57,86 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float)); } - private void CopyTo2x2(in BufferArea area) + private void Copy2x2Scale(in BufferArea area) { - ref float destBase = ref area.GetReferenceToOrigin(); - int destStride = area.Stride; - - this.WidenCopyImpl2x2(ref destBase, 0, destStride); - this.WidenCopyImpl2x2(ref destBase, 1, destStride); - this.WidenCopyImpl2x2(ref destBase, 2, destStride); - this.WidenCopyImpl2x2(ref destBase, 3, destStride); - this.WidenCopyImpl2x2(ref destBase, 4, destStride); - this.WidenCopyImpl2x2(ref destBase, 5, destStride); - this.WidenCopyImpl2x2(ref destBase, 6, destStride); - this.WidenCopyImpl2x2(ref destBase, 7, destStride); + ref Vector2 destBase = ref Unsafe.As(ref area.GetReferenceToOrigin()); + int destStride = area.Stride / 2; + + this.WidenCopyRowImpl2x2(ref destBase, 0, destStride); + this.WidenCopyRowImpl2x2(ref destBase, 1, destStride); + this.WidenCopyRowImpl2x2(ref destBase, 2, destStride); + this.WidenCopyRowImpl2x2(ref destBase, 3, destStride); + this.WidenCopyRowImpl2x2(ref destBase, 4, destStride); + this.WidenCopyRowImpl2x2(ref destBase, 5, destStride); + this.WidenCopyRowImpl2x2(ref destBase, 6, destStride); + this.WidenCopyRowImpl2x2(ref destBase, 7, destStride); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WidenCopyImpl2x2(ref float destBase, int row, int destStride) + private void WidenCopyRowImpl2x2(ref Vector2 destBase, int row, int destStride) { - ref Vector4 selfLeft = ref Unsafe.Add(ref this.V0L, 2 * row); - ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); - ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride); - - Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X; - Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X; - Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y; - Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y; - Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z; - Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z; - Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W; - Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W; - - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W; - - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W; - - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W; - Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W; + ref Vector4 sLeft = ref Unsafe.Add(ref this.V0L, 2 * row); + ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); + + int offset = 2 * row * destStride; + ref Vector4 dTopLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset)); + ref Vector4 dBottomLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset + destStride)); + + var xyLeft = new Vector4(sLeft.X); + xyLeft.Z = sLeft.Y; + xyLeft.W = sLeft.Y; + + var zwLeft = new Vector4(sLeft.Z); + zwLeft.Z = sLeft.W; + zwLeft.W = sLeft.W; + + var xyRight = new Vector4(sRight.X); + xyRight.Z = sRight.Y; + xyRight.W = sRight.Y; + + var zwRight = new Vector4(sRight.Z); + zwRight.Z = sRight.W; + zwRight.W = sRight.W; + + dTopLeft = xyLeft; + Unsafe.Add(ref dTopLeft, 1) = zwLeft; + Unsafe.Add(ref dTopLeft, 2) = xyRight; + Unsafe.Add(ref dTopLeft, 3) = zwRight; + + dBottomLeft = xyLeft; + Unsafe.Add(ref dBottomLeft, 1) = zwLeft; + Unsafe.Add(ref dBottomLeft, 2) = xyRight; + Unsafe.Add(ref dBottomLeft, 3) = zwRight; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void WidenCopyImpl(ref Vector4 s, ref float destBase) + [MethodImpl(InliningOptions.ColdPath)] + private void CopyArbitraryScale(BufferArea area, int horizontalScale, int verticalScale) { - Unsafe.Add(ref destBase, 0) = s.X; - Unsafe.Add(ref destBase, 1) = s.X; - Unsafe.Add(ref destBase, 2) = s.Y; - Unsafe.Add(ref destBase, 3) = s.Y; - Unsafe.Add(ref destBase, 4) = s.Z; - Unsafe.Add(ref destBase, 5) = s.Z; - Unsafe.Add(ref destBase, 6) = s.W; - Unsafe.Add(ref destBase, 7) = s.W; + ref float destBase = ref area.GetReferenceToOrigin(); + + for (int y = 0; y < 8; y++) + { + int yy = y * verticalScale; + int y8 = y * 8; + + for (int x = 0; x < 8; x++) + { + int xx = x * horizontalScale; + + float value = this[y8 + x]; + + for (int i = 0; i < verticalScale; i++) + { + int baseIdx = ((yy + i) * area.Stride) + xx; + + for (int j = 0; j < horizontalScale; j++) + { + // area[xx + j, yy + i] = value; + Unsafe.Add(ref destBase, baseIdx + j) = value; + } + } + } + } } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs index e83896f587..09ed6408d7 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// Transpose the block into the destination block. /// /// The destination block - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public void TransposeInto(ref Block8x8F d) { d.V0L.X = V0L.X; @@ -119,7 +119,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// AVX2-only variant for executing and in one step. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public void NormalizeColorsAndRoundInplaceAvx2() { Vector off = new Vector(128f); @@ -154,7 +154,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// Fill the block from 'source' doing short -> float conversion. /// - public void LoadFrom(ref Block8x8 source) + public void LoadFromInt16Scalar(ref Block8x8 source) { ref short selfRef = ref Unsafe.As(ref source); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt index 82d82ef0c2..f93ee6522d 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.tt @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// Transpose the block into the destination block. /// /// The destination block - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public void TransposeInto(ref Block8x8F d) { <# @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// AVX2-only variant for executing and in one step. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public void NormalizeColorsAndRoundInplaceAvx2() { Vector off = new Vector(128f); @@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// Fill the block from 'source' doing short -> float conversion. /// - public void LoadFrom(ref Block8x8 source) + public void LoadFromInt16Scalar(ref Block8x8 source) { ref short selfRef = ref Unsafe.As(ref source); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index 59fc234c42..81393342d6 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// The float value at the specified index public float this[int idx] { - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] get { GuardBlockIndex(idx); @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components return Unsafe.Add(ref selfRef, idx); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] set { GuardBlockIndex(idx); @@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// Fill the block with defaults (zeroes) /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public void Clear() { // The cheapest way to do this in C#: @@ -160,7 +160,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// Load raw 32bit floating point data from source /// /// Source - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public void LoadFrom(Span source) { ref byte s = ref Unsafe.As(ref MemoryMarshal.GetReference(source)); @@ -174,7 +174,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// Block pointer /// Source - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static unsafe void LoadFrom(Block8x8F* blockPtr, Span source) { blockPtr->LoadFrom(source); @@ -200,7 +200,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// Copy raw 32bit floating point data to dest /// /// Destination - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public void CopyTo(Span dest) { ref byte d = ref Unsafe.As(ref MemoryMarshal.GetReference(dest)); @@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// Pointer to block /// Destination - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static unsafe void CopyTo(Block8x8F* blockPtr, Span dest) { float* fPtr = (float*)blockPtr; @@ -230,7 +230,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// Block pointer /// Destination - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public static unsafe void CopyTo(Block8x8F* blockPtr, Span dest) { blockPtr->CopyTo(dest); @@ -240,7 +240,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// Copy raw 32bit floating point data to dest /// /// Destination - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public unsafe void CopyTo(float[] dest) { fixed (void* ptr = &this.V0L) @@ -276,7 +276,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// Multiply all elements of the block. /// /// The value to multiply by - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public void MultiplyInplace(float value) { this.V0L *= value; @@ -300,7 +300,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// Multiply all elements of the block by the corresponding elements of 'other' /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public void MultiplyInplace(ref Block8x8F other) { this.V0L *= other.V0L; @@ -325,7 +325,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// Adds a vector to all elements of the block. /// /// The added vector - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public void AddToAllInplace(Vector4 diff) { this.V0L += diff; @@ -395,7 +395,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } /// - /// Scales the 16x16 region represented by the 4 source blocks to the 8x8 DST block. + /// Scales the 16x16 region represented by the 4 source blocks to the 8x8 DST block. /// /// The destination block. /// The source block. @@ -420,7 +420,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private static void DivideRoundAll(ref Block8x8F a, ref Block8x8F b) { a.V0L = DivideRound(a.V0L, b.V0L); @@ -493,25 +493,69 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } } + [MethodImpl(InliningOptions.ShortMethod)] + public void LoadFrom(ref Block8x8 source) + { +#if SUPPORTS_EXTENDED_INTRINSICS + if (SimdUtils.IsAvx2CompatibleArchitecture) + { + this.LoadFromInt16ExtendedAvx2(ref source); + return; + } +#endif + this.LoadFromInt16Scalar(ref source); + } + + /// + /// Loads values from using extended AVX2 intrinsics. + /// + /// The source + public void LoadFromInt16ExtendedAvx2(ref Block8x8 source) + { + DebugGuard.IsTrue( + SimdUtils.IsAvx2CompatibleArchitecture, + "LoadFromUInt16ExtendedAvx2 only works on AVX2 compatible architecture!"); + + ref Vector sRef = ref Unsafe.As>(ref source); + ref Vector dRef = ref Unsafe.As>(ref this); + + // Vector.Count == 16 on AVX2 + // We can process 2 block rows in a single step + SimdUtils.ExtendedIntrinsics.ConvertToSingle(sRef, out Vector top, out Vector bottom); + dRef = top; + Unsafe.Add(ref dRef, 1) = bottom; + + SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 1), out top, out bottom); + Unsafe.Add(ref dRef, 2) = top; + Unsafe.Add(ref dRef, 3) = bottom; + + SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 2), out top, out bottom); + Unsafe.Add(ref dRef, 4) = top; + Unsafe.Add(ref dRef, 5) = bottom; + + SimdUtils.ExtendedIntrinsics.ConvertToSingle(Unsafe.Add(ref sRef, 3), out top, out bottom); + Unsafe.Add(ref dRef, 6) = top; + Unsafe.Add(ref dRef, 7) = bottom; + } + /// public override string ToString() { var sb = new StringBuilder(); sb.Append('['); - for (int i = 0; i < Size; i++) + for (int i = 0; i < Size - 1; i++) { sb.Append(this[i]); - if (i < Size - 1) - { - sb.Append(','); - } + sb.Append(','); } + sb.Append(this[Size - 1]); + sb.Append(']'); return sb.ToString(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private static Vector NormalizeAndRound(Vector row, Vector off, Vector max) { row += off; @@ -520,13 +564,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components return row.FastRound(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor) { // sign(dividend) = max(min(dividend, 1), -1) var sign = Vector4.Clamp(dividend, NegativeOne, Vector4.One); - // AlmostRound(dividend/divisor) = dividend/divisior + 0.5*sign(dividend) + // AlmostRound(dividend/divisor) = dividend/divisor + 0.5*sign(dividend) return (dividend / divisor) + (sign * Offset); } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs index 5d7a31a12b..7424145c3b 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs @@ -3,6 +3,8 @@ using System; using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { @@ -17,24 +19,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters public override void ConvertToRgba(in ComponentValues values, Span result) { - // TODO: We can optimize a lot here with Vector and SRCS.Unsafe()! - ReadOnlySpan yVals = values.Component0; - - var v = new Vector4(0, 0, 0, 1); - var scale = new Vector4(1 / 255F, 1 / 255F, 1 / 255F, 1F); + ref float sBase = ref MemoryMarshal.GetReference(values.Component0); + ref Vector4 dBase = ref MemoryMarshal.GetReference(result); + for (int i = 0; i < result.Length; i++) { - float y = yVals[i]; - - v.X = y; - v.Y = y; - v.Z = y; - + var v = new Vector4(Unsafe.Add(ref sBase, i)); + v.W = 1f; v *= scale; - - result[i] = v; + Unsafe.Add(ref dBase, i) = v; } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs index 4b2626c582..23aa1acbe6 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs @@ -6,7 +6,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Common.Tuples; +using SixLabors.ImageSharp.Tuples; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters { @@ -32,11 +32,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters } /// - /// SIMD convert using buffers of sizes divisable by 8. + /// SIMD convert using buffers of sizes divisible by 8. /// internal static void ConvertCore(in ComponentValues values, Span result) { - DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisable by 8!"); + DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisible by 8!"); ref Vector4Pair yBase = ref Unsafe.As(ref MemoryMarshal.GetReference(values.Component0)); @@ -109,7 +109,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters // Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order: ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); - destination.Collect(ref r, ref g, ref b); + destination.Pack(ref r, ref g, ref b); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs index ab4947e65c..f0a70a6f38 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs @@ -6,7 +6,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Common.Tuples; +using SixLabors.ImageSharp.Tuples; // ReSharper disable ImpureMethodCallOnReadonlyValueField namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters } /// - /// SIMD convert using buffers of sizes divisable by 8. + /// SIMD convert using buffers of sizes divisible by 8. /// internal static void ConvertCore(in ComponentValues values, Span result) { @@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters // Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order: ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); - destination.Collect(ref rr, ref gg, ref bb); + destination.Pack(ref rr, ref gg, ref bb); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs index 60abb7fb2c..a44ebf89d1 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs @@ -6,8 +6,8 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; -using SixLabors.ImageSharp.Common.Tuples; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Tuples; using SixLabors.Memory; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters internal abstract partial class JpegColorConverter { /// - /// The avalilable converters + /// The available converters /// private static readonly JpegColorConverter[] Converters = { @@ -157,9 +157,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters public Vector4 V0, V1, V2, V3, V4, V5, V6, V7; /// - /// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order. + /// Pack (r0,r1...r7) (g0,g1...g7) (b0,b1...b7) vector values as (r0,g0,b0,1), (r1,g1,b1,1) ... /// - public void Collect(ref Vector4Pair r, ref Vector4Pair g, ref Vector4Pair b) + public void Pack(ref Vector4Pair r, ref Vector4Pair g, ref Vector4Pair b) { this.V0.X = r.A.X; this.V0.Y = g.A.X; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs index 26bcde8e51..06b46746a6 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FastACTables.cs @@ -20,54 +20,47 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// Initializes a new instance of the class. /// /// The memory allocator used to allocate memory for image processing operations. - public FastACTables(MemoryAllocator memoryAllocator) - { - this.tables = memoryAllocator.Allocate2D(512, 4, AllocationOptions.Clean); - } + public FastACTables(MemoryAllocator memoryAllocator) => this.tables = memoryAllocator.Allocate2D(512, 4, AllocationOptions.Clean); /// /// Gets the representing the table at the index in the collection. /// /// The table index. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan GetTableSpan(int index) - { - return this.tables.GetRowSpan(index); - } + [MethodImpl(InliningOptions.ShortMethod)] + public ReadOnlySpan GetTableSpan(int index) => this.tables.GetRowSpan(index); /// - /// Gets a reference to the first element of the AC table indexed by /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref short GetAcTableReference(JpegComponent component) - { - return ref this.tables.GetRowSpan(component.ACHuffmanTableId)[0]; - } + /// Gets a reference to the first element of the AC table indexed by + /// + /// The frame component. + [MethodImpl(InliningOptions.ShortMethod)] + public ref short GetAcTableReference(JpegComponent component) => ref this.tables.GetRowSpan(component.ACHuffmanTableId)[0]; /// /// Builds a lookup table for fast AC entropy scan decoding. /// /// The table index. /// The collection of AC Huffman tables. - public void BuildACTableLut(int index, HuffmanTables acHuffmanTables) + public unsafe void BuildACTableLut(int index, HuffmanTables acHuffmanTables) { const int FastBits = ScanDecoder.FastBits; Span fastAC = this.tables.GetRowSpan(index); - ref HuffmanTable huffman = ref acHuffmanTables[index]; + ref HuffmanTable huffmanTable = ref acHuffmanTables[index]; int i; for (i = 0; i < (1 << FastBits); i++) { - byte fast = huffman.Lookahead[i]; + byte fast = huffmanTable.Lookahead[i]; fastAC[i] = 0; if (fast < byte.MaxValue) { - int rs = huffman.Values[fast]; + int rs = huffmanTable.Values[fast]; int run = (rs >> 4) & 15; int magbits = rs & 15; - int len = huffman.Sizes[fast]; + int len = huffmanTable.Sizes[fast]; - if (magbits > 0 && len + magbits <= FastBits) + if (magbits != 0 && len + magbits <= FastBits) { // Magnitude code followed by receive_extend code int k = ((i << len) & ((1 << FastBits) - 1)) >> (FastBits - magbits); @@ -80,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // if the result is small enough, we can fit it in fastAC table if (k >= -128 && k <= 127) { - fastAC[i] = (short)((k * 256) + (run * 16) + (len + magbits)); + fastAC[i] = (short)((k << 8) + (run << 4) + (len + magbits)); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer256.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer256.cs deleted file mode 100644 index 1d26178e0c..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer256.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder -{ - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct FixedByteBuffer256 - { - public fixed byte Data[256]; - - public byte this[int idx] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ref byte self = ref Unsafe.As(ref this); - return Unsafe.Add(ref self, idx); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer512.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer512.cs deleted file mode 100644 index 556e74fd58..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedByteBuffer512.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder -{ - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct FixedByteBuffer512 - { - public fixed byte Data[1 << ScanDecoder.FastBits]; - - public byte this[int idx] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ref byte self = ref Unsafe.As(ref this); - return Unsafe.Add(ref self, idx); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt16Buffer257.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt16Buffer257.cs deleted file mode 100644 index a3b67a700b..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt16Buffer257.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder -{ - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct FixedInt16Buffer257 - { - public fixed short Data[257]; - - public short this[int idx] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ref short self = ref Unsafe.As(ref this); - return Unsafe.Add(ref self, idx); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt32Buffer18.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt32Buffer18.cs deleted file mode 100644 index bba89f072f..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedInt32Buffer18.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder -{ - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct FixedInt32Buffer18 - { - public fixed int Data[18]; - - public int this[int idx] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ref int self = ref Unsafe.As(ref this); - return Unsafe.Add(ref self, idx); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedUInt32Buffer18.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedUInt32Buffer18.cs deleted file mode 100644 index 1d3ca99338..0000000000 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/FixedUInt32Buffer18.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder -{ - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct FixedUInt32Buffer18 - { - public fixed uint Data[18]; - - public uint this[int idx] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - ref uint self = ref Unsafe.As(ref this); - return Unsafe.Add(ref self, idx); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs index 0138164ed2..9e134746bc 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanTable.cs @@ -20,114 +20,105 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// /// Gets the max code array /// - public FixedUInt32Buffer18 MaxCode; + public fixed uint MaxCode[18]; /// /// Gets the value offset array /// - public FixedInt32Buffer18 ValOffset; + public fixed int ValOffset[18]; /// /// Gets the huffman value array /// - public FixedByteBuffer256 Values; + public fixed byte Values[256]; /// /// Gets the lookahead array /// - public FixedByteBuffer512 Lookahead; + public fixed byte Lookahead[512]; /// /// Gets the sizes array /// - public FixedInt16Buffer257 Sizes; + public fixed short Sizes[257]; /// /// Initializes a new instance of the struct. /// /// The to use for buffer allocations. - /// The code lengths + /// The code lengths /// The huffman values - public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan count, ReadOnlySpan values) + public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan codeLengths, ReadOnlySpan values) { const int Length = 257; using (IMemoryOwner huffcode = memoryAllocator.Allocate(Length)) { ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.GetSpan()); + ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths); // Figure C.1: make table of Huffman code length for each symbol - fixed (short* sizesRef = this.Sizes.Data) + ref short sizesRef = ref this.Sizes[0]; + short x = 0; + + for (short i = 1; i < 17; i++) { - short x = 0; - for (short i = 1; i < 17; i++) + byte length = Unsafe.Add(ref codeLengthsRef, i); + for (short j = 0; j < length; j++) { - byte l = count[i]; - for (short j = 0; j < l; j++) - { - sizesRef[x] = i; - x++; - } + Unsafe.Add(ref sizesRef, x++) = i; } + } - sizesRef[x] = 0; + Unsafe.Add(ref sizesRef, x) = 0; - // Figure C.2: generate the codes themselves - int k = 0; - fixed (int* valOffsetRef = this.ValOffset.Data) - fixed (uint* maxcodeRef = this.MaxCode.Data) + // Figure C.2: generate the codes themselves + int si = 0; + ref int valOffsetRef = ref this.ValOffset[0]; + ref uint maxcodeRef = ref this.MaxCode[0]; + + uint code = 0; + int k; + for (k = 1; k < 17; k++) + { + // Compute delta to add to code to compute symbol id. + Unsafe.Add(ref valOffsetRef, k) = (int)(si - code); + if (Unsafe.Add(ref sizesRef, si) == k) { - uint code = 0; - int j; - for (j = 1; j < 17; j++) + while (Unsafe.Add(ref sizesRef, si) == k) { - // Compute delta to add to code to compute symbol id. - valOffsetRef[j] = (int)(k - code); - if (sizesRef[k] == j) - { - while (sizesRef[k] == j) - { - Unsafe.Add(ref huffcodeRef, k++) = (short)code++; - } - } - - // Figure F.15: generate decoding tables for bit-sequential decoding. - // Compute largest code + 1 for this size. preshifted as need later. - maxcodeRef[j] = code << (16 - j); - code <<= 1; + Unsafe.Add(ref huffcodeRef, si++) = (short)code++; } - - maxcodeRef[j] = 0xFFFFFFFF; } - // Generate non-spec lookup tables to speed up decoding. - fixed (byte* lookaheadRef = this.Lookahead.Data) - { - const int FastBits = ScanDecoder.FastBits; - var fast = new Span(lookaheadRef, 1 << FastBits); - fast.Fill(0xFF); // Flag for non-accelerated + // Figure F.15: generate decoding tables for bit-sequential decoding. + // Compute largest code + 1 for this size. preshifted as we need it later. + Unsafe.Add(ref maxcodeRef, k) = code << (16 - k); + code <<= 1; + } + + Unsafe.Add(ref maxcodeRef, k) = 0xFFFFFFFF; - for (int i = 0; i < k; i++) + // Generate non-spec lookup tables to speed up decoding. + const int FastBits = ScanDecoder.FastBits; + ref byte fastRef = ref this.Lookahead[0]; + Unsafe.InitBlockUnaligned(ref fastRef, 0xFF, 1 << FastBits); // Flag for non-accelerated + + for (int i = 0; i < si; i++) + { + int size = Unsafe.Add(ref sizesRef, i); + if (size <= FastBits) + { + int c = Unsafe.Add(ref huffcodeRef, i) << (FastBits - size); + int m = 1 << (FastBits - size); + for (int l = 0; l < m; l++) { - int s = sizesRef[i]; - if (s <= ScanDecoder.FastBits) - { - int c = Unsafe.Add(ref huffcodeRef, i) << (FastBits - s); - int m = 1 << (FastBits - s); - for (int j = 0; j < m; j++) - { - fast[c + j] = (byte)i; - } - } + Unsafe.Add(ref fastRef, c + l) = (byte)i; } } } } - fixed (byte* huffValRef = this.Values.Data) - { - var huffValSpan = new Span(huffValRef, 256); - values.CopyTo(huffValSpan); - } + Unsafe.CopyBlockUnaligned(ref this.Values[0], ref MemoryMarshal.GetReference(values), 256); } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs index c033980336..2492a985a8 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IJpegComponent.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder @@ -43,16 +42,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// /// Gets the storing the "raw" frequency-domain decoded + unzigged blocks. - /// We need to apply IDCT and dequantiazition to transform them into color-space blocks. + /// We need to apply IDCT and dequantization to transform them into color-space blocks. /// Buffer2D SpectralBlocks { get; } - - /// - /// Gets a reference to the at the given row and column index from - /// - /// The column - /// The row - /// The - ref Block8x8 GetBlockReference(int column, int row); } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs index dace78b337..1454bb5b12 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/IRawJpegData.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder Size ImageSizeInPixels { get; } /// - /// Gets the number of coponents. + /// Gets the number of components. /// int ComponentCount { get; } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs index f153ce062a..4bff492486 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// The minor version /// The units for the density values /// The horizontal pixel density - /// The veritcal pixel density + /// The vertical pixel density private JFifMarker(byte majorVersion, byte minorVersion, byte densityUnits, short xDensity, short yDensity) { Guard.MustBeGreaterThan(xDensity, 0, nameof(xDensity)); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs index 0108e30815..da4b2847b8 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// - Dequantize /// - Applying IDCT /// - Level shift by +128, clip to [0, 255] - /// - Copy the resultin color values into 'destArea' scaling up the block by amount defined in + /// - Copy the resulting color values into 'destArea' scaling up the block by amount defined in /// /// The source block. /// The destination buffer area. diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs index 65a584c4f2..ef03582d69 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs @@ -128,21 +128,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors); } - this.SpectralBlocks = this.memoryAllocator.Allocate2D(blocksPerColumnForMcu, blocksPerLineForMcu + 1, AllocationOptions.Clean); - } + int totalNumberOfBlocks = blocksPerColumnForMcu * (blocksPerLineForMcu + 1); + int width = this.WidthInBlocks + 1; + int height = totalNumberOfBlocks / width; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref Block8x8 GetBlockReference(int column, int row) - { - int offset = ((this.WidthInBlocks + 1) * row) + column; - return ref Unsafe.Add(ref MemoryMarshal.GetReference(this.SpectralBlocks.GetSpan()), offset); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref short GetBlockDataReference(int column, int row) - { - ref Block8x8 blockRef = ref this.GetBlockReference(column, row); - return ref Unsafe.As(ref blockRef); + this.SpectralBlocks = this.memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean); } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs index 890f402595..94ec600dd5 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponentPostProcessor.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.Memory; @@ -20,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder private int currentComponentRowInBlocks; /// - /// The size of the area in corrsponding to one 8x8 Jpeg block + /// The size of the area in corresponding to one 8x8 Jpeg block /// private readonly Size blockAreaSize; @@ -88,12 +90,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int yBuffer = y * this.blockAreaSize.Height; - for (int x = 0; x < this.SizeInBlocks.Width; x++) - { - int xBlock = x; - int xBuffer = x * this.blockAreaSize.Width; + Span blockRow = this.Component.SpectralBlocks.GetRowSpan(yBlock); + + ref Block8x8 blockRowBase = ref MemoryMarshal.GetReference(blockRow); - ref Block8x8 block = ref this.Component.GetBlockReference(xBlock, yBlock); + for (int xBlock = 0; xBlock < this.SizeInBlocks.Width; xBlock++) + { + ref Block8x8 block = ref Unsafe.Add(ref blockRowBase, xBlock); + int xBuffer = xBlock * this.blockAreaSize.Width; BufferArea destArea = this.ColorBuffer.GetArea( xBuffer, diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs index da089fa44a..36a3dc2d26 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegFrame.cs @@ -45,6 +45,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// public byte[] ComponentIds { get; set; } + /// + /// Gets or sets the order in which to process the components + /// in interleaved mode. + /// + public byte[] ComponentOrder { get; set; } + /// /// Gets or sets the frame component collection /// diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs index 94382553ca..7ce86b4c9b 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegImagePostProcessor.cs @@ -26,6 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// internal class JpegImagePostProcessor : IDisposable { + private readonly Configuration configuration; + /// /// The number of block rows to be processed in one Step. /// @@ -49,15 +51,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// /// Initializes a new instance of the class. /// - /// The to use for buffer allocations. + /// The to configure internal operations. /// The representing the uncompressed spectral Jpeg data - public JpegImagePostProcessor(MemoryAllocator memoryAllocator, IRawJpegData rawJpeg) + public JpegImagePostProcessor(Configuration configuration, IRawJpegData rawJpeg) { + this.configuration = configuration; this.RawJpeg = rawJpeg; IJpegComponent c0 = rawJpeg.Components.First(); this.NumberOfPostProcessorSteps = c0.SizeInBlocks.Height / BlockRowsPerStep; this.PostProcessorBufferSize = new Size(c0.SizeInBlocks.Width * 8, PixelRowsPerStep); + MemoryAllocator memoryAllocator = configuration.MemoryAllocator; this.ComponentProcessors = rawJpeg.Components.Select(c => new JpegComponentPostProcessor(memoryAllocator, this, c)).ToArray(); this.rgbaBuffer = memoryAllocator.Allocate(rawJpeg.ImageSizeInPixels.Width); this.colorConverter = JpegColorConverter.GetConverter(rawJpeg.ColorSpace); @@ -159,7 +163,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder Span destRow = destination.GetPixelRowSpan(yy); - PixelOperations.Instance.PackFromVector4(this.rgbaBuffer.GetSpan(), destRow, destination.Width); + // TODO: Investigate if slicing is actually necessary + PixelOperations.Instance.FromVector4(this.configuration, this.rgbaBuffer.GetSpan().Slice(0, destRow.Length), destRow); } } } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs index a6d5faaea1..3e7108b151 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ProfileResolver.cs @@ -14,22 +14,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// /// Describes the EXIF specific markers /// - public static readonly byte[] JFifMarker = Encoding.UTF8.GetBytes("JFIF\0"); + public static readonly byte[] JFifMarker = Encoding.ASCII.GetBytes("JFIF\0"); /// /// Describes the EXIF specific markers /// - public static readonly byte[] IccMarker = Encoding.UTF8.GetBytes("ICC_PROFILE\0"); + public static readonly byte[] IccMarker = Encoding.ASCII.GetBytes("ICC_PROFILE\0"); /// /// Describes the ICC specific markers /// - public static readonly byte[] ExifMarker = Encoding.UTF8.GetBytes("Exif\0\0"); + public static readonly byte[] ExifMarker = Encoding.ASCII.GetBytes("Exif\0\0"); /// /// Describes Adobe specific markers /// - public static readonly byte[] AdobeMarker = Encoding.UTF8.GetBytes("Adobe"); + public static readonly byte[] AdobeMarker = Encoding.ASCII.GetBytes("Adobe"); /// /// Returns a value indicating whether the passed bytes are a match to the profile identifier diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs index 8c525335bc..48abca2a4e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/ScanDecoder.cs @@ -1,8 +1,10 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder { @@ -34,9 +36,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // The restart interval. private readonly int restartInterval; - // The current component index. - private readonly int componentIndex; - // The number of interleaved components. private readonly int componentsLength; @@ -87,7 +86,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// The DC Huffman tables. /// The AC Huffman tables. /// The fast AC decoding tables. - /// The component index within the array. /// The length of the components. Different to the array length. /// The reset interval. /// The spectral selection start. @@ -100,7 +98,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder HuffmanTables dcHuffmanTables, HuffmanTables acHuffmanTables, FastACTables fastACTables, - int componentIndex, int componentsLength, int restartInterval, int spectralStart, @@ -117,7 +114,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder this.components = frame.Components; this.marker = JpegConstants.Markers.XFF; this.markerPosition = 0; - this.componentIndex = componentIndex; this.componentsLength = componentsLength; this.restartInterval = restartInterval; this.spectralStart = spectralStart; @@ -148,7 +144,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private static uint LRot(uint x, int y) => (x << y) | (x >> (32 - y)); private void ParseBaselineData() @@ -176,7 +172,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // Scan an interleaved mcu... process components in order for (int k = 0; k < this.componentsLength; k++) { - JpegComponent component = this.components[k]; + int order = this.frame.ComponentOrder[k]; + JpegComponent component = this.components[order]; ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; ref HuffmanTable acHuffmanTable = ref this.acHuffmanTables[component.ACHuffmanTableId]; @@ -184,10 +181,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; + int mcuRow = mcu / mcusPerLine; + // Scan out an mcu's worth of this component; that's just determined // by the basic H and V specified for the component for (int y = 0; y < v; y++) { + int blockRow = (mcuRow * v) + y; + Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow); for (int x = 0; x < h; x++) { if (this.eof) @@ -195,15 +196,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder return; } - int mcuRow = mcu / mcusPerLine; int mcuCol = mcu % mcusPerLine; - int blockRow = (mcuRow * v) + y; int blockCol = (mcuCol * h) + x; this.DecodeBlockBaseline( component, - blockRow, - blockCol, + ref blockSpan[blockCol], ref dcHuffmanTable, ref acHuffmanTable, ref fastACRef); @@ -223,14 +221,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } /// - /// Non-interleaved data, we just need to process one block at a ti - /// in trivial scanline order - /// number of blocks to do just depends on how many actual "pixels" - /// component has, independent of interleaved MCU blocking and such + /// Non-interleaved data, we just need to process one block at a time in trivial scanline order + /// number of blocks to do just depends on how many actual "pixels" each component has, + /// independent of interleaved MCU blocking and such. /// private void ParseBaselineDataNonInterleaved() { - JpegComponent component = this.components[this.componentIndex]; + JpegComponent component = this.components[this.frame.ComponentOrder[0]]; int w = component.WidthInBlocks; int h = component.HeightInBlocks; @@ -242,6 +239,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int mcu = 0; for (int j = 0; j < h; j++) { + // TODO: Isn't blockRow == j actually? + int blockRow = mcu / w; + Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow); + for (int i = 0; i < w; i++) { if (this.eof) @@ -249,13 +250,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder return; } - int blockRow = mcu / w; + // TODO: Isn't blockCol == i actually? int blockCol = mcu % w; this.DecodeBlockBaseline( component, - blockRow, - blockCol, + ref blockSpan[blockCol], ref dcHuffmanTable, ref acHuffmanTable, ref fastACRef); @@ -295,7 +295,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // Scan an interleaved mcu... process components in order for (int k = 0; k < this.componentsLength; k++) { - JpegComponent component = this.components[k]; + int order = this.frame.ComponentOrder[k]; + JpegComponent component = this.components[order]; ref HuffmanTable dcHuffmanTable = ref this.dcHuffmanTables[component.DCHuffmanTableId]; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; @@ -304,6 +305,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // by the basic H and V specified for the component for (int y = 0; y < v; y++) { + int mcuRow = mcu / mcusPerLine; + int blockRow = (mcuRow * v) + y; + Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow); + for (int x = 0; x < h; x++) { if (this.eof) @@ -311,15 +316,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder return; } - int mcuRow = mcu / mcusPerLine; int mcuCol = mcu % mcusPerLine; - int blockRow = (mcuRow * v) + y; int blockCol = (mcuCol * h) + x; this.DecodeBlockProgressiveDC( component, - blockRow, - blockCol, + ref blockSpan[blockCol], ref dcHuffmanTable); } } @@ -344,7 +346,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder /// private void ParseProgressiveDataNonInterleaved() { - JpegComponent component = this.components[this.componentIndex]; + JpegComponent component = this.components[this.frame.ComponentOrder[0]]; int w = component.WidthInBlocks; int h = component.HeightInBlocks; @@ -356,6 +358,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder int mcu = 0; for (int j = 0; j < h; j++) { + // TODO: isn't blockRow == j actually? + int blockRow = mcu / w; + Span blockSpan = component.SpectralBlocks.GetRowSpan(blockRow); + for (int i = 0; i < w; i++) { if (this.eof) @@ -363,23 +369,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder return; } - int blockRow = mcu / w; + // TODO: isn't blockCol == i actually? int blockCol = mcu % w; + ref Block8x8 block = ref blockSpan[blockCol]; + if (this.spectralStart == 0) { this.DecodeBlockProgressiveDC( component, - blockRow, - blockCol, + ref block, ref dcHuffmanTable); } else { this.DecodeBlockProgressiveAC( - component, - blockRow, - blockCol, + ref block, ref acHuffmanTable, ref fastACRef); } @@ -396,8 +401,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder private void DecodeBlockBaseline( JpegComponent component, - int row, - int col, + ref Block8x8 block, ref HuffmanTable dcTable, ref HuffmanTable acTable, ref short fastACRef) @@ -410,7 +414,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder JpegThrowHelper.ThrowBadHuffmanCode(); } - ref short blockDataRef = ref component.GetBlockDataReference(col, row); + ref short blockDataRef = ref Unsafe.As(ref block); int diff = t != 0 ? this.ExtendReceive(t) : 0; int dc = component.DcPredictor + diff; @@ -475,8 +479,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder private void DecodeBlockProgressiveDC( JpegComponent component, - int row, - int col, + ref Block8x8 block, ref HuffmanTable dcTable) { if (this.spectralEnd != 0) @@ -486,7 +489,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder this.CheckBits(); - ref short blockDataRef = ref component.GetBlockDataReference(col, row); + ref short blockDataRef = ref Unsafe.As(ref block); if (this.successiveHigh == 0) { @@ -510,9 +513,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } private void DecodeBlockProgressiveAC( - JpegComponent component, - int row, - int col, + ref Block8x8 block, ref HuffmanTable acTable, ref short fastACRef) { @@ -521,7 +522,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder JpegThrowHelper.ThrowImageFormatException("Can't merge DC and AC."); } - ref short blockDataRef = ref component.GetBlockDataReference(col, row); + ref short blockDataRef = ref Unsafe.As(ref block); if (this.successiveHigh == 0) { @@ -729,8 +730,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } uint k = LRot(this.codeBuffer, n); - this.codeBuffer = k & ~Bmask[n]; - k &= Bmask[n]; + uint mask = Bmask[n]; + this.codeBuffer = k & ~mask; + k &= mask; this.codeBits -= n; return (int)k; } @@ -753,7 +755,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder [MethodImpl(InliningOptions.ColdPath)] private void FillBuffer() { - // Attempt to load at least the minimum nbumber of required bits into the buffer. + // Attempt to load at least the minimum number of required bits into the buffer. // We fail to do so only if we hit a marker or reach the end of the input stream. do { @@ -804,7 +806,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } [MethodImpl(InliningOptions.ShortMethod)] - private int DecodeHuffman(ref HuffmanTable table) + private unsafe int DecodeHuffman(ref HuffmanTable table) { this.CheckBits(); @@ -829,7 +831,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } [MethodImpl(InliningOptions.ColdPath)] - private int DecodeHuffmanSlow(ref HuffmanTable table) + private unsafe int DecodeHuffmanSlow(ref HuffmanTable table) { // Naive test is to shift the code_buffer down so k bits are // valid, then test against MaxCode. To speed this up, we've @@ -839,7 +841,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder // that way we don't need to shift inside the loop. uint temp = this.codeBuffer >> 16; int k; - for (k = FastBits + 1; ; k++) + for (k = FastBits + 1; ; ++k) { if (temp < table.MaxCode[k]) { @@ -910,7 +912,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder } // If it's NOT a restart, then just bail, so we get corrupt data rather than no data. - // Reset the stream to before any bad markers to ensure we can read sucessive segments. + // Reset the stream to before any bad markers to ensure we can read successive segments. if (this.badMarker) { this.stream.Position = this.markerPosition; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs index a0cc9ee8e5..cb0810985e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/RgbToYCbCrTables.cs @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// /// Initializes the YCbCr tables /// - /// The intialized + /// The initialized public static RgbToYCbCrTables Create() { RgbToYCbCrTables tables = default; diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs index 311ffed24b..d775425c5c 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/YCbCrForwardConverter{TPixel}.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// On-stack worker struct to efficiently encapsulate the TPixel -> Rgb24 -> YCbCr conversion chain of 8x8 pixel blocks. /// /// The pixel type to work on - internal struct YCbCrForwardConverter + internal ref struct YCbCrForwardConverter where TPixel : struct, IPixel { /// @@ -53,12 +53,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// /// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (, , ) /// - public void Convert(IPixelSource pixels, int x, int y) + public void Convert(ImageFrame frame, int x, int y) { - this.pixelBlock.LoadAndStretchEdges(pixels, x, y); + this.pixelBlock.LoadAndStretchEdges(frame, x, y); Span rgbSpan = this.rgbBlock.AsSpanUnsafe(); - PixelOperations.Instance.ToRgb24(this.pixelBlock.AsSpanUnsafe(), rgbSpan, 64); + PixelOperations.Instance.ToRgb24(frame.Configuration, this.pixelBlock.AsSpanUnsafe(), rgbSpan); ref float yBlockStart = ref Unsafe.As(ref this.Y); ref float cbBlockStart = ref Unsafe.As(ref this.Cb); diff --git a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs index 2e20da266b..c795ccc8b5 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/GenericBlock8x8.cs @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// FOR TESTING ONLY! - /// Gets or sets a value in a row+coulumn of the 8x8 block + /// Gets or sets a value in a row+column of the 8x8 block /// /// The x position index in the row /// The column index diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 011b6100dc..f6da9cb2ec 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -51,12 +51,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg private readonly byte[] markerBuffer = new byte[2]; /// - /// The DC HUffman tables + /// The DC Huffman tables /// private HuffmanTables dcHuffmanTables; /// - /// The AC HUffman tables + /// The AC Huffman tables /// private HuffmanTables acHuffmanTables; @@ -510,7 +510,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// The remaining bytes in the segment block. private void ProcessApplicationHeaderMarker(int remaining) { - if (remaining < 5) + // We can only decode JFif identifiers. + if (remaining < JFifMarker.Length) { // Skip the application header length this.InputStream.Skip(remaining); @@ -746,11 +747,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg if (!metadataOnly) { // No need to pool this. They max out at 4 - this.Frame.ComponentIds = new byte[this.Frame.ComponentCount]; - this.Frame.Components = new JpegComponent[this.Frame.ComponentCount]; + this.Frame.ComponentIds = new byte[this.ComponentCount]; + this.Frame.ComponentOrder = new byte[this.ComponentCount]; + this.Frame.Components = new JpegComponent[this.ComponentCount]; this.ColorSpace = this.DeduceJpegColorSpace(); - for (int i = 0; i < this.Frame.ComponentCount; i++) + for (int i = 0; i < this.ComponentCount; i++) { byte hv = this.temp[index + 1]; int h = hv >> 4; @@ -822,10 +824,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg codeLengths.GetSpan(), huffmanValues.GetSpan()); - if (huffmanTableSpec >> 4 != 0) + if (tableType != 0) { // Build a table that decodes both magnitude and value of small ACs in one go. - this.fastACTables.BuildACTableLut(huffmanTableSpec & 15, this.acHuffmanTables); + this.fastACTables.BuildACTableLut(tableIndex, this.acHuffmanTables); } } } @@ -854,10 +856,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg private void ProcessStartOfScanMarker() { int selectorsCount = this.InputStream.ReadByte(); - int componentIndex = -1; for (int i = 0; i < selectorsCount; i++) { - componentIndex = -1; + int componentIndex = -1; int selector = this.InputStream.ReadByte(); for (int j = 0; j < this.Frame.ComponentIds.Length; j++) @@ -866,6 +867,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg if (selector == id) { componentIndex = j; + break; } } @@ -878,6 +880,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg int tableSpec = this.InputStream.ReadByte(); component.DCHuffmanTableId = tableSpec >> 4; component.ACHuffmanTableId = tableSpec & 15; + this.Frame.ComponentOrder[i] = (byte)componentIndex; } this.InputStream.Read(this.temp, 0, 3); @@ -892,7 +895,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg this.dcHuffmanTables, this.acHuffmanTables, this.fastACTables, - componentIndex, selectorsCount, this.resetInterval, spectralStart, @@ -910,7 +912,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// The table index /// The codelengths /// The values - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private void BuildHuffmanTable(HuffmanTables tables, int index, ReadOnlySpan codeLengths, ReadOnlySpan values) => tables[index] = new HuffmanTable(this.configuration.MemoryAllocator, codeLengths, values); @@ -918,7 +920,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// Reads a from the stream advancing it by two bytes /// /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private ushort ReadUint16() { this.InputStream.Read(this.markerBuffer, 0, 2); @@ -933,12 +935,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg private Image PostProcessIntoImage() where TPixel : struct, IPixel { - using (var postProcessor = new JpegImagePostProcessor(this.configuration.MemoryAllocator, this)) + var image = Image.CreateUninitialized( + this.configuration, + this.ImageWidth, + this.ImageHeight, + this.MetaData); + + using (var postProcessor = new JpegImagePostProcessor(this.configuration, this)) { - var image = new Image(this.configuration, this.ImageWidth, this.ImageHeight, this.MetaData); postProcessor.PostProcess(image.Frames.RootFrame); - return image; } + + return image; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs index a4677ba2b7..0dcbd8fef7 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs @@ -822,11 +822,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg { for (int i = 0; i < componentCount; i++) { - this.buffer[(3 * i) + 6] = (byte)(i + 1); + int i3 = 3 * i; + this.buffer[i3 + 6] = (byte)(i + 1); // We use 4:2:0 chroma subsampling by default. - this.buffer[(3 * i) + 7] = subsamples[i]; - this.buffer[(3 * i) + 8] = chroma[i]; + this.buffer[i3 + 7] = subsamples[i]; + this.buffer[i3 + 8] = chroma[i]; } } diff --git a/src/ImageSharp/Formats/Png/Adam7.cs b/src/ImageSharp/Formats/Png/Adam7.cs new file mode 100644 index 0000000000..4e6485b55f --- /dev/null +++ b/src/ImageSharp/Formats/Png/Adam7.cs @@ -0,0 +1,56 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.Formats.Png +{ + /// + /// Constants and helper methods for the Adam7 interlacing algorithm. + /// + internal static class Adam7 + { + /// + /// The amount to increment when processing each column per scanline for each interlaced pass. + /// + public static readonly int[] ColumnIncrement = { 8, 8, 4, 4, 2, 2, 1 }; + + /// + /// The index to start at when processing each column per scanline for each interlaced pass. + /// + public static readonly int[] FirstColumn = { 0, 4, 0, 2, 0, 1, 0 }; + + /// + /// The index to start at when processing each row per scanline for each interlaced pass. + /// + public static readonly int[] FirstRow = { 0, 0, 4, 0, 2, 0, 1 }; + + /// + /// The amount to increment when processing each row per scanline for each interlaced pass. + /// + public static readonly int[] RowIncrement = { 8, 8, 8, 4, 4, 2, 2 }; + + /// + /// Returns the correct number of columns for each interlaced pass. + /// + /// The line width. + /// The current pass index. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ComputeColumns(int width, int passIndex) + { + switch (passIndex) + { + case 0: return (width + 7) / 8; + case 1: return (width + 3) / 8; + case 2: return (width + 3) / 4; + case 3: return (width + 1) / 4; + case 4: return (width + 1) / 2; + case 5: return width / 2; + case 6: return width; + default: throw new ArgumentException($"Not a valid pass index: {passIndex}"); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs b/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs new file mode 100644 index 0000000000..6ab0dd6576 --- /dev/null +++ b/src/ImageSharp/Formats/Png/Chunks/PhysicalChunkData.cs @@ -0,0 +1,107 @@ +using System; +using System.Buffers.Binary; +using SixLabors.ImageSharp.Common.Helpers; +using SixLabors.ImageSharp.MetaData; + +namespace SixLabors.ImageSharp.Formats.Png.Chunks +{ + /// + /// The pHYs chunk specifies the intended pixel size or aspect ratio for display of the image. + /// + internal readonly struct PhysicalChunkData + { + public const int Size = 9; + + public PhysicalChunkData(uint x, uint y, byte unitSpecifier) + { + this.XAxisPixelsPerUnit = x; + this.YAxisPixelsPerUnit = y; + this.UnitSpecifier = unitSpecifier; + } + + /// + /// Gets the number of pixels per unit on the X axis. + /// + public uint XAxisPixelsPerUnit { get; } + + /// + /// Gets the number of pixels per unit on the Y axis. + /// + public uint YAxisPixelsPerUnit { get; } + + /// + /// Gets the unit specifier. + /// 0: unit is unknown + /// 1: unit is the meter + /// When the unit specifier is 0, the pHYs chunk defines pixel aspect ratio only; the actual size of the pixels remains unspecified. + /// + public byte UnitSpecifier { get; } + + /// + /// Parses the PhysicalChunkData from the given buffer. + /// + /// The data buffer. + /// The parsed PhysicalChunkData. + public static PhysicalChunkData Parse(ReadOnlySpan data) + { + uint hResolution = BinaryPrimitives.ReadUInt32BigEndian(data.Slice(0, 4)); + uint vResolution = BinaryPrimitives.ReadUInt32BigEndian(data.Slice(4, 4)); + byte unit = data[8]; + + return new PhysicalChunkData(hResolution, vResolution, unit); + } + + /// + /// Constructs the PngPhysicalChunkData from the provided metadata. + /// If the resolution units are not in meters, they are automatically converted. + /// + /// The metadata. + /// The constructed PngPhysicalChunkData instance. + public static PhysicalChunkData FromMetadata(ImageMetaData meta) + { + byte unitSpecifier = 0; + uint x; + uint y; + + switch (meta.ResolutionUnits) + { + case PixelResolutionUnit.AspectRatio: + unitSpecifier = 0; // Unspecified + x = (uint)Math.Round(meta.HorizontalResolution); + y = (uint)Math.Round(meta.VerticalResolution); + break; + + case PixelResolutionUnit.PixelsPerInch: + unitSpecifier = 1; // Per meter + x = (uint)Math.Round(UnitConverter.InchToMeter(meta.HorizontalResolution)); + y = (uint)Math.Round(UnitConverter.InchToMeter(meta.VerticalResolution)); + break; + + case PixelResolutionUnit.PixelsPerCentimeter: + unitSpecifier = 1; // Per meter + x = (uint)Math.Round(UnitConverter.CmToMeter(meta.HorizontalResolution)); + y = (uint)Math.Round(UnitConverter.CmToMeter(meta.VerticalResolution)); + break; + + default: + unitSpecifier = 1; // Per meter + x = (uint)Math.Round(meta.HorizontalResolution); + y = (uint)Math.Round(meta.VerticalResolution); + break; + } + + return new PhysicalChunkData(x, y, unitSpecifier); + } + + /// + /// Writes the data to the given buffer. + /// + /// The buffer. + public void WriteTo(Span buffer) + { + BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(0, 4), this.XAxisPixelsPerUnit); + BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(4, 4), this.YAxisPixelsPerUnit); + buffer[8] = this.UnitSpecifier; + } + } +} diff --git a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs index ffcf9b0f30..bc5a54e8b9 100644 --- a/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/AverageFilter.cs @@ -30,7 +30,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters // Average(x) + floor((Raw(x-bpp)+Prior(x))/2) int x = 1; - for (; x <= bytesPerPixel /* Note the <= because x starts at 1 */; ++x) { + for (; x <= bytesPerPixel /* Note the <= because x starts at 1 */; ++x) + { ref byte scan = ref Unsafe.Add(ref scanBaseRef, x); byte above = Unsafe.Add(ref prevBaseRef, x); scan = (byte)(scan + (above >> 1)); @@ -68,7 +69,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters resultBaseRef = 3; int x = 0; - for (; x < bytesPerPixel; /* Note: ++x happens in the body to avoid one add operation */) { + for (; x < bytesPerPixel; /* Note: ++x happens in the body to avoid one add operation */) + { byte scan = Unsafe.Add(ref scanBaseRef, x); byte above = Unsafe.Add(ref prevBaseRef, x); ++x; @@ -77,7 +79,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters sum += ImageMaths.FastAbs(unchecked((sbyte)res)); } - for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */) { + for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */) + { byte scan = Unsafe.Add(ref scanBaseRef, x); byte left = Unsafe.Add(ref scanBaseRef, xLeft); byte above = Unsafe.Add(ref prevBaseRef, x); @@ -97,9 +100,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters /// The above byte /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int Average(byte left, byte above) - { - return (left + above) >> 1; - } + private static int Average(byte left, byte above) => (left + above) >> 1; } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index 0d3df079c9..4cd61e043d 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters ref byte prevBaseRef = ref MemoryMarshal.GetReference(previousScanline); // Paeth(x) + PaethPredictor(Raw(x-bpp), Prior(x), Prior(x-bpp)) - int offset = bytesPerPixel + 1; // Add one bcause x starts at one. + int offset = bytesPerPixel + 1; // Add one because x starts at one. int x = 1; for (; x < offset; x++) { @@ -72,7 +72,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters resultBaseRef = 4; int x = 0; - for (; x < bytesPerPixel; /* Note: ++x happens in the body to avoid one add operation */) { + for (; x < bytesPerPixel; /* Note: ++x happens in the body to avoid one add operation */) + { byte scan = Unsafe.Add(ref scanBaseRef, x); byte above = Unsafe.Add(ref prevBaseRef, x); ++x; @@ -81,7 +82,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters sum += ImageMaths.FastAbs(unchecked((sbyte)res)); } - for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */) { + for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */) + { byte scan = Unsafe.Add(ref scanBaseRef, x); byte left = Unsafe.Add(ref scanBaseRef, xLeft); byte above = Unsafe.Add(ref prevBaseRef, x); diff --git a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs index cfb7781be4..6af5f0b648 100644 --- a/src/ImageSharp/Formats/Png/Filters/SubFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/SubFilter.cs @@ -59,7 +59,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters resultBaseRef = 1; int x = 0; - for (; x < bytesPerPixel; /* Note: ++x happens in the body to avoid one add operation */) { + for (; x < bytesPerPixel; /* Note: ++x happens in the body to avoid one add operation */) + { byte scan = Unsafe.Add(ref scanBaseRef, x); ++x; ref byte res = ref Unsafe.Add(ref resultBaseRef, x); @@ -67,7 +68,8 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters sum += ImageMaths.FastAbs(unchecked((sbyte)res)); } - for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */) { + for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */) + { byte scan = Unsafe.Add(ref scanBaseRef, x); byte prev = Unsafe.Add(ref scanBaseRef, xLeft); ++x; diff --git a/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs b/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs index bd0b932056..5b650ac2a0 100644 --- a/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs +++ b/src/ImageSharp/Formats/Png/IPngDecoderOptions.cs @@ -6,7 +6,7 @@ using System.Text; namespace SixLabors.ImageSharp.Formats.Png { /// - /// The optioas for decoding png images + /// The options for decoding png images /// internal interface IPngDecoderOptions { diff --git a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs index 77bc9f7a05..7e5a9fa6b8 100644 --- a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs +++ b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Gets the filter method. /// - PngFilterMethod FilterMethod { get; } + PngFilterMethod? FilterMethod { get; } /// /// Gets the compression level 1-9. diff --git a/src/ImageSharp/Formats/Png/PngBitDepth.cs b/src/ImageSharp/Formats/Png/PngBitDepth.cs index 396f2c1608..0321b532ab 100644 --- a/src/ImageSharp/Formats/Png/PngBitDepth.cs +++ b/src/ImageSharp/Formats/Png/PngBitDepth.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Provides enumeration for the available PNG bit depths. /// - public enum PngBitDepth + public enum PngBitDepth : byte { /// /// 1 bit per sample or per palette index (not per pixel). diff --git a/src/ImageSharp/Formats/Png/PngChunkType.cs b/src/ImageSharp/Formats/Png/PngChunkType.cs index 7654c17014..1b251a5748 100644 --- a/src/ImageSharp/Formats/Png/PngChunkType.cs +++ b/src/ImageSharp/Formats/Png/PngChunkType.cs @@ -56,10 +56,10 @@ namespace SixLabors.ImageSharp.Formats.Png Text = 0x74455874U, /// - /// This chunk specifies that the image uses simple transparency: + /// The tRNS chunk specifies that the image uses simple transparency: /// either alpha values associated with palette entries (for indexed-color images) /// or a single transparent color (for grayscale and true color images). /// - PaletteAlpha = 0x74524E53U + Transparency = 0x74524E53U } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/PngConstants.cs b/src/ImageSharp/Formats/Png/PngConstants.cs index ff25e26b7a..62a7b74aba 100644 --- a/src/ImageSharp/Formats/Png/PngConstants.cs +++ b/src/ImageSharp/Formats/Png/PngConstants.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// The default encoding for text metadata. /// - public static readonly Encoding DefaultEncoding = Encoding.GetEncoding("ASCII"); + public static readonly Encoding DefaultEncoding = Encoding.ASCII; /// /// The list of mimetypes that equate to a png. @@ -41,5 +41,17 @@ namespace SixLabors.ImageSharp.Formats.Png /// The header bytes as a big endian coded ulong. /// public const ulong HeaderValue = 0x89504E470D0A1A0AUL; + + /// + /// The dictionary of available color types. + /// + public static readonly Dictionary ColorTypes = new Dictionary() + { + [PngColorType.Grayscale] = new byte[] { 1, 2, 4, 8, 16 }, + [PngColorType.Rgb] = new byte[] { 8, 16 }, + [PngColorType.Palette] = new byte[] { 1, 2, 4, 8 }, + [PngColorType.GrayscaleWithAlpha] = new byte[] { 8, 16 }, + [PngColorType.RgbWithAlpha] = new byte[] { 8, 16 } + }; } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index ef2f226556..9ffac5e626 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -3,13 +3,12 @@ using System; using System.Buffers.Binary; -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.Formats.Png.Filters; using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Memory; @@ -26,51 +25,9 @@ namespace SixLabors.ImageSharp.Formats.Png internal sealed class PngDecoderCore { /// - /// The dictionary of available color types. + /// Reusable buffer. /// - private static readonly Dictionary ColorTypes = new Dictionary() - { - [PngColorType.Grayscale] = new byte[] { 1, 2, 4, 8, 16 }, - [PngColorType.Rgb] = new byte[] { 8, 16 }, - [PngColorType.Palette] = new byte[] { 1, 2, 4, 8 }, - [PngColorType.GrayscaleWithAlpha] = new byte[] { 8, 16 }, - [PngColorType.RgbWithAlpha] = new byte[] { 8, 16 } - }; - - /// - /// The amount to increment when processing each column per scanline for each interlaced pass - /// - private static readonly int[] Adam7ColumnIncrement = { 8, 8, 4, 4, 2, 2, 1 }; - - /// - /// The index to start at when processing each column per scanline for each interlaced pass - /// - private static readonly int[] Adam7FirstColumn = { 0, 4, 0, 2, 0, 1, 0 }; - - /// - /// The index to start at when processing each row per scanline for each interlaced pass - /// - private static readonly int[] Adam7FirstRow = { 0, 0, 4, 0, 2, 0, 1 }; - - /// - /// The amount to increment when processing each row per scanline for each interlaced pass - /// - private static readonly int[] Adam7RowIncrement = { 8, 8, 8, 4, 4, 2, 2 }; - - /// - /// Reusable buffer for reading chunk types. - /// - private readonly byte[] chunkTypeBuffer = new byte[4]; - - /// - /// Reusable buffer for reading chunk lengths. - /// - private readonly byte[] chunkLengthBuffer = new byte[4]; - - /// - /// Reusable buffer for reading crc values. - /// - private readonly byte[] crcBuffer = new byte[4]; + private readonly byte[] buffer = new byte[4]; /// /// Reusable crc for validating chunks. @@ -92,6 +49,11 @@ namespace SixLabors.ImageSharp.Formats.Png /// private readonly bool ignoreMetadata; + /// + /// Used the manage memory allocations. + /// + private readonly MemoryAllocator memoryAllocator; + /// /// The stream to decode from. /// @@ -145,7 +107,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// The index of the current scanline being processed /// - private int currentRow = Adam7FirstRow[0]; + private int currentRow = Adam7.FirstRow[0]; /// /// The current pass for an interlaced PNG @@ -162,31 +124,6 @@ namespace SixLabors.ImageSharp.Formats.Png /// private PngColorType pngColorType; - /// - /// Represents any color in an 8 bit Rgb24 encoded png that should be transparent - /// - private Rgb24 rgb24Trans; - - /// - /// Represents any color in a 16 bit Rgb24 encoded png that should be transparent - /// - private Rgb48 rgb48Trans; - - /// - /// Represents any color in an 8 bit grayscale encoded png that should be transparent - /// - private byte luminanceTrans; - - /// - /// Represents any color in a 16 bit grayscale encoded png that should be transparent - /// - private ushort luminance16Trans; - - /// - /// Whether the image has transparency chunk and markers were decoded - /// - private bool hasTrans; - /// /// The next chunk of data to return /// @@ -200,12 +137,11 @@ namespace SixLabors.ImageSharp.Formats.Png public PngDecoderCore(Configuration configuration, IPngDecoderOptions options) { this.configuration = configuration ?? Configuration.Default; + this.memoryAllocator = this.configuration.MemoryAllocator; this.textEncoding = options.TextEncoding ?? PngConstants.DefaultEncoding; this.ignoreMetadata = options.IgnoreMetadata; } - private MemoryAllocator MemoryAllocator => this.configuration.MemoryAllocator; - /// /// Decodes the stream to the image. /// @@ -236,7 +172,6 @@ namespace SixLabors.ImageSharp.Formats.Png { case PngChunkType.Header: this.ReadHeaderChunk(pngMetaData, chunk.Data.Array); - this.ValidateHeader(); break; case PngChunkType.Physical: this.ReadPhysicalChunk(metaData, chunk.Data.GetSpan()); @@ -253,7 +188,7 @@ namespace SixLabors.ImageSharp.Formats.Png using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk)) { deframeStream.AllocateNewBytes(chunk.Length); - this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame); + this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame, pngMetaData); } break; @@ -262,14 +197,14 @@ namespace SixLabors.ImageSharp.Formats.Png Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length); this.palette = pal; break; - case PngChunkType.PaletteAlpha: + case PngChunkType.Transparency: byte[] alpha = new byte[chunk.Length]; Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length); this.paletteAlpha = alpha; - this.AssignTransparentMarkers(alpha); + this.AssignTransparentMarkers(alpha, pngMetaData); break; case PngChunkType.Text: - this.ReadTextChunk(metaData, chunk.Data.Array, chunk.Length); + this.ReadTextChunk(metaData, chunk.Data.Array.AsSpan(0, chunk.Length)); break; case PngChunkType.Exif: if (!this.ignoreMetadata) @@ -325,7 +260,6 @@ namespace SixLabors.ImageSharp.Formats.Png { case PngChunkType.Header: this.ReadHeaderChunk(pngMetaData, chunk.Data.Array); - this.ValidateHeader(); break; case PngChunkType.Physical: this.ReadPhysicalChunk(metaData, chunk.Data.GetSpan()); @@ -337,7 +271,7 @@ namespace SixLabors.ImageSharp.Formats.Png this.SkipChunkDataAndCrc(chunk); break; case PngChunkType.Text: - this.ReadTextChunk(metaData, chunk.Data.Array, chunk.Length); + this.ReadTextChunk(metaData, chunk.Data.Array.AsSpan(0, chunk.Length)); break; case PngChunkType.End: this.isEndChunkReached = true; @@ -391,19 +325,19 @@ namespace SixLabors.ImageSharp.Formats.Png return false; } - buffer = this.MemoryAllocator.AllocateManagedByteBuffer(bytesPerScanline * 8 / bits, AllocationOptions.Clean); - byte[] result = buffer.Array; + buffer = this.memoryAllocator.AllocateManagedByteBuffer(bytesPerScanline * 8 / bits, AllocationOptions.Clean); + ref byte sourceRef = ref MemoryMarshal.GetReference(source); + ref byte resultRef = ref buffer.Array[0]; int mask = 0xFF >> (8 - bits); int resultOffset = 0; - for (int i = 0; i < bytesPerScanline - 1; i++) + for (int i = 0; i < bytesPerScanline; i++) { - byte b = source[i]; + byte b = Unsafe.Add(ref sourceRef, i); for (int shift = 0; shift < 8; shift += bits) { int colorIndex = (b >> (8 - bits - shift)) & mask; - result[resultOffset] = (byte)colorIndex; - + Unsafe.Add(ref resultRef, resultOffset) = (byte)colorIndex; resultOffset++; } } @@ -418,26 +352,14 @@ namespace SixLabors.ImageSharp.Formats.Png /// The data containing physical data. private void ReadPhysicalChunk(ImageMetaData metadata, ReadOnlySpan data) { - // The pHYs chunk specifies the intended pixel size or aspect ratio for display of the image. It contains: - // Pixels per unit, X axis: 4 bytes (unsigned integer) - // Pixels per unit, Y axis: 4 bytes (unsigned integer) - // Unit specifier: 1 byte - // - // The following values are legal for the unit specifier: - // 0: unit is unknown - // 1: unit is the meter - // - // When the unit specifier is 0, the pHYs chunk defines pixel aspect ratio only; the actual size of the pixels remains unspecified. - int hResolution = BinaryPrimitives.ReadInt32BigEndian(data.Slice(0, 4)); - int vResolution = BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4)); - byte unit = data[8]; - - metadata.ResolutionUnits = unit == byte.MinValue + var physicalChunk = PhysicalChunkData.Parse(data); + + metadata.ResolutionUnits = physicalChunk.UnitSpecifier == byte.MinValue ? PixelResolutionUnit.AspectRatio : PixelResolutionUnit.PixelsPerMeter; - metadata.HorizontalResolution = hResolution; - metadata.VerticalResolution = vResolution; + metadata.HorizontalResolution = physicalChunk.XAxisPixelsPerUnit; + metadata.VerticalResolution = physicalChunk.YAxisPixelsPerUnit; } /// @@ -470,7 +392,7 @@ namespace SixLabors.ImageSharp.Formats.Png this.bytesPerSample = this.header.BitDepth / 8; } - this.previousScanline = this.MemoryAllocator.AllocateManagedByteBuffer(this.bytesPerScanline, AllocationOptions.Clean); + this.previousScanline = this.memoryAllocator.AllocateManagedByteBuffer(this.bytesPerScanline, AllocationOptions.Clean); this.scanline = this.configuration.MemoryAllocator.AllocateManagedByteBuffer(this.bytesPerScanline, AllocationOptions.Clean); } @@ -549,16 +471,17 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The containing data. /// The pixel data. - private void ReadScanlines(Stream dataStream, ImageFrame image) + /// The png meta data + private void ReadScanlines(Stream dataStream, ImageFrame image, PngMetaData pngMetaData) where TPixel : struct, IPixel { if (this.header.InterlaceMethod == PngInterlaceMode.Adam7) { - this.DecodeInterlacedPixelData(dataStream, image); + this.DecodeInterlacedPixelData(dataStream, image, pngMetaData); } else { - this.DecodePixelData(dataStream, image); + this.DecodePixelData(dataStream, image, pngMetaData); } } @@ -568,7 +491,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The compressed pixel data stream. /// The image to decode to. - private void DecodePixelData(Stream compressedStream, ImageFrame image) + /// The png meta data + private void DecodePixelData(Stream compressedStream, ImageFrame image, PngMetaData pngMetaData) where TPixel : struct, IPixel { while (this.currentRow < this.header.Height) @@ -589,22 +513,18 @@ namespace SixLabors.ImageSharp.Formats.Png break; case FilterType.Sub: - SubFilter.Decode(scanlineSpan, this.bytesPerPixel); break; case FilterType.Up: - UpFilter.Decode(scanlineSpan, this.previousScanline.GetSpan()); break; case FilterType.Average: - AverageFilter.Decode(scanlineSpan, this.previousScanline.GetSpan(), this.bytesPerPixel); break; case FilterType.Paeth: - PaethFilter.Decode(scanlineSpan, this.previousScanline.GetSpan(), this.bytesPerPixel); break; @@ -612,7 +532,7 @@ namespace SixLabors.ImageSharp.Formats.Png throw new ImageFormatException("Unknown filter type."); } - this.ProcessDefilteredScanline(this.scanline.Array, image); + this.ProcessDefilteredScanline(scanlineSpan, image, pngMetaData); this.SwapBuffers(); this.currentRow++; @@ -626,12 +546,13 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The compressed pixel data stream. /// The current image. - private void DecodeInterlacedPixelData(Stream compressedStream, ImageFrame image) + /// The png meta data + private void DecodeInterlacedPixelData(Stream compressedStream, ImageFrame image, PngMetaData pngMetaData) where TPixel : struct, IPixel { while (true) { - int numColumns = this.ComputeColumnsAdam7(this.pass); + int numColumns = Adam7.ComputeColumns(this.header.Width, this.pass); if (numColumns == 0) { @@ -663,22 +584,18 @@ namespace SixLabors.ImageSharp.Formats.Png break; case FilterType.Sub: - SubFilter.Decode(scanSpan, this.bytesPerPixel); break; case FilterType.Up: - UpFilter.Decode(scanSpan, prevSpan); break; case FilterType.Average: - AverageFilter.Decode(scanSpan, prevSpan, this.bytesPerPixel); break; case FilterType.Paeth: - PaethFilter.Decode(scanSpan, prevSpan, this.bytesPerPixel); break; @@ -687,11 +604,11 @@ namespace SixLabors.ImageSharp.Formats.Png } Span rowSpan = image.GetPixelRowSpan(this.currentRow); - this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]); + this.ProcessInterlacedDefilteredScanline(this.scanline.GetSpan(), rowSpan, pngMetaData, Adam7.FirstColumn[this.pass], Adam7.ColumnIncrement[this.pass]); this.SwapBuffers(); - this.currentRow += Adam7RowIncrement[this.pass]; + this.currentRow += Adam7.RowIncrement[this.pass]; } this.pass++; @@ -699,7 +616,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.pass < 7) { - this.currentRow = Adam7FirstRow[this.pass]; + this.currentRow = Adam7.FirstRow[this.pass]; } else { @@ -715,214 +632,75 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The de-filtered scanline /// The image - private void ProcessDefilteredScanline(ReadOnlySpan defilteredScanline, ImageFrame pixels) + /// The png meta data + private void ProcessDefilteredScanline(ReadOnlySpan defilteredScanline, ImageFrame pixels, PngMetaData pngMetaData) where TPixel : struct, IPixel { - TPixel pixel = default; Span rowSpan = pixels.GetPixelRowSpan(this.currentRow); // Trim the first marker byte from the buffer ReadOnlySpan trimmed = defilteredScanline.Slice(1, defilteredScanline.Length - 1); // Convert 1, 2, and 4 bit pixel data into the 8 bit equivalent. - ReadOnlySpan scanlineSpan = this.TryScaleUpTo8BitArray(trimmed, this.bytesPerScanline, this.header.BitDepth, out IManagedByteBuffer buffer) + ReadOnlySpan scanlineSpan = this.TryScaleUpTo8BitArray(trimmed, this.bytesPerScanline - 1, this.header.BitDepth, out IManagedByteBuffer buffer) ? buffer.GetSpan() : trimmed; switch (this.pngColorType) { case PngColorType.Grayscale: - - int factor = 255 / (ImageMaths.GetColorCountForBitDepth(this.header.BitDepth) - 1); - - if (!this.hasTrans) - { - if (this.header.BitDepth == 16) - { - Rgb48 rgb48 = default; - for (int x = 0, o = 0; x < this.header.Width; x++, o += 2) - { - ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - rgb48.R = luminance; - rgb48.G = luminance; - rgb48.B = luminance; - pixel.PackFromRgb48(rgb48); - rowSpan[x] = pixel; - } - } - else - { - // TODO: We should really be using Rgb24 here but IPixel does not have a PackFromRgb24 method. - var rgba32 = new Rgba32(0, 0, 0, byte.MaxValue); - for (int x = 0; x < this.header.Width; x++) - { - byte luminance = (byte)(scanlineSpan[x] * factor); - rgba32.R = luminance; - rgba32.G = luminance; - rgba32.B = luminance; - pixel.PackFromRgba32(rgba32); - rowSpan[x] = pixel; - } - } - } - else - { - if (this.header.BitDepth == 16) - { - Rgba64 rgba64 = default; - for (int x = 0, o = 0; x < this.header.Width; x++, o += 2) - { - ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - rgba64.R = luminance; - rgba64.G = luminance; - rgba64.B = luminance; - rgba64.A = luminance.Equals(this.luminance16Trans) ? ushort.MinValue : ushort.MaxValue; - - pixel.PackFromRgba64(rgba64); - rowSpan[x] = pixel; - } - } - else - { - Rgba32 rgba32 = default; - for (int x = 0; x < this.header.Width; x++) - { - byte luminance = (byte)(scanlineSpan[x] * factor); - rgba32.R = luminance; - rgba32.G = luminance; - rgba32.B = luminance; - rgba32.A = luminance.Equals(this.luminanceTrans) ? byte.MinValue : byte.MaxValue; - - pixel.PackFromRgba32(rgba32); - rowSpan[x] = pixel; - } - } - } + PngScanlineProcessor.ProcessGrayscaleScanline( + this.header, + scanlineSpan, + rowSpan, + pngMetaData.HasTrans, + pngMetaData.TransparentGray16.GetValueOrDefault(), + pngMetaData.TransparentGray8.GetValueOrDefault()); break; case PngColorType.GrayscaleWithAlpha: - - if (this.header.BitDepth == 16) - { - Rgba64 rgba64 = default; - for (int x = 0, o = 0; x < this.header.Width; x++, o += 4) - { - ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - ushort alpha = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); - rgba64.R = luminance; - rgba64.G = luminance; - rgba64.B = luminance; - rgba64.A = alpha; - - pixel.PackFromRgba64(rgba64); - rowSpan[x] = pixel; - } - } - else - { - Rgba32 rgba32 = default; - for (int x = 0; x < this.header.Width; x++) - { - int offset = x * this.bytesPerPixel; - byte luminance = scanlineSpan[offset]; - byte alpha = scanlineSpan[offset + this.bytesPerSample]; - - rgba32.R = luminance; - rgba32.G = luminance; - rgba32.B = luminance; - rgba32.A = alpha; - - pixel.PackFromRgba32(rgba32); - rowSpan[x] = pixel; - } - } + PngScanlineProcessor.ProcessGrayscaleWithAlphaScanline( + this.header, + scanlineSpan, + rowSpan, + this.bytesPerPixel, + this.bytesPerSample); break; case PngColorType.Palette: - - this.ProcessScanlineFromPalette(scanlineSpan, rowSpan); + PngScanlineProcessor.ProcessPaletteScanline( + this.header, + scanlineSpan, + rowSpan, + this.palette, + this.paletteAlpha); break; case PngColorType.Rgb: - - if (!this.hasTrans) - { - if (this.header.BitDepth == 16) - { - Rgb48 rgb48 = default; - for (int x = 0, o = 0; x < this.header.Width; x++, o += 6) - { - rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); - rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 4, 2)); - pixel.PackFromRgb48(rgb48); - rowSpan[x] = pixel; - } - } - else - { - PixelOperations.Instance.PackFromRgb24Bytes(scanlineSpan, rowSpan, this.header.Width); - } - } - else - { - if (this.header.BitDepth == 16) - { - Rgb48 rgb48 = default; - Rgba64 rgba64 = default; - for (int x = 0, o = 0; x < this.header.Width; x++, o += 6) - { - rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); - rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 4, 2)); - - rgba64.Rgb = rgb48; - rgba64.A = rgb48.Equals(this.rgb48Trans) ? ushort.MinValue : ushort.MaxValue; - - pixel.PackFromRgba64(rgba64); - rowSpan[x] = pixel; - } - } - else - { - ReadOnlySpan rgb24Span = MemoryMarshal.Cast(scanlineSpan); - for (int x = 0; x < this.header.Width; x++) - { - ref readonly Rgb24 rgb24 = ref rgb24Span[x]; - Rgba32 rgba32 = default; - rgba32.Rgb = rgb24; - rgba32.A = rgb24.Equals(this.rgb24Trans) ? byte.MinValue : byte.MaxValue; - - pixel.PackFromRgba32(rgba32); - rowSpan[x] = pixel; - } - } - } + PngScanlineProcessor.ProcessRgbScanline( + this.configuration, + this.header, + scanlineSpan, + rowSpan, + this.bytesPerPixel, + this.bytesPerSample, + pngMetaData.HasTrans, + pngMetaData.TransparentRgb48.GetValueOrDefault(), + pngMetaData.TransparentRgb24.GetValueOrDefault()); break; case PngColorType.RgbWithAlpha: - - if (this.header.BitDepth == 16) - { - Rgba64 rgba64 = default; - for (int x = 0, o = 0; x < this.header.Width; x++, o += 8) - { - rgba64.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); - rgba64.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 4, 2)); - rgba64.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 6, 2)); - pixel.PackFromRgba64(rgba64); - rowSpan[x] = pixel; - } - } - else - { - PixelOperations.Instance.PackFromRgba32Bytes(scanlineSpan, rowSpan, this.header.Width); - } + PngScanlineProcessor.ProcessRgbaScanline( + this.configuration, + this.header, + scanlineSpan, + rowSpan, + this.bytesPerPixel, + this.bytesPerSample); break; } @@ -936,13 +714,12 @@ namespace SixLabors.ImageSharp.Formats.Png /// The pixel format. /// The de-filtered scanline /// The current image row. + /// The png meta data /// The column start index. Always 0 for none interlaced images. /// The column increment. Always 1 for none interlaced images. - private void ProcessInterlacedDefilteredScanline(ReadOnlySpan defilteredScanline, Span rowSpan, int pixelOffset = 0, int increment = 1) + private void ProcessInterlacedDefilteredScanline(ReadOnlySpan defilteredScanline, Span rowSpan, PngMetaData pngMetaData, int pixelOffset = 0, int increment = 1) where TPixel : struct, IPixel { - TPixel pixel = default; - // Trim the first marker byte from the buffer ReadOnlySpan trimmed = defilteredScanline.Slice(1, defilteredScanline.Length - 1); @@ -954,245 +731,66 @@ namespace SixLabors.ImageSharp.Formats.Png switch (this.pngColorType) { case PngColorType.Grayscale: - - int factor = 255 / (ImageMaths.GetColorCountForBitDepth(this.header.BitDepth) - 1); - - if (!this.hasTrans) - { - if (this.header.BitDepth == 16) - { - Rgb48 rgb48 = default; - for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 2) - { - ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - rgb48.R = luminance; - rgb48.G = luminance; - rgb48.B = luminance; - - pixel.PackFromRgb48(rgb48); - rowSpan[x] = pixel; - } - } - else - { - // TODO: We should really be using Rgb24 here but IPixel does not have a PackFromRgb24 method. - var rgba32 = new Rgba32(0, 0, 0, byte.MaxValue); - for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++) - { - byte luminance = (byte)(scanlineSpan[o] * factor); - rgba32.R = luminance; - rgba32.G = luminance; - rgba32.B = luminance; - - pixel.PackFromRgba32(rgba32); - rowSpan[x] = pixel; - } - } - } - else - { - if (this.header.BitDepth == 16) - { - Rgba64 rgba64 = default; - for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 2) - { - ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - rgba64.R = luminance; - rgba64.G = luminance; - rgba64.B = luminance; - rgba64.A = luminance.Equals(this.luminance16Trans) ? ushort.MinValue : ushort.MaxValue; - - pixel.PackFromRgba64(rgba64); - rowSpan[x] = pixel; - } - } - else - { - Rgba32 rgba32 = default; - for (int x = pixelOffset; x < this.header.Width; x += increment) - { - byte luminance = (byte)(scanlineSpan[x] * factor); - rgba32.R = luminance; - rgba32.G = luminance; - rgba32.B = luminance; - rgba32.A = luminance.Equals(this.luminanceTrans) ? byte.MinValue : byte.MaxValue; - - pixel.PackFromRgba32(rgba32); - rowSpan[x] = pixel; - } - } - } + PngScanlineProcessor.ProcessInterlacedGrayscaleScanline( + this.header, + scanlineSpan, + rowSpan, + pixelOffset, + increment, + pngMetaData.HasTrans, + pngMetaData.TransparentGray16.GetValueOrDefault(), + pngMetaData.TransparentGray8.GetValueOrDefault()); break; case PngColorType.GrayscaleWithAlpha: - - if (this.header.BitDepth == 16) - { - Rgba64 rgba64 = default; - for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 4) - { - ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - ushort alpha = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); - rgba64.R = luminance; - rgba64.G = luminance; - rgba64.B = luminance; - rgba64.A = alpha; - - pixel.PackFromRgba64(rgba64); - rowSpan[x] = pixel; - } - } - else - { - Rgba32 rgba32 = default; - for (int x = pixelOffset; x < this.header.Width; x += increment) - { - int offset = x * this.bytesPerPixel; - byte luminance = scanlineSpan[offset]; - byte alpha = scanlineSpan[offset + this.bytesPerSample]; - rgba32.R = luminance; - rgba32.G = luminance; - rgba32.B = luminance; - rgba32.A = alpha; - - pixel.PackFromRgba32(rgba32); - rowSpan[x] = pixel; - } - } + PngScanlineProcessor.ProcessInterlacedGrayscaleWithAlphaScanline( + this.header, + scanlineSpan, + rowSpan, + pixelOffset, + increment, + this.bytesPerPixel, + this.bytesPerSample); break; case PngColorType.Palette: - - Span palettePixels = MemoryMarshal.Cast(this.palette); - - if (this.paletteAlpha?.Length > 0) - { - // If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha - // channel and we should try to read it. - Rgba32 rgba = default; - for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++) - { - int index = scanlineSpan[o]; - rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : byte.MaxValue; - rgba.Rgb = palettePixels[index]; - - pixel.PackFromRgba32(rgba); - rowSpan[x] = pixel; - } - } - else - { - var rgba = new Rgba32(0, 0, 0, byte.MaxValue); - for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o++) - { - int index = scanlineSpan[o]; - rgba.Rgb = palettePixels[index]; - - pixel.PackFromRgba32(rgba); - rowSpan[x] = pixel; - } - } + PngScanlineProcessor.ProcessInterlacedPaletteScanline( + this.header, + scanlineSpan, + rowSpan, + pixelOffset, + increment, + this.palette, + this.paletteAlpha); break; case PngColorType.Rgb: - - if (this.header.BitDepth == 16) - { - if (this.hasTrans) - { - Rgb48 rgb48 = default; - Rgba64 rgba64 = default; - for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 6) - { - rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); - rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 4, 2)); - - rgba64.Rgb = rgb48; - rgba64.A = rgb48.Equals(this.rgb48Trans) ? ushort.MinValue : ushort.MaxValue; - - pixel.PackFromRgba64(rgba64); - rowSpan[x] = pixel; - } - } - else - { - Rgb48 rgb48 = default; - for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 6) - { - rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); - rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 4, 2)); - pixel.PackFromRgb48(rgb48); - rowSpan[x] = pixel; - } - } - } - else - { - if (this.hasTrans) - { - Rgba32 rgba = default; - for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel) - { - rgba.R = scanlineSpan[o]; - rgba.G = scanlineSpan[o + this.bytesPerSample]; - rgba.B = scanlineSpan[o + (2 * this.bytesPerSample)]; - rgba.A = this.rgb24Trans.Equals(rgba.Rgb) ? byte.MinValue : byte.MaxValue; - - pixel.PackFromRgba32(rgba); - rowSpan[x] = pixel; - } - } - else - { - var rgba = new Rgba32(0, 0, 0, byte.MaxValue); - for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel) - { - rgba.R = scanlineSpan[o]; - rgba.G = scanlineSpan[o + this.bytesPerSample]; - rgba.B = scanlineSpan[o + (2 * this.bytesPerSample)]; - - pixel.PackFromRgba32(rgba); - rowSpan[x] = pixel; - } - } - } + PngScanlineProcessor.ProcessInterlacedRgbScanline( + this.header, + scanlineSpan, + rowSpan, + pixelOffset, + increment, + this.bytesPerPixel, + this.bytesPerSample, + pngMetaData.HasTrans, + pngMetaData.TransparentRgb48.GetValueOrDefault(), + pngMetaData.TransparentRgb24.GetValueOrDefault()); break; case PngColorType.RgbWithAlpha: - - if (this.header.BitDepth == 16) - { - Rgba64 rgba64 = default; - for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += 8) - { - rgba64.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); - rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); - rgba64.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 4, 2)); - rgba64.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 6, 2)); - pixel.PackFromRgba64(rgba64); - rowSpan[x] = pixel; - } - } - else - { - Rgba32 rgba = default; - for (int x = pixelOffset, o = 0; x < this.header.Width; x += increment, o += this.bytesPerPixel) - { - rgba.R = scanlineSpan[o]; - rgba.G = scanlineSpan[o + this.bytesPerSample]; - rgba.B = scanlineSpan[o + (2 * this.bytesPerSample)]; - rgba.A = scanlineSpan[o + (3 * this.bytesPerSample)]; - - pixel.PackFromRgba32(rgba); - rowSpan[x] = pixel; - } - } + PngScanlineProcessor.ProcessInterlacedRgbaScanline( + this.header, + scanlineSpan, + rowSpan, + pixelOffset, + increment, + this.bytesPerPixel, + this.bytesPerSample); break; } @@ -1204,7 +802,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// Decodes and assigns marker colors that identify transparent pixels in non indexed images /// /// The alpha tRNS array - private void AssignTransparentMarkers(ReadOnlySpan alpha) + /// The png meta data + private void AssignTransparentMarkers(ReadOnlySpan alpha, PngMetaData pngMetaData) { if (this.pngColorType == PngColorType.Rgb) { @@ -1215,16 +814,17 @@ namespace SixLabors.ImageSharp.Formats.Png ushort rc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2)); ushort gc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(2, 2)); ushort bc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(4, 2)); - this.rgb48Trans = new Rgb48(rc, gc, bc); - this.hasTrans = true; + + pngMetaData.TransparentRgb48 = new Rgb48(rc, gc, bc); + pngMetaData.HasTrans = true; return; } byte r = ReadByteLittleEndian(alpha, 0); byte g = ReadByteLittleEndian(alpha, 2); byte b = ReadByteLittleEndian(alpha, 4); - this.rgb24Trans = new Rgb24(r, g, b); - this.hasTrans = true; + pngMetaData.TransparentRgb24 = new Rgb24(r, g, b); + pngMetaData.HasTrans = true; } } else if (this.pngColorType == PngColorType.Grayscale) @@ -1233,58 +833,14 @@ namespace SixLabors.ImageSharp.Formats.Png { if (this.header.BitDepth == 16) { - this.luminance16Trans = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2)); + pngMetaData.TransparentGray16 = new Gray16(BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2))); } else { - this.luminanceTrans = ReadByteLittleEndian(alpha, 0); + pngMetaData.TransparentGray8 = new Gray8(ReadByteLittleEndian(alpha, 0)); } - this.hasTrans = true; - } - } - } - - /// - /// Processes a scanline that uses a palette - /// - /// The type of pixel we are expanding to - /// The defiltered scanline - /// The current output image row - private void ProcessScanlineFromPalette(ReadOnlySpan scanline, Span row) - where TPixel : struct, IPixel - { - ReadOnlySpan palettePixels = MemoryMarshal.Cast(this.palette); - var color = default(TPixel); - - if (this.paletteAlpha?.Length > 0) - { - Rgba32 rgba = default; - - // If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha - // channel and we should try to read it. - for (int x = 0; x < this.header.Width; x++) - { - int index = scanline[x]; - rgba.A = this.paletteAlpha.Length > index ? this.paletteAlpha[index] : byte.MaxValue; - rgba.Rgb = palettePixels[index]; - - color.PackFromRgba32(rgba); - row[x] = color; - } - } - else - { - // TODO: We should have PackFromRgb24. - var rgba = new Rgba32(0, 0, 0, byte.MaxValue); - for (int x = 0; x < this.header.Width; x++) - { - int index = scanline[x]; - - rgba.Rgb = palettePixels[index]; - - color.PackFromRgba32(rgba); - row[x] = color; + pngMetaData.HasTrans = true; } } } @@ -1296,47 +852,12 @@ namespace SixLabors.ImageSharp.Formats.Png /// The containing data. private void ReadHeaderChunk(PngMetaData pngMetaData, ReadOnlySpan data) { - byte bitDepth = data[8]; - this.header = new PngHeader( - width: BinaryPrimitives.ReadInt32BigEndian(data.Slice(0, 4)), - height: BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4)), - bitDepth: bitDepth, - colorType: (PngColorType)data[9], - compressionMethod: data[10], - filterMethod: data[11], - interlaceMethod: (PngInterlaceMode)data[12]); - - pngMetaData.BitDepth = (PngBitDepth)bitDepth; - pngMetaData.ColorType = this.header.ColorType; - } - - /// - /// Validates the png header. - /// - /// - /// Thrown if the image does pass validation. - /// - private void ValidateHeader() - { - if (!ColorTypes.ContainsKey(this.header.ColorType)) - { - throw new NotSupportedException("Color type is not supported or not valid."); - } - - if (!ColorTypes[this.header.ColorType].Contains(this.header.BitDepth)) - { - throw new NotSupportedException("Bit depth is not supported or not valid."); - } + this.header = PngHeader.Parse(data); - if (this.header.FilterMethod != 0) - { - throw new NotSupportedException("The png specification only defines 0 as filter method."); - } + this.header.Validate(); - if (this.header.InterlaceMethod != PngInterlaceMode.None && this.header.InterlaceMethod != PngInterlaceMode.Adam7) - { - throw new NotSupportedException("The png specification only defines 'None' and 'Adam7' as interlaced methods."); - } + pngMetaData.BitDepth = (PngBitDepth)this.header.BitDepth; + pngMetaData.ColorType = this.header.ColorType; this.pngColorType = this.header.ColorType; } @@ -1345,28 +866,18 @@ namespace SixLabors.ImageSharp.Formats.Png /// Reads a text chunk containing image properties from the data. /// /// The metadata to decode to. - /// The containing data. - /// The maximum length to read. - private void ReadTextChunk(ImageMetaData metadata, byte[] data, int length) + /// The containing the data. + private void ReadTextChunk(ImageMetaData metadata, ReadOnlySpan data) { if (this.ignoreMetadata) { return; } - int zeroIndex = 0; - - for (int i = 0; i < length; i++) - { - if (data[i] == 0) - { - zeroIndex = i; - break; - } - } + int zeroIndex = data.IndexOf((byte)0); - string name = this.textEncoding.GetString(data, 0, zeroIndex); - string value = this.textEncoding.GetString(data, zeroIndex + 1, length - zeroIndex - 1); + string name = this.textEncoding.GetString(data.Slice(0, zeroIndex)); + string value = this.textEncoding.GetString(data.Slice(zeroIndex + 1)); metadata.Properties.Add(new ImageProperty(name, value)); } @@ -1382,7 +893,7 @@ namespace SixLabors.ImageSharp.Formats.Png return 0; } - this.currentStream.Read(this.crcBuffer, 0, 4); + this.currentStream.Read(this.buffer, 0, 4); if (this.TryReadChunk(out PngChunk chunk)) { @@ -1415,9 +926,7 @@ namespace SixLabors.ImageSharp.Formats.Png return true; } - int length = this.ReadChunkLength(); - - if (length == -1) + if (!this.TryReadChunkLength(out int length)) { chunk = default; @@ -1431,9 +940,7 @@ namespace SixLabors.ImageSharp.Formats.Png // That lets us read one byte at a time until we reach a known chunk. this.currentStream.Position -= 3; - length = this.ReadChunkLength(); - - if (length == -1) + if (!this.TryReadChunkLength(out length)) { chunk = default; @@ -1471,13 +978,17 @@ namespace SixLabors.ImageSharp.Formats.Png /// The . private void ValidateChunk(in PngChunk chunk) { + Span chunkType = stackalloc byte[4]; + + BinaryPrimitives.WriteUInt32BigEndian(chunkType, (uint)chunk.Type); + this.crc.Reset(); - this.crc.Update(this.chunkTypeBuffer); + this.crc.Update(chunkType); this.crc.Update(chunk.Data.GetSpan()); if (this.crc.Value != chunk.Crc) { - string chunkTypeName = Encoding.UTF8.GetString(this.chunkTypeBuffer, 0, 4); + string chunkTypeName = Encoding.ASCII.GetString(chunkType); throw new ImageFormatException($"CRC Error. PNG {chunkTypeName} chunk is corrupt!"); } @@ -1491,14 +1002,9 @@ namespace SixLabors.ImageSharp.Formats.Png /// private uint ReadChunkCrc() { - int numBytes = this.currentStream.Read(this.crcBuffer, 0, 4); - - if (numBytes >= 1 && numBytes <= 3) - { - throw new ImageFormatException("Image stream is not valid!"); - } - - return BinaryPrimitives.ReadUInt32BigEndian(this.crcBuffer); + return this.currentStream.Read(this.buffer, 0, 4) == 4 + ? BinaryPrimitives.ReadUInt32BigEndian(this.buffer) + : throw new ImageFormatException("Image stream is not valid!"); } /// @@ -1533,54 +1039,29 @@ namespace SixLabors.ImageSharp.Formats.Png /// private PngChunkType ReadChunkType() { - int numBytes = this.currentStream.Read(this.chunkTypeBuffer, 0, 4); - - if (numBytes >= 1 && numBytes <= 3) - { - throw new ImageFormatException("Image stream is not valid!"); - } - - return (PngChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.chunkTypeBuffer.AsSpan()); + return this.currentStream.Read(this.buffer, 0, 4) == 4 + ? (PngChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer) + : throw new ImageFormatException("Invalid PNG data."); } /// - /// Calculates the length of the given chunk. + /// Attempts to read the length of the next chunk. /// - /// - /// Thrown if the input stream is not valid. - /// - private int ReadChunkLength() + /// + /// Whether the the length was read. + /// + private bool TryReadChunkLength(out int result) { - int numBytes = this.currentStream.Read(this.chunkLengthBuffer, 0, 4); - - if (numBytes < 4) + if (this.currentStream.Read(this.buffer, 0, 4) == 4) { - return -1; + result = BinaryPrimitives.ReadInt32BigEndian(this.buffer); + + return true; } - return BinaryPrimitives.ReadInt32BigEndian(this.chunkLengthBuffer); - } + result = default; - /// - /// Returns the correct number of columns for each interlaced pass. - /// - /// Th current pass index - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int ComputeColumnsAdam7(int passIndex) - { - int width = this.header.Width; - switch (passIndex) - { - case 0: return (width + 7) / 8; - case 1: return (width + 3) / 8; - case 2: return (width + 3) / 4; - case 3: return (width + 1) / 4; - case 4: return (width + 1) / 2; - case 5: return width / 2; - case 6: return width; - default: throw new ArgumentException($"Not a valid pass index: {passIndex}"); - } + return false; } private void SwapBuffers() diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs index f47a6518f0..96e97a305f 100644 --- a/src/ImageSharp/Formats/Png/PngEncoder.cs +++ b/src/ImageSharp/Formats/Png/PngEncoder.cs @@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Gets or sets the filter method. /// - public PngFilterMethod FilterMethod { get; set; } = PngFilterMethod.Paeth; + public PngFilterMethod? FilterMethod { get; set; } /// /// Gets or sets the compression level 1-9. diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index e4d2fc510d..292de007ca 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -2,10 +2,15 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Buffers.Binary; +using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Common.Helpers; +using SixLabors.ImageSharp.Formats.Png.Chunks; using SixLabors.ImageSharp.Formats.Png.Filters; using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Memory; @@ -21,8 +26,28 @@ namespace SixLabors.ImageSharp.Formats.Png /// internal sealed class PngEncoderCore : IDisposable { + /// + /// The dictionary of available color types. + /// + private static readonly Dictionary ColorTypes = new Dictionary() + { + [PngColorType.Grayscale] = new byte[] { 1, 2, 4, 8, 16 }, + [PngColorType.Rgb] = new byte[] { 8, 16 }, + [PngColorType.Palette] = new byte[] { 1, 2, 4, 8 }, + [PngColorType.GrayscaleWithAlpha] = new byte[] { 8, 16 }, + [PngColorType.RgbWithAlpha] = new byte[] { 8, 16 } + }; + + /// + /// Used the manage memory allocations. + /// private readonly MemoryAllocator memoryAllocator; + /// + /// The configuration instance for the decoding operation + /// + private Configuration configuration; + /// /// The maximum block size, defaults at 64k for uncompressed blocks. /// @@ -158,7 +183,11 @@ namespace SixLabors.ImageSharp.Formats.Png this.memoryAllocator = memoryAllocator; this.pngBitDepth = options.BitDepth; this.pngColorType = options.ColorType; - this.pngFilterMethod = options.FilterMethod; + + // Specification recommends default filter method None for paletted images and Paeth for others. + this.pngFilterMethod = options.FilterMethod ?? (options.ColorType.Equals(PngColorType.Palette) + ? PngFilterMethod.None + : PngFilterMethod.Paeth); this.compressionLevel = options.CompressionLevel; this.gamma = options.Gamma; this.quantizer = options.Quantizer; @@ -177,6 +206,7 @@ namespace SixLabors.ImageSharp.Formats.Png Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); + this.configuration = image.GetConfiguration(); this.width = image.Width; this.height = image.Height; @@ -189,28 +219,38 @@ namespace SixLabors.ImageSharp.Formats.Png this.pngBitDepth = this.pngBitDepth ?? pngMetaData.BitDepth; this.use16Bit = this.pngBitDepth.Equals(PngBitDepth.Bit16); + // Ensure we are not allowing impossible combinations. + if (!ColorTypes.ContainsKey(this.pngColorType.Value)) + { + throw new NotSupportedException("Color type is not supported or not valid."); + } + stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length); QuantizedFrame quantized = null; - ReadOnlySpan quantizedPixelsSpan = default; if (this.pngColorType == PngColorType.Palette) { - byte bits; + byte bits = (byte)this.pngBitDepth; + if (!ColorTypes[this.pngColorType.Value].Contains(bits)) + { + throw new NotSupportedException("Bit depth is not supported or not valid."); + } // Use the metadata to determine what quantization depth to use if no quantizer has been set. if (this.quantizer == null) { - bits = (byte)Math.Min(8u, (short)this.pngBitDepth); - int colorSize = ImageMaths.GetColorCountForBitDepth(bits); - this.quantizer = new WuQuantizer(colorSize); + this.quantizer = new WuQuantizer(ImageMaths.GetColorCountForBitDepth(bits)); } // Create quantized frame returning the palette and set the bit depth. - quantized = this.quantizer.CreateFrameQuantizer().QuantizeFrame(image.Frames.RootFrame); - quantizedPixelsSpan = quantized.GetPixelSpan(); - bits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8); + quantized = this.quantizer.CreateFrameQuantizer(image.GetConfiguration()) + .QuantizeFrame(image.Frames.RootFrame); + byte quantizedBits = (byte)ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length).Clamp(1, 8); + bits = Math.Max(bits, quantizedBits); // Png only supports in four pixel depths: 1, 2, 4, and 8 bits when using the PLTE chunk + // We check again for the bit depth as the bit depth of the color palette from a given quantizer might not + // be within the acceptable range. if (bits == 3) { bits = 4; @@ -224,7 +264,11 @@ namespace SixLabors.ImageSharp.Formats.Png } else { - this.bitDepth = (byte)(this.use16Bit ? 16 : 8); + this.bitDepth = (byte)this.pngBitDepth; + if (!ColorTypes[this.pngColorType.Value].Contains(this.bitDepth)) + { + throw new NotSupportedException("Bit depth is not supported or not valid."); + } } this.bytesPerPixel = this.CalculateBytesPerPixel(); @@ -246,10 +290,15 @@ namespace SixLabors.ImageSharp.Formats.Png this.WritePaletteChunk(stream, quantized); } + if (pngMetaData.HasTrans) + { + this.WriteTransparencyChunk(stream, pngMetaData); + } + this.WritePhysicalChunk(stream, metaData); this.WriteGammaChunk(stream); this.WriteExifChunk(stream, metaData); - this.WriteDataChunks(image.Frames.RootFrame, quantizedPixelsSpan, stream); + this.WriteDataChunks(image.Frames.RootFrame, quantized, stream); this.WriteEndChunk(stream); stream.Flush(); @@ -276,36 +325,58 @@ namespace SixLabors.ImageSharp.Formats.Png private void CollectGrayscaleBytes(ReadOnlySpan rowSpan) where TPixel : struct, IPixel { - // Use ITU-R recommendation 709 to match libpng. - const float RX = .2126F; - const float GX = .7152F; - const float BX = .0722F; + ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); Span rawScanlineSpan = this.rawScanline.GetSpan(); + ref byte rawScanlineSpanRef = ref MemoryMarshal.GetReference(rawScanlineSpan); if (this.pngColorType.Equals(PngColorType.Grayscale)) { - // TODO: Realistically we should support 1, 2, 4, 8, and 16 bit grayscale images. - // we currently do the other types via palette. Maybe RC as I don't understand how the data is packed yet - // for 1, 2, and 4 bit grayscale images. if (this.use16Bit) { // 16 bit grayscale - Rgb48 rgb = default; - for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 2) + using (IMemoryOwner luminanceBuffer = this.memoryAllocator.Allocate(rowSpan.Length)) { - rowSpan[x].ToRgb48(ref rgb); - ushort luminance = (ushort)((RX * rgb.R) + (GX * rgb.G) + (BX * rgb.B)); - BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance); + Span luminanceSpan = luminanceBuffer.GetSpan(); + ref Gray16 luminanceRef = ref MemoryMarshal.GetReference(luminanceSpan); + PixelOperations.Instance.ToGray16(this.configuration, rowSpan, luminanceSpan); + + // Can't map directly to byte array as it's big endian. + for (int x = 0, o = 0; x < luminanceSpan.Length; x++, o += 2) + { + Gray16 luminance = Unsafe.Add(ref luminanceRef, x); + BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance.PackedValue); + } } } else { - // 8 bit grayscale - Rgb24 rgb = default; - for (int x = 0; x < rowSpan.Length; x++) + if (this.bitDepth == 8) { - rowSpan[x].ToRgb24(ref rgb); - rawScanlineSpan[x] = (byte)((RX * rgb.R) + (GX * rgb.G) + (BX * rgb.B)); + // 8 bit grayscale + PixelOperations.Instance.ToGray8Bytes( + this.configuration, + rowSpan, + rawScanlineSpan, + rowSpan.Length); + } + else + { + // 1, 2, and 4 bit grayscale + using (IManagedByteBuffer temp = this.memoryAllocator.AllocateManagedByteBuffer( + rowSpan.Length, + AllocationOptions.Clean)) + { + int scaleFactor = 255 / (ImageMaths.GetColorCountForBitDepth(this.bitDepth) - 1); + Span tempSpan = temp.GetSpan(); + + // We need to first create an array of luminance bytes then scale them down to the correct bit depth. + PixelOperations.Instance.ToGray8Bytes( + this.configuration, + rowSpan, + tempSpan, + rowSpan.Length); + this.ScaleDownFrom8BitArray(tempSpan, rawScanlineSpan, this.bitDepth, scaleFactor); + } } } } @@ -314,24 +385,34 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.use16Bit) { // 16 bit grayscale + alpha - Rgba64 rgba = default; - for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 4) + // TODO: Should we consider in the future a GrayAlpha32 type. + using (IMemoryOwner rgbaBuffer = this.memoryAllocator.Allocate(rowSpan.Length)) { - rowSpan[x].ToRgba64(ref rgba); - ushort luminance = (ushort)((RX * rgba.R) + (GX * rgba.G) + (BX * rgba.B)); - BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance); - BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgba.A); + Span rgbaSpan = rgbaBuffer.GetSpan(); + ref Rgba64 rgbaRef = ref MemoryMarshal.GetReference(rgbaSpan); + PixelOperations.Instance.ToRgba64(this.configuration, rowSpan, rgbaSpan); + + // Can't map directly to byte array as it's big endian. + for (int x = 0, o = 0; x < rgbaSpan.Length; x++, o += 4) + { + Rgba64 rgba = Unsafe.Add(ref rgbaRef, x); + ushort luminance = ImageMaths.Get16BitBT709Luminance(rgba.R, rgba.G, rgba.B); + BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance); + BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgba.A); + } } } else { // 8 bit grayscale + alpha + // TODO: Should we consider in the future a GrayAlpha16 type. Rgba32 rgba = default; for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 2) { - rowSpan[x].ToRgba32(ref rgba); - rawScanlineSpan[o] = (byte)((RX * rgba.R) + (GX * rgba.G) + (BX * rgba.B)); - rawScanlineSpan[o + 1] = rgba.A; + Unsafe.Add(ref rowSpanRef, x).ToRgba32(ref rgba); + Unsafe.Add(ref rawScanlineSpanRef, o) = + ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + Unsafe.Add(ref rawScanlineSpanRef, o + 1) = rgba.A; } } } @@ -352,28 +433,43 @@ namespace SixLabors.ImageSharp.Formats.Png case 4: { // 8 bit Rgba - PixelOperations.Instance.ToRgba32Bytes(rowSpan, rawScanlineSpan, this.width); + PixelOperations.Instance.ToRgba32Bytes( + this.configuration, + rowSpan, + rawScanlineSpan, + this.width); break; } case 3: { // 8 bit Rgb - PixelOperations.Instance.ToRgb24Bytes(rowSpan, rawScanlineSpan, this.width); + PixelOperations.Instance.ToRgb24Bytes( + this.configuration, + rowSpan, + rawScanlineSpan, + this.width); break; } case 8: { // 16 bit Rgba - Rgba64 rgba = default; - for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 8) + using (IMemoryOwner rgbaBuffer = this.memoryAllocator.Allocate(rowSpan.Length)) { - rowSpan[x].ToRgba64(ref rgba); - BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), rgba.R); - BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgba.G); - BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 4, 2), rgba.B); - BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 6, 2), rgba.A); + Span rgbaSpan = rgbaBuffer.GetSpan(); + ref Rgba64 rgbaRef = ref MemoryMarshal.GetReference(rgbaSpan); + PixelOperations.Instance.ToRgba64(this.configuration, rowSpan, rgbaSpan); + + // Can't map directly to byte array as it's big endian. + for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 8) + { + Rgba64 rgba = Unsafe.Add(ref rgbaRef, x); + BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), rgba.R); + BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgba.G); + BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 4, 2), rgba.B); + BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 6, 2), rgba.A); + } } break; @@ -382,13 +478,20 @@ namespace SixLabors.ImageSharp.Formats.Png default: { // 16 bit Rgb - Rgb48 rgb = default; - for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 6) + using (IMemoryOwner rgbBuffer = this.memoryAllocator.Allocate(rowSpan.Length)) { - rowSpan[x].ToRgb48(ref rgb); - BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), rgb.R); - BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgb.G); - BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 4, 2), rgb.B); + Span rgbSpan = rgbBuffer.GetSpan(); + ref Rgb48 rgbRef = ref MemoryMarshal.GetReference(rgbSpan); + PixelOperations.Instance.ToRgb48(this.configuration, rowSpan, rgbSpan); + + // Can't map directly to byte array as it's big endian. + for (int x = 0, o = 0; x < rowSpan.Length; x++, o += 6) + { + Rgb48 rgb = Unsafe.Add(ref rgbRef, x); + BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), rgb.R); + BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 2, 2), rgb.G); + BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o + 4, 2), rgb.B); + } } break; @@ -402,18 +505,25 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// The pixel format. /// The row span. - /// The span of quantized pixels. Can be null. + /// The quantized pixels. Can be null. /// The row. /// The - private IManagedByteBuffer EncodePixelRow(ReadOnlySpan rowSpan, ReadOnlySpan quantizedPixelsSpan, int row) + private IManagedByteBuffer EncodePixelRow(ReadOnlySpan rowSpan, QuantizedFrame quantized, int row) where TPixel : struct, IPixel { switch (this.pngColorType) { case PngColorType.Palette: - int stride = this.rawScanline.Length(); - quantizedPixelsSpan.Slice(row * stride, stride).CopyTo(this.rawScanline.GetSpan()); + if (this.bitDepth < 8) + { + this.ScaleDownFrom8BitArray(quantized.GetRowSpan(row), this.rawScanline.GetSpan(), this.bitDepth); + } + else + { + int stride = this.rawScanline.Length(); + quantized.GetPixelSpan().Slice(row * stride, stride).CopyTo(this.rawScanline.GetSpan()); + } break; case PngColorType.Grayscale: @@ -538,16 +648,9 @@ namespace SixLabors.ImageSharp.Formats.Png /// The . private void WriteHeaderChunk(Stream stream, in PngHeader header) { - BinaryPrimitives.WriteInt32BigEndian(this.chunkDataBuffer.AsSpan(0, 4), header.Width); - BinaryPrimitives.WriteInt32BigEndian(this.chunkDataBuffer.AsSpan(4, 4), header.Height); - - this.chunkDataBuffer[8] = header.BitDepth; - this.chunkDataBuffer[9] = (byte)header.ColorType; - this.chunkDataBuffer[10] = header.CompressionMethod; - this.chunkDataBuffer[11] = header.FilterMethod; - this.chunkDataBuffer[12] = (byte)header.InterlaceMethod; + header.WriteTo(this.chunkDataBuffer); - this.WriteChunk(stream, PngChunkType.Header, this.chunkDataBuffer, 0, 13); + this.WriteChunk(stream, PngChunkType.Header, this.chunkDataBuffer, 0, PngHeader.Size); } /// @@ -563,16 +666,17 @@ namespace SixLabors.ImageSharp.Formats.Png TPixel[] palette = quantized.Palette; int paletteLength = Math.Min(palette.Length, 256); int colorTableLength = paletteLength * 3; - Rgba32 rgba = default; bool anyAlpha = false; using (IManagedByteBuffer colorTable = this.memoryAllocator.AllocateManagedByteBuffer(colorTableLength)) using (IManagedByteBuffer alphaTable = this.memoryAllocator.AllocateManagedByteBuffer(paletteLength)) { - Span colorTableSpan = colorTable.GetSpan(); - Span alphaTableSpan = alphaTable.GetSpan(); + ref byte colorTableRef = ref MemoryMarshal.GetReference(colorTable.GetSpan()); + ref byte alphaTableRef = ref MemoryMarshal.GetReference(alphaTable.GetSpan()); Span quantizedSpan = quantized.GetPixelSpan(); + Rgba32 rgba = default; + for (int i = 0; i < paletteLength; i++) { if (quantizedSpan.IndexOf((byte)i) > -1) @@ -582,9 +686,9 @@ namespace SixLabors.ImageSharp.Formats.Png byte alpha = rgba.A; - colorTableSpan[offset] = rgba.R; - colorTableSpan[offset + 1] = rgba.G; - colorTableSpan[offset + 2] = rgba.B; + Unsafe.Add(ref colorTableRef, offset) = rgba.R; + Unsafe.Add(ref colorTableRef, offset + 1) = rgba.G; + Unsafe.Add(ref colorTableRef, offset + 2) = rgba.B; if (alpha > this.threshold) { @@ -592,7 +696,7 @@ namespace SixLabors.ImageSharp.Formats.Png } anyAlpha = anyAlpha || alpha < byte.MaxValue; - alphaTableSpan[i] = alpha; + Unsafe.Add(ref alphaTableRef, i) = alpha; } } @@ -601,7 +705,7 @@ namespace SixLabors.ImageSharp.Formats.Png // Write the transparency data if (anyAlpha) { - this.WriteChunk(stream, PngChunkType.PaletteAlpha, alphaTable.Array, 0, paletteLength); + this.WriteChunk(stream, PngChunkType.Transparency, alphaTable.Array, 0, paletteLength); } } } @@ -613,51 +717,9 @@ namespace SixLabors.ImageSharp.Formats.Png /// The image meta data. private void WritePhysicalChunk(Stream stream, ImageMetaData meta) { - // The pHYs chunk specifies the intended pixel size or aspect ratio for display of the image. It contains: - // Pixels per unit, X axis: 4 bytes (unsigned integer) - // Pixels per unit, Y axis: 4 bytes (unsigned integer) - // Unit specifier: 1 byte - // - // The following values are legal for the unit specifier: - // 0: unit is unknown - // 1: unit is the meter - // - // When the unit specifier is 0, the pHYs chunk defines pixel aspect ratio only; the actual size of the pixels remains unspecified. - Span hResolution = this.chunkDataBuffer.AsSpan(0, 4); - Span vResolution = this.chunkDataBuffer.AsSpan(4, 4); - - switch (meta.ResolutionUnits) - { - case PixelResolutionUnit.AspectRatio: - - this.chunkDataBuffer[8] = 0; - BinaryPrimitives.WriteInt32BigEndian(hResolution, (int)Math.Round(meta.HorizontalResolution)); - BinaryPrimitives.WriteInt32BigEndian(vResolution, (int)Math.Round(meta.VerticalResolution)); - break; - - case PixelResolutionUnit.PixelsPerInch: - - this.chunkDataBuffer[8] = 1; // Per meter - BinaryPrimitives.WriteInt32BigEndian(hResolution, (int)Math.Round(UnitConverter.InchToMeter(meta.HorizontalResolution))); - BinaryPrimitives.WriteInt32BigEndian(vResolution, (int)Math.Round(UnitConverter.InchToMeter(meta.VerticalResolution))); - break; - - case PixelResolutionUnit.PixelsPerCentimeter: - - this.chunkDataBuffer[8] = 1; // Per meter - BinaryPrimitives.WriteInt32BigEndian(hResolution, (int)Math.Round(UnitConverter.CmToMeter(meta.HorizontalResolution))); - BinaryPrimitives.WriteInt32BigEndian(vResolution, (int)Math.Round(UnitConverter.CmToMeter(meta.VerticalResolution))); - break; - - default: - - this.chunkDataBuffer[8] = 1; // Per meter - BinaryPrimitives.WriteInt32BigEndian(hResolution, (int)Math.Round(meta.HorizontalResolution)); - BinaryPrimitives.WriteInt32BigEndian(vResolution, (int)Math.Round(meta.VerticalResolution)); - break; - } + PhysicalChunkData.FromMetadata(meta).WriteTo(this.chunkDataBuffer); - this.WriteChunk(stream, PngChunkType.Physical, this.chunkDataBuffer, 0, 9); + this.WriteChunk(stream, PngChunkType.Physical, this.chunkDataBuffer, 0, PhysicalChunkData.Size); } /// @@ -691,14 +753,59 @@ namespace SixLabors.ImageSharp.Formats.Png } } + /// + /// Writes the transparency chunk to the stream + /// + /// The containing image data. + /// The image meta data. + private void WriteTransparencyChunk(Stream stream, PngMetaData pngMetaData) + { + Span alpha = this.chunkDataBuffer.AsSpan(); + if (pngMetaData.ColorType.Equals(PngColorType.Rgb)) + { + if (pngMetaData.TransparentRgb48.HasValue && this.use16Bit) + { + Rgb48 rgb = pngMetaData.TransparentRgb48.Value; + BinaryPrimitives.WriteUInt16LittleEndian(alpha, rgb.R); + BinaryPrimitives.WriteUInt16LittleEndian(alpha.Slice(2, 2), rgb.G); + BinaryPrimitives.WriteUInt16LittleEndian(alpha.Slice(4, 2), rgb.B); + + this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 6); + } + else if (pngMetaData.TransparentRgb24.HasValue) + { + alpha.Clear(); + Rgb24 rgb = pngMetaData.TransparentRgb24.Value; + alpha[1] = rgb.R; + alpha[3] = rgb.G; + alpha[5] = rgb.B; + this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 6); + } + } + else if (pngMetaData.ColorType.Equals(PngColorType.Grayscale)) + { + if (pngMetaData.TransparentGray16.HasValue && this.use16Bit) + { + BinaryPrimitives.WriteUInt16LittleEndian(alpha, pngMetaData.TransparentGray16.Value.PackedValue); + this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2); + } + else if (pngMetaData.TransparentGray8.HasValue) + { + alpha.Clear(); + alpha[1] = pngMetaData.TransparentGray8.Value.PackedValue; + this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2); + } + } + } + /// /// Writes the pixel information to the stream. /// /// The pixel format. /// The image. - /// The span of quantized pixel data. Can be null. + /// The quantized pixel data. Can be null. /// The stream. - private void WriteDataChunks(ImageFrame pixels, ReadOnlySpan quantizedPixelsSpan, Stream stream) + private void WriteDataChunks(ImageFrame pixels, QuantizedFrame quantized, Stream stream) where TPixel : struct, IPixel { this.bytesPerScanline = this.CalculateScanlineLength(this.width); @@ -714,26 +821,22 @@ namespace SixLabors.ImageSharp.Formats.Png break; case PngFilterMethod.Sub: - this.sub = this.memoryAllocator.AllocateManagedByteBuffer(resultLength, AllocationOptions.Clean); break; case PngFilterMethod.Up: - this.up = this.memoryAllocator.AllocateManagedByteBuffer(resultLength, AllocationOptions.Clean); break; case PngFilterMethod.Average: - this.average = this.memoryAllocator.AllocateManagedByteBuffer(resultLength, AllocationOptions.Clean); break; case PngFilterMethod.Paeth: - this.paeth = this.memoryAllocator.AllocateManagedByteBuffer(resultLength, AllocationOptions.Clean); break; - case PngFilterMethod.Adaptive: + case PngFilterMethod.Adaptive: this.sub = this.memoryAllocator.AllocateManagedByteBuffer(resultLength, AllocationOptions.Clean); this.up = this.memoryAllocator.AllocateManagedByteBuffer(resultLength, AllocationOptions.Clean); this.average = this.memoryAllocator.AllocateManagedByteBuffer(resultLength, AllocationOptions.Clean); @@ -750,7 +853,7 @@ namespace SixLabors.ImageSharp.Formats.Png { for (int y = 0; y < this.height; y++) { - IManagedByteBuffer r = this.EncodePixelRow((ReadOnlySpan)pixels.GetPixelRowSpan(y), quantizedPixelsSpan, y); + IManagedByteBuffer r = this.EncodePixelRow((ReadOnlySpan)pixels.GetPixelRowSpan(y), quantized, y); deflateStream.Write(r.Array, 0, resultLength); IManagedByteBuffer temp = this.rawScanline; @@ -830,6 +933,48 @@ namespace SixLabors.ImageSharp.Formats.Png stream.Write(this.buffer, 0, 4); // write the crc } + /// + /// Packs the given 8 bit array into and array of depths. + /// + /// The source span in 8 bits. + /// The resultant span in . + /// The bit depth. + /// The scaling factor. + private void ScaleDownFrom8BitArray(ReadOnlySpan source, Span result, int bits, float scale = 1) + { + ref byte sourceRef = ref MemoryMarshal.GetReference(source); + ref byte resultRef = ref MemoryMarshal.GetReference(result); + + int shift = 8 - bits; + byte mask = (byte)(0xFF >> shift); + byte shift0 = (byte)shift; + int v = 0; + int resultOffset = 0; + + for (int i = 0; i < source.Length; i++) + { + int value = ((int)MathF.Round(Unsafe.Add(ref sourceRef, i) / scale)) & mask; + v |= value << shift; + + if (shift == 0) + { + shift = shift0; + Unsafe.Add(ref resultRef, resultOffset) = (byte)v; + resultOffset++; + v = 0; + } + else + { + shift -= bits; + } + } + + if (shift != shift0) + { + Unsafe.Add(ref resultRef, resultOffset) = (byte)v; + } + } + /// /// Calculates the scanline length. /// @@ -851,4 +996,4 @@ namespace SixLabors.ImageSharp.Formats.Png return scanlineLength / mod; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Png/PngHeader.cs b/src/ImageSharp/Formats/Png/PngHeader.cs index df85642bed..ea43ba96a5 100644 --- a/src/ImageSharp/Formats/Png/PngHeader.cs +++ b/src/ImageSharp/Formats/Png/PngHeader.cs @@ -1,6 +1,9 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; +using System.Buffers.Binary; + namespace SixLabors.ImageSharp.Formats.Png { /// @@ -8,6 +11,8 @@ namespace SixLabors.ImageSharp.Formats.Png /// internal readonly struct PngHeader { + public const int Size = 13; + public PngHeader( int width, int height, @@ -74,5 +79,68 @@ namespace SixLabors.ImageSharp.Formats.Png /// Two values are currently defined: 0 (no interlace) or 1 (Adam7 interlace). /// public PngInterlaceMode InterlaceMethod { get; } + + /// + /// Validates the png header. + /// + /// + /// Thrown if the image does pass validation. + /// + public void Validate() + { + if (!PngConstants.ColorTypes.TryGetValue(this.ColorType, out byte[] supportedBitDepths)) + { + throw new NotSupportedException($"Invalid or unsupported color type. Was '{this.ColorType}'."); + } + + if (supportedBitDepths.AsSpan().IndexOf(this.BitDepth) == -1) + { + throw new NotSupportedException($"Invalid or unsupported bit depth. Was '{this.BitDepth}'."); + } + + if (this.FilterMethod != 0) + { + throw new NotSupportedException($"Invalid filter method. Expected 0. Was '{this.FilterMethod}'."); + } + + // The png specification only defines 'None' and 'Adam7' as interlaced methods. + if (this.InterlaceMethod != PngInterlaceMode.None && this.InterlaceMethod != PngInterlaceMode.Adam7) + { + throw new NotSupportedException($"Invalid interlace method. Expected 'None' or 'Adam7'. Was '{this.InterlaceMethod}'."); + } + } + + /// + /// Writes the header to the given buffer. + /// + /// The buffer to write to. + public void WriteTo(Span buffer) + { + BinaryPrimitives.WriteInt32BigEndian(buffer.Slice(0, 4), this.Width); + BinaryPrimitives.WriteInt32BigEndian(buffer.Slice(4, 4), this.Height); + + buffer[8] = this.BitDepth; + buffer[9] = (byte)this.ColorType; + buffer[10] = this.CompressionMethod; + buffer[11] = this.FilterMethod; + buffer[12] = (byte)this.InterlaceMethod; + } + + /// + /// Parses the PngHeader from the given data buffer. + /// + /// The data to parse. + /// The parsed PngHeader. + public static PngHeader Parse(ReadOnlySpan data) + { + return new PngHeader( + width: BinaryPrimitives.ReadInt32BigEndian(data.Slice(0, 4)), + height: BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4)), + bitDepth: data[8], + colorType: (PngColorType)data[9], + compressionMethod: data[10], + filterMethod: data[11], + interlaceMethod: (PngInterlaceMode)data[12]); + } } } diff --git a/src/ImageSharp/Formats/Png/PngMetaData.cs b/src/ImageSharp/Formats/Png/PngMetaData.cs index 9c76765146..d5ab3d2554 100644 --- a/src/ImageSharp/Formats/Png/PngMetaData.cs +++ b/src/ImageSharp/Formats/Png/PngMetaData.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Formats.Png { /// @@ -24,6 +26,11 @@ namespace SixLabors.ImageSharp.Formats.Png this.BitDepth = other.BitDepth; this.ColorType = other.ColorType; this.Gamma = other.Gamma; + this.HasTrans = other.HasTrans; + this.TransparentGray8 = other.TransparentGray8; + this.TransparentGray16 = other.TransparentGray16; + this.TransparentRgb24 = other.TransparentRgb24; + this.TransparentRgb48 = other.TransparentRgb48; } /// @@ -42,6 +49,31 @@ namespace SixLabors.ImageSharp.Formats.Png /// public float Gamma { get; set; } + /// + /// Gets or sets the Rgb 24 transparent color. This represents any color in an 8 bit Rgb24 encoded png that should be transparent + /// + public Rgb24? TransparentRgb24 { get; set; } + + /// + /// Gets or sets the Rgb 48 transparent color. This represents any color in a 16 bit Rgb24 encoded png that should be transparent + /// + public Rgb48? TransparentRgb48 { get; set; } + + /// + /// Gets or sets the 8 bit grayscale transparent color. This represents any color in an 8 bit grayscale encoded png that should be transparent + /// + public Gray8? TransparentGray8 { get; set; } + + /// + /// Gets or sets the 16 bit grayscale transparent color. This represents any color in a 16 bit grayscale encoded png that should be transparent + /// + public Gray16? TransparentGray16 { get; set; } + + /// + /// Gets or sets a value indicating whether the image has transparency chunk and markers were decoded + /// + public bool HasTrans { get; set; } + /// public IDeepCloneable DeepClone() => new PngMetaData(this); } diff --git a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs new file mode 100644 index 0000000000..4242b2d554 --- /dev/null +++ b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs @@ -0,0 +1,578 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Png +{ + /// + /// Provides methods to allow the decoding of raw scanlines to image rows of different pixel formats. + /// TODO: We should make this a stateful class or struct to reduce the number of arguments on methods (most are invariant). + /// + internal static class PngScanlineProcessor + { + public static void ProcessGrayscaleScanline( + in PngHeader header, + ReadOnlySpan scanlineSpan, + Span rowSpan, + bool hasTrans, + Gray16 luminance16Trans, + Gray8 luminanceTrans) + where TPixel : struct, IPixel + { + TPixel pixel = default; + ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); + ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); + int scaleFactor = 255 / (ImageMaths.GetColorCountForBitDepth(header.BitDepth) - 1); + + if (!hasTrans) + { + if (header.BitDepth == 16) + { + for (int x = 0, o = 0; x < header.Width; x++, o += 2) + { + ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); + pixel.FromGray16(new Gray16(luminance)); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + else + { + for (int x = 0; x < header.Width; x++) + { + byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * scaleFactor); + pixel.FromGray8(new Gray8(luminance)); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + + return; + } + + if (header.BitDepth == 16) + { + Rgba64 rgba64 = default; + for (int x = 0, o = 0; x < header.Width; x++, o += 2) + { + ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); + rgba64.R = luminance; + rgba64.G = luminance; + rgba64.B = luminance; + rgba64.A = luminance.Equals(luminance16Trans.PackedValue) ? ushort.MinValue : ushort.MaxValue; + + pixel.FromRgba64(rgba64); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + else + { + byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor); + Rgba32 rgba32 = default; + for (int x = 0; x < header.Width; x++) + { + byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * scaleFactor); + rgba32.R = luminance; + rgba32.G = luminance; + rgba32.B = luminance; + rgba32.A = luminance.Equals(scaledLuminanceTrans) ? byte.MinValue : byte.MaxValue; + + pixel.FromRgba32(rgba32); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + } + + public static void ProcessInterlacedGrayscaleScanline( + in PngHeader header, + ReadOnlySpan scanlineSpan, + Span rowSpan, + int pixelOffset, + int increment, + bool hasTrans, + Gray16 luminance16Trans, + Gray8 luminanceTrans) + where TPixel : struct, IPixel + { + TPixel pixel = default; + ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); + ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); + int scaleFactor = 255 / (ImageMaths.GetColorCountForBitDepth(header.BitDepth) - 1); + + if (!hasTrans) + { + if (header.BitDepth == 16) + { + for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += 2) + { + ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); + pixel.FromGray16(new Gray16(luminance)); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + else + { + for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++) + { + byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor); + pixel.FromGray8(new Gray8(luminance)); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + + return; + } + + if (header.BitDepth == 16) + { + Rgba64 rgba64 = default; + for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += 2) + { + ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); + rgba64.R = luminance; + rgba64.G = luminance; + rgba64.B = luminance; + rgba64.A = luminance.Equals(luminance16Trans.PackedValue) ? ushort.MinValue : ushort.MaxValue; + + pixel.FromRgba64(rgba64); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + else + { + byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor); + Rgba32 rgba32 = default; + for (int x = pixelOffset; x < header.Width; x += increment) + { + byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * scaleFactor); + rgba32.R = luminance; + rgba32.G = luminance; + rgba32.B = luminance; + rgba32.A = luminance.Equals(scaledLuminanceTrans) ? byte.MinValue : byte.MaxValue; + + pixel.FromRgba32(rgba32); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + } + + public static void ProcessGrayscaleWithAlphaScanline( + in PngHeader header, + ReadOnlySpan scanlineSpan, + Span rowSpan, + int bytesPerPixel, + int bytesPerSample) + where TPixel : struct, IPixel + { + TPixel pixel = default; + ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); + ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); + + if (header.BitDepth == 16) + { + Rgba64 rgba64 = default; + for (int x = 0, o = 0; x < header.Width; x++, o += 4) + { + ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); + ushort alpha = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); + rgba64.R = luminance; + rgba64.G = luminance; + rgba64.B = luminance; + rgba64.A = alpha; + + pixel.FromRgba64(rgba64); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + else + { + Rgba32 rgba32 = default; + for (int x = 0; x < header.Width; x++) + { + int offset = x * bytesPerPixel; + byte luminance = Unsafe.Add(ref scanlineSpanRef, offset); + byte alpha = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample); + + rgba32.R = luminance; + rgba32.G = luminance; + rgba32.B = luminance; + rgba32.A = alpha; + + pixel.FromRgba32(rgba32); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + } + + public static void ProcessInterlacedGrayscaleWithAlphaScanline( + in PngHeader header, + ReadOnlySpan scanlineSpan, + Span rowSpan, + int pixelOffset, + int increment, + int bytesPerPixel, + int bytesPerSample) + where TPixel : struct, IPixel + { + TPixel pixel = default; + ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); + ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); + + if (header.BitDepth == 16) + { + Rgba64 rgba64 = default; + for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += 4) + { + ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2)); + ushort alpha = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2)); + rgba64.R = luminance; + rgba64.G = luminance; + rgba64.B = luminance; + rgba64.A = alpha; + + pixel.FromRgba64(rgba64); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + else + { + Rgba32 rgba32 = default; + for (int x = pixelOffset; x < header.Width; x += increment) + { + int offset = x * bytesPerPixel; + byte luminance = Unsafe.Add(ref scanlineSpanRef, offset); + byte alpha = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample); + rgba32.R = luminance; + rgba32.G = luminance; + rgba32.B = luminance; + rgba32.A = alpha; + + pixel.FromRgba32(rgba32); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + } + + public static void ProcessPaletteScanline( + in PngHeader header, + ReadOnlySpan scanlineSpan, + Span rowSpan, + ReadOnlySpan palette, + byte[] paletteAlpha) + where TPixel : struct, IPixel + { + TPixel pixel = default; + ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); + ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); + ReadOnlySpan palettePixels = MemoryMarshal.Cast(palette); + ref Rgb24 palettePixelsRef = ref MemoryMarshal.GetReference(palettePixels); + + if (paletteAlpha?.Length > 0) + { + // If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha + // channel and we should try to read it. + Rgba32 rgba = default; + ref byte paletteAlphaRef = ref paletteAlpha[0]; + + for (int x = 0; x < header.Width; x++) + { + int index = Unsafe.Add(ref scanlineSpanRef, x); + rgba.Rgb = Unsafe.Add(ref palettePixelsRef, index); + rgba.A = paletteAlpha.Length > index ? Unsafe.Add(ref paletteAlphaRef, index) : byte.MaxValue; + + pixel.FromRgba32(rgba); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + else + { + for (int x = 0; x < header.Width; x++) + { + int index = Unsafe.Add(ref scanlineSpanRef, x); + Rgb24 rgb = Unsafe.Add(ref palettePixelsRef, index); + + pixel.FromRgb24(rgb); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + } + + public static void ProcessInterlacedPaletteScanline( + in PngHeader header, + ReadOnlySpan scanlineSpan, + Span rowSpan, + int pixelOffset, + int increment, + ReadOnlySpan palette, + byte[] paletteAlpha) + where TPixel : struct, IPixel + { + TPixel pixel = default; + ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); + ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); + ReadOnlySpan palettePixels = MemoryMarshal.Cast(palette); + ref Rgb24 palettePixelsRef = ref MemoryMarshal.GetReference(palettePixels); + + if (paletteAlpha?.Length > 0) + { + // If the alpha palette is not null and has one or more entries, this means, that the image contains an alpha + // channel and we should try to read it. + Rgba32 rgba = default; + ref byte paletteAlphaRef = ref paletteAlpha[0]; + for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++) + { + int index = Unsafe.Add(ref scanlineSpanRef, o); + rgba.A = paletteAlpha.Length > index ? Unsafe.Add(ref paletteAlphaRef, index) : byte.MaxValue; + rgba.Rgb = Unsafe.Add(ref palettePixelsRef, index); + + pixel.FromRgba32(rgba); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + else + { + for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++) + { + int index = Unsafe.Add(ref scanlineSpanRef, o); + Rgb24 rgb = Unsafe.Add(ref palettePixelsRef, index); + + pixel.FromRgb24(rgb); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + } + + public static void ProcessRgbScanline( + Configuration configuration, + in PngHeader header, + ReadOnlySpan scanlineSpan, + Span rowSpan, + int bytesPerPixel, + int bytesPerSample, + bool hasTrans, + Rgb48 rgb48Trans, + Rgb24 rgb24Trans) + where TPixel : struct, IPixel + { + TPixel pixel = default; + ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); + + if (!hasTrans) + { + if (header.BitDepth == 16) + { + Rgb48 rgb48 = default; + for (int x = 0, o = 0; x < header.Width; x++, o += bytesPerPixel) + { + rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); + rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); + rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); + + pixel.FromRgb48(rgb48); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + else + { + PixelOperations.Instance.FromRgb24Bytes(configuration, scanlineSpan, rowSpan, header.Width); + } + + return; + } + + if (header.BitDepth == 16) + { + Rgb48 rgb48 = default; + Rgba64 rgba64 = default; + for (int x = 0, o = 0; x < header.Width; x++, o += bytesPerPixel) + { + rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); + rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); + rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); + + rgba64.Rgb = rgb48; + rgba64.A = rgb48.Equals(rgb48Trans) ? ushort.MinValue : ushort.MaxValue; + + pixel.FromRgba64(rgba64); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + else + { + ReadOnlySpan rgb24Span = MemoryMarshal.Cast(scanlineSpan); + ref Rgb24 rgb24SpanRef = ref MemoryMarshal.GetReference(rgb24Span); + for (int x = 0; x < header.Width; x++) + { + ref readonly Rgb24 rgb24 = ref Unsafe.Add(ref rgb24SpanRef, x); + Rgba32 rgba32 = default; + rgba32.Rgb = rgb24; + rgba32.A = rgb24.Equals(rgb24Trans) ? byte.MinValue : byte.MaxValue; + + pixel.FromRgba32(rgba32); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + } + + public static void ProcessInterlacedRgbScanline( + in PngHeader header, + ReadOnlySpan scanlineSpan, + Span rowSpan, + int pixelOffset, + int increment, + int bytesPerPixel, + int bytesPerSample, + bool hasTrans, + Rgb48 rgb48Trans, + Rgb24 rgb24Trans) + where TPixel : struct, IPixel + { + TPixel pixel = default; + ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); + ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); + + if (header.BitDepth == 16) + { + if (hasTrans) + { + Rgb48 rgb48 = default; + Rgba64 rgba64 = default; + for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += bytesPerPixel) + { + rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); + rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); + rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); + + rgba64.Rgb = rgb48; + rgba64.A = rgb48.Equals(rgb48Trans) ? ushort.MinValue : ushort.MaxValue; + + pixel.FromRgba64(rgba64); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + else + { + Rgb48 rgb48 = default; + for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += bytesPerPixel) + { + rgb48.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); + rgb48.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); + rgb48.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); + + pixel.FromRgb48(rgb48); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + + return; + } + + if (hasTrans) + { + Rgba32 rgba = default; + for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += bytesPerPixel) + { + rgba.R = Unsafe.Add(ref scanlineSpanRef, o); + rgba.G = Unsafe.Add(ref scanlineSpanRef, o + bytesPerSample); + rgba.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * bytesPerSample)); + rgba.A = rgb24Trans.Equals(rgba.Rgb) ? byte.MinValue : byte.MaxValue; + + pixel.FromRgba32(rgba); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + else + { + Rgb24 rgb = default; + for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += bytesPerPixel) + { + rgb.R = Unsafe.Add(ref scanlineSpanRef, o); + rgb.G = Unsafe.Add(ref scanlineSpanRef, o + bytesPerSample); + rgb.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * bytesPerSample)); + + pixel.FromRgb24(rgb); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + } + + public static void ProcessRgbaScanline( + Configuration configuration, + in PngHeader header, + ReadOnlySpan scanlineSpan, + Span rowSpan, + int bytesPerPixel, + int bytesPerSample) + where TPixel : struct, IPixel + { + TPixel pixel = default; + ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); + + if (header.BitDepth == 16) + { + Rgba64 rgba64 = default; + for (int x = 0, o = 0; x < header.Width; x++, o += bytesPerPixel) + { + rgba64.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); + rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); + rgba64.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); + rgba64.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (3 * bytesPerSample), bytesPerSample)); + + pixel.FromRgba64(rgba64); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + else + { + PixelOperations.Instance.FromRgba32Bytes(configuration, scanlineSpan, rowSpan, header.Width); + } + } + + public static void ProcessInterlacedRgbaScanline( + in PngHeader header, + ReadOnlySpan scanlineSpan, + Span rowSpan, + int pixelOffset, + int increment, + int bytesPerPixel, + int bytesPerSample) + where TPixel : struct, IPixel + { + TPixel pixel = default; + ref byte scanlineSpanRef = ref MemoryMarshal.GetReference(scanlineSpan); + ref TPixel rowSpanRef = ref MemoryMarshal.GetReference(rowSpan); + + if (header.BitDepth == 16) + { + Rgba64 rgba64 = default; + for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += bytesPerPixel) + { + rgba64.R = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, bytesPerSample)); + rgba64.G = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + bytesPerSample, bytesPerSample)); + rgba64.B = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (2 * bytesPerSample), bytesPerSample)); + rgba64.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + (3 * bytesPerSample), bytesPerSample)); + + pixel.FromRgba64(rgba64); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + else + { + Rgba32 rgba = default; + for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += bytesPerPixel) + { + rgba.R = Unsafe.Add(ref scanlineSpanRef, o); + rgba.G = Unsafe.Add(ref scanlineSpanRef, o + bytesPerSample); + rgba.B = Unsafe.Add(ref scanlineSpanRef, o + (2 * bytesPerSample)); + rgba.A = Unsafe.Add(ref scanlineSpanRef, o + (3 * bytesPerSample)); + + pixel.FromRgba32(rgba); + Unsafe.Add(ref rowSpanRef, x) = pixel; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs index a92220a595..583175b566 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// /// Delegate to get more data once we've exhausted the current data remaining /// - private Func getData; + private readonly Func getData; /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/IImageInfo.cs b/src/ImageSharp/IImageInfo.cs index 25d5ec7cab..6e64aa679a 100644 --- a/src/ImageSharp/IImageInfo.cs +++ b/src/ImageSharp/IImageInfo.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.MetaData; namespace SixLabors.ImageSharp { /// - /// Encapsulates properties that descibe basic image information including dimensions, pixel type information + /// Encapsulates properties that describe basic image information including dimensions, pixel type information /// and additional metadata /// public interface IImageInfo diff --git a/src/ImageSharp/IO/IFileSystem.cs b/src/ImageSharp/IO/IFileSystem.cs index 088d4abb8b..593c760fcf 100644 --- a/src/ImageSharp/IO/IFileSystem.cs +++ b/src/ImageSharp/IO/IFileSystem.cs @@ -5,7 +5,6 @@ using System.IO; namespace SixLabors.ImageSharp.IO { - #if !NETSTANDARD1_1 /// /// A simple interface representing the filesystem. /// @@ -25,5 +24,4 @@ namespace SixLabors.ImageSharp.IO /// A stream representing the file to open. Stream Create(string path); } -#endif } diff --git a/src/ImageSharp/IO/LocalFileSystem.cs b/src/ImageSharp/IO/LocalFileSystem.cs index 204f5f4e1e..dc5901ff92 100644 --- a/src/ImageSharp/IO/LocalFileSystem.cs +++ b/src/ImageSharp/IO/LocalFileSystem.cs @@ -1,30 +1,19 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; using System.IO; -using System.Text; namespace SixLabors.ImageSharp.IO { - #if !NETSTANDARD1_1 /// /// A wrapper around the local File apis. /// internal class LocalFileSystem : IFileSystem { /// - public Stream OpenRead(string path) - { - return File.OpenRead(path); - } + public Stream OpenRead(string path) => File.OpenRead(path); /// - public Stream Create(string path) - { - return File.Create(path); - } + public Stream Create(string path) => File.Create(path); } -#endif -} +} \ No newline at end of file diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index 894551e08f..ffdab25e24 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; @@ -15,6 +16,29 @@ namespace SixLabors.ImageSharp /// public static partial class Image { + /// + /// Creates an instance backed by an uninitialized memory buffer. + /// This is an optimized creation method intended to be used by decoders. + /// The image might be filled with memory garbage. + /// + /// The pixel type + /// The + /// The width of the image + /// The height of the image + /// The + /// The result + internal static Image CreateUninitialized( + Configuration configuration, + int width, + int height, + ImageMetaData metadata) + where TPixel : struct, IPixel + { + Buffer2D uninitializedMemoryBuffer = + configuration.MemoryAllocator.Allocate2D(width, height); + return new Image(configuration, uninitializedMemoryBuffer.MemorySource, width, height, metadata); + } + /// /// By reading the header on the provided stream this calculates the images format. /// @@ -30,7 +54,7 @@ namespace SixLabors.ImageSharp return null; } - using (IManagedByteBuffer buffer = config.MemoryAllocator.AllocateManagedByteBuffer(maxHeaderSize)) + using (IManagedByteBuffer buffer = config.MemoryAllocator.AllocateManagedByteBuffer(maxHeaderSize, AllocationOptions.Clean)) { long startPosition = stream.Position; stream.Read(buffer.Array, 0, maxHeaderSize); diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 12abf720bd..34927e6e2b 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -174,8 +174,6 @@ namespace SixLabors.ImageSharp } } -#if !NETSTANDARD1_1 - /// /// By reading the header on the provided byte array this calculates the images format. /// @@ -200,18 +198,17 @@ namespace SixLabors.ImageSharp return null; } - IImageFormat format = default; foreach (IImageFormatDetector detector in config.ImageFormatsManager.FormatDetectors) { IImageFormat f = detector.DetectFormat(data); if (f != null) { - format = f; + return f; } } - return format; + return default; } /// @@ -303,6 +300,5 @@ namespace SixLabors.ImageSharp } } } -#endif } } \ No newline at end of file diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index ad8f3426f2..b13cef4824 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -#if !NETSTANDARD1_1 using System; using System.IO; using SixLabors.ImageSharp.Formats; @@ -212,5 +211,4 @@ namespace SixLabors.ImageSharp } } } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs index e8d9ab7547..c2935bed93 100644 --- a/src/ImageSharp/Image.WrapMemory.cs +++ b/src/ImageSharp/Image.WrapMemory.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp public static partial class Image { /// - /// Wraps an existing contigous memory area of 'width'x'height' pixels, + /// Wraps an existing contiguous memory area of 'width' x 'height' pixels, /// allowing to view/manipulate it as an ImageSharp instance. /// /// The pixel type @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp } /// - /// Wraps an existing contigous memory area of 'width'x'height' pixels, + /// Wraps an existing contiguous memory area of 'width' x 'height' pixels, /// allowing to view/manipulate it as an ImageSharp instance. /// /// The pixel type @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp } /// - /// Wraps an existing contigous memory area of 'width'x'height' pixels, + /// Wraps an existing contiguous memory area of 'width' x 'height' pixels, /// allowing to view/manipulate it as an ImageSharp instance. /// The memory is being observed, the caller remains responsible for managing it's lifecycle. /// @@ -79,15 +79,15 @@ namespace SixLabors.ImageSharp } /// - /// Wraps an existing contigous memory area of 'width'x'height' pixels, + /// Wraps an existing contiguous memory area of 'width' x 'height' pixels, /// allowing to view/manipulate it as an ImageSharp instance. - /// The ownership of the is being transfered to the new instance, + /// The ownership of the is being transferred to the new instance, /// meaning that the caller is not allowed to dispose . /// It will be disposed together with the result image. /// /// The pixel type /// The - /// The that is being transfered to the image + /// The that is being transferred to the image /// The width of the memory image /// The height of the memory image /// The @@ -105,15 +105,15 @@ namespace SixLabors.ImageSharp } /// - /// Wraps an existing contigous memory area of 'width'x'height' pixels, + /// Wraps an existing contiguous memory area of 'width' x 'height' pixels, /// allowing to view/manipulate it as an ImageSharp instance. - /// The ownership of the is being transfered to the new instance, + /// The ownership of the is being transferred to the new instance, /// meaning that the caller is not allowed to dispose . /// It will be disposed together with the result image. /// /// The pixel type /// The - /// The that is being transfered to the image + /// The that is being transferred to the image /// The width of the memory image /// The height of the memory image /// An instance @@ -128,14 +128,14 @@ namespace SixLabors.ImageSharp } /// - /// Wraps an existing contigous memory area of 'width'x'height' pixels, + /// Wraps an existing contiguous memory area of 'width' x 'height' pixels, /// allowing to view/manipulate it as an ImageSharp instance. - /// The ownership of the is being transfered to the new instance, + /// The ownership of the is being transferred to the new instance, /// meaning that the caller is not allowed to dispose . /// It will be disposed together with the result image. /// /// The pixel type - /// The that is being transfered to the image + /// The that is being transferred to the image /// The width of the memory image /// The height of the memory image /// An instance diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index bf312cb6f5..cbf93275c8 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/src/ImageSharp/ImageExtensions.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Runtime.InteropServices; using System.Text; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; @@ -17,7 +16,6 @@ namespace SixLabors.ImageSharp /// public static partial class ImageExtensions { -#if !NETSTANDARD1_1 /// /// Writes the image to the given stream using the currently loaded image format. /// @@ -35,7 +33,7 @@ namespace SixLabors.ImageSharp if (format is null) { var sb = new StringBuilder(); - sb.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:"); + sb.AppendLine($"Can't find a format that is associated with the file extension '{ext}'. Registered formats with there extensions include:"); foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats) { sb.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}"); @@ -49,7 +47,7 @@ namespace SixLabors.ImageSharp if (encoder is null) { var sb = new StringBuilder(); - sb.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:"); + sb.AppendLine($"Can't find encoder for file extension '{ext}' using image format '{format.Name}'. Registered encoders include:"); foreach (KeyValuePair enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders) { sb.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}"); @@ -78,7 +76,6 @@ namespace SixLabors.ImageSharp source.Save(fs, encoder); } } -#endif /// /// Writes the image to the given stream using the currently loaded image format. diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs index 5f1aa5a38e..f69ae37574 100644 --- a/src/ImageSharp/ImageFrame{TPixel}.cs +++ b/src/ImageSharp/ImageFrame{TPixel}.cs @@ -2,13 +2,12 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; -using System.Numerics; using System.Runtime.CompilerServices; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.MetaData; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; @@ -22,7 +21,6 @@ namespace SixLabors.ImageSharp public sealed class ImageFrame : IPixelSource, IDisposable where TPixel : struct, IPixel { - private readonly Configuration configuration; private bool isDisposed; /// @@ -85,7 +83,7 @@ namespace SixLabors.ImageSharp Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - this.configuration = configuration; + this.Configuration = configuration; this.MemoryAllocator = configuration.MemoryAllocator; this.PixelBuffer = this.MemoryAllocator.Allocate2D(width, height); this.MetaData = metaData ?? new ImageFrameMetaData(); @@ -119,7 +117,7 @@ namespace SixLabors.ImageSharp Guard.MustBeGreaterThan(height, 0, nameof(height)); Guard.NotNull(metaData, nameof(metaData)); - this.configuration = configuration; + this.Configuration = configuration; this.MemoryAllocator = configuration.MemoryAllocator; this.PixelBuffer = new Buffer2D(memorySource, width, height); this.MetaData = metaData; @@ -135,7 +133,7 @@ namespace SixLabors.ImageSharp Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(source, nameof(source)); - this.configuration = configuration; + this.Configuration = configuration; this.MemoryAllocator = configuration.MemoryAllocator; this.PixelBuffer = this.MemoryAllocator.Allocate2D(source.PixelBuffer.Width, source.PixelBuffer.Height); source.PixelBuffer.GetSpan().CopyTo(this.PixelBuffer.GetSpan()); @@ -147,6 +145,11 @@ namespace SixLabors.ImageSharp /// public MemoryAllocator MemoryAllocator { get; } + /// + /// Gets the instance associated with this . + /// + internal Configuration Configuration { get; } + /// /// Gets the image pixels. Not private as Buffer2D requires an array in its constructor. /// @@ -249,13 +252,13 @@ namespace SixLabors.ImageSharp } /// - public override string ToString() => $"ImageFrame<{typeof(TPixel).Name}>: {this.Width}x{this.Height}"; + public override string ToString() => $"ImageFrame<{typeof(TPixel).Name}>({this.Width}x{this.Height})"; /// /// Clones the current instance. /// /// The - internal ImageFrame Clone() => this.Clone(this.configuration); + internal ImageFrame Clone() => this.Clone(this.Configuration); /// /// Clones the current instance. @@ -270,7 +273,7 @@ namespace SixLabors.ImageSharp /// The pixel format. /// The internal ImageFrame CloneAs() - where TPixel2 : struct, IPixel => this.CloneAs(this.configuration); + where TPixel2 : struct, IPixel => this.CloneAs(this.Configuration); /// /// Returns a copy of the image frame in the given pixel format. @@ -288,20 +291,18 @@ namespace SixLabors.ImageSharp var target = new ImageFrame(configuration, this.Width, this.Height, this.MetaData.DeepClone()); - ParallelFor.WithTemporaryBuffer( - 0, - this.Height, + ParallelHelper.IterateRows( + this.Bounds(), configuration, - this.Width, - (int y, IMemoryOwner tempRowBuffer) => - { - Span sourceRow = this.GetPixelRowSpan(y); - Span targetRow = target.GetPixelRowSpan(y); - Span tempRowSpan = tempRowBuffer.GetSpan(); - - PixelOperations.Instance.ToScaledVector4(sourceRow, tempRowSpan, sourceRow.Length); - PixelOperations.Instance.PackFromScaledVector4(tempRowSpan, targetRow, targetRow.Length); - }); + (rows) => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = this.GetPixelRowSpan(y); + Span targetRow = target.GetPixelRowSpan(y); + PixelOperations.Instance.To(configuration, sourceRow, targetRow); + } + }); return target; } @@ -313,15 +314,16 @@ namespace SixLabors.ImageSharp /// The value to initialize the bitmap with. internal void Clear(ParallelOptions parallelOptions, TPixel value) { - Parallel.For( - 0, - this.Height, - parallelOptions, - y => - { - Span targetRow = this.GetPixelRowSpan(y); - targetRow.Fill(value); - }); + Span span = this.GetPixelSpan(); + + if (value.Equals(default)) + { + span.Clear(); + } + else + { + span.Fill(value); + } } /// diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index a7ca0a014c..c19c92426f 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -5,7 +5,7 @@ $(packageversion) 0.0.1 Six Labors and contributors - netstandard1.1;netstandard1.3;netstandard2.0;netcoreapp2.1 + netstandard1.3;netstandard2.0;netcoreapp2.1;net472 true true SixLabors.ImageSharp @@ -31,27 +31,32 @@ IOperation Latest + + + $(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS + + + - + All - - - - + + + ..\..\ImageSharp.ruleset SixLabors.ImageSharp @@ -72,14 +77,46 @@ TextTemplatingFileGenerator Block8x8F.Generated.cs - + TextTemplatingFileGenerator PixelOperations{TPixel}.Generated.cs - + + TextTemplatingFileGenerator + Argb32.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + Bgr24.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + Bgra32.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + Gray8.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + Gray16.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + Rgb24.PixelOperations.Generated.cs + + TextTemplatingFileGenerator Rgba32.PixelOperations.Generated.cs + + TextTemplatingFileGenerator + Rgb48.PixelOperations.Generated.cs + + + TextTemplatingFileGenerator + Rgba64.PixelOperations.Generated.cs + PorterDuffFunctions.Generated.cs TextTemplatingFileGenerator @@ -105,16 +142,56 @@ True Block8x8F.Generated.tt - + True True PixelOperations{TPixel}.Generated.tt - + + True + True + Argb32.PixelOperations.Generated.tt + + + True + True + Bgr24.PixelOperations.Generated.tt + + + True + True + Bgra32.PixelOperations.Generated.tt + + + True + True + Gray8.PixelOperations.Generated.tt + + + True + True + Gray16.PixelOperations.Generated.tt + + + True + True + Rgb24.PixelOperations.Generated.tt + + True True Rgba32.PixelOperations.Generated.tt + + True + True + Rgb48.PixelOperations.Generated.tt + + + True + True + Rgba64.PixelOperations.Generated.tt + True True diff --git a/src/ImageSharp/ImageSharp.csproj.DotSettings b/src/ImageSharp/ImageSharp.csproj.DotSettings new file mode 100644 index 0000000000..a7337240a5 --- /dev/null +++ b/src/ImageSharp/ImageSharp.csproj.DotSettings @@ -0,0 +1,10 @@ + + True + True + True + True + True + True + True + True + True \ No newline at end of file diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 9d4c1ef0b3..178194b21e 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -186,7 +186,7 @@ namespace SixLabors.ImageSharp public Image Clone() => this.Clone(this.configuration); /// - /// Clones the current image with the given configueation. + /// Clones the current image with the given configuration. /// /// The configuration providing initialization code which allows extending the library. /// Returns a new with all the same pixel data as the original. diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index 107457ae73..17ab6e2522 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -96,15 +96,14 @@ namespace SixLabors.ImageSharp.Memory /// The /// The rectangle subarea /// The - public static BufferArea GetArea(this Buffer2D buffer, Rectangle rectangle) + public static BufferArea GetArea(this Buffer2D buffer, in Rectangle rectangle) where T : struct => new BufferArea(buffer, rectangle); public static BufferArea GetArea(this Buffer2D buffer, int x, int y, int width, int height) - where T : struct - { - var rectangle = new Rectangle(x, y, width, height); - return new BufferArea(buffer, rectangle); - } + where T : struct => new BufferArea(buffer, new Rectangle(x, y, width, height)); + + public static BufferArea GetAreaBetweenRows(this Buffer2D buffer, int minY, int maxY) + where T : struct => new BufferArea(buffer, new Rectangle(0, minY, buffer.Width, maxY - minY)); /// /// Return a to the whole area of 'buffer' @@ -114,5 +113,14 @@ namespace SixLabors.ImageSharp.Memory /// The public static BufferArea GetArea(this Buffer2D buffer) where T : struct => new BufferArea(buffer); + + /// + /// Gets a span for all the pixels in defined by + /// + public static Span GetMultiRowSpan(this Buffer2D buffer, in RowInterval rows) + where T : struct + { + return buffer.Span.Slice(rows.Min * buffer.Width, rows.Height * buffer.Width); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Memory/RowInterval.cs b/src/ImageSharp/Memory/RowInterval.cs new file mode 100644 index 0000000000..0750e0368c --- /dev/null +++ b/src/ImageSharp/Memory/RowInterval.cs @@ -0,0 +1,45 @@ +// Copyright(c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Memory +{ + /// + /// Represents an interval of rows in a and/or + /// + internal readonly struct RowInterval + { + /// + /// Initializes a new instance of the struct. + /// + public RowInterval(int min, int max) + { + DebugGuard.MustBeLessThan(min, max, nameof(min)); + + this.Min = min; + this.Max = max; + } + + /// + /// Gets the INCLUSIVE minimum + /// + public int Min { get; } + + /// + /// Gets the EXCLUSIVE maximum + /// + public int Max { get; } + + /// + /// Gets the difference ( - ) + /// + public int Height => this.Max - this.Min; + + /// + public override string ToString() + { + return $"RowInterval [{this.Min}->{this.Max}["; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs index b48b146f11..37ceaf10f6 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif { this.Parts = ExifParts.All; this.data = data; - this.InvalidTags = new List(); + this.InvalidTags = Array.Empty(); } /// @@ -63,7 +63,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif this.Parts = other.Parts; this.thumbnailLength = other.thumbnailLength; this.thumbnailOffset = other.thumbnailOffset; - this.InvalidTags = new List(other.InvalidTags); + + this.InvalidTags = other.InvalidTags.Count > 0 + ? new List(other.InvalidTags) + : (IReadOnlyList)Array.Empty(); + if (other.values != null) { this.values = new List(other.Values.Count); @@ -289,7 +293,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif this.values = reader.ReadValues(); - this.InvalidTags = new List(reader.InvalidTags); + this.InvalidTags = reader.InvalidTags.Count > 0 + ? new List(reader.InvalidTags) + : (IReadOnlyList)Array.Empty(); + this.thumbnailOffset = (int)reader.ThumbnailOffset; this.thumbnailLength = (int)reader.ThumbnailLength; } diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs index 549cb3fe09..100649c0fd 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Text; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Primitives; @@ -19,7 +18,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif /// internal sealed class ExifReader { - private readonly List invalidTags = new List(); + private List invalidTags; private readonly byte[] exifData; private int position; private Endianness endianness = Endianness.BigEndian; @@ -38,7 +37,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif /// /// Gets the invalid tags. /// - public IReadOnlyList InvalidTags => this.invalidTags; + public IReadOnlyList InvalidTags => this.invalidTags ?? (IReadOnlyList)Array.Empty(); /// /// Gets the thumbnail length in the byte stream @@ -125,27 +124,16 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif private byte ConvertToByte(ReadOnlySpan buffer) => buffer[0]; - private unsafe string ConvertToString(ReadOnlySpan buffer) + private string ConvertToString(ReadOnlySpan buffer) { - Span nullChar = stackalloc byte[1] { 0 }; - - int nullCharIndex = buffer.IndexOf(nullChar); + int nullCharIndex = buffer.IndexOf((byte)0); if (nullCharIndex > -1) { buffer = buffer.Slice(0, nullCharIndex); } -#if NETSTANDARD1_1 - return Encoding.UTF8.GetString(buffer.ToArray(), 0, buffer.Length); -#elif NETCOREAPP2_1 return Encoding.UTF8.GetString(buffer); -#else - fixed (byte* pointer = &MemoryMarshal.GetReference(buffer)) - { - return Encoding.UTF8.GetString(pointer, buffer.Length); - } -#endif } /// @@ -349,7 +337,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif // Ensure that the new index does not overrun the data if (newIndex > int.MaxValue) { - this.invalidTags.Add(tag); + this.AddInvalidTag(tag); exifValue = default; @@ -360,7 +348,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif if (this.RemainingLength < size) { - this.invalidTags.Add(tag); + this.AddInvalidTag(tag); + this.position = oldIndex; exifValue = default; @@ -383,13 +372,23 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif return true; } + private void AddInvalidTag(ExifTag tag) + { + if (this.invalidTags == null) + { + this.invalidTags = new List(); + } + + this.invalidTags.Add(tag); + } + + [MethodImpl(InliningOptions.ShortMethod)] private TEnum ToEnum(int value, TEnum defaultValue) where TEnum : struct { - var enumValue = (TEnum)(object)value; - if (Enum.GetValues(typeof(TEnum)).Cast().Any(v => v.Equals(enumValue))) + if (EnumHelper.IsDefined(value)) { - return enumValue; + return Unsafe.As(ref value); } return defaultValue; @@ -558,5 +557,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif ? BinaryPrimitives.ReadInt16BigEndian(buffer) : BinaryPrimitives.ReadInt16LittleEndian(buffer); } + + private class EnumHelper + where TEnum : struct + { + private static readonly int[] Values = Enum.GetValues(typeof(TEnum)).Cast() + .Select(e => Convert.ToInt32(e)).OrderBy(e => e).ToArray(); + + [MethodImpl(InliningOptions.ShortMethod)] + public static bool IsDefined(int value) + { + return Array.BinarySearch(Values, value) >= 0; + } + } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs index ade373341e..9079526d5a 100644 --- a/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs +++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs @@ -17,8 +17,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif /// /// Which parts will be written. /// - private ExifParts allowedParts; - private IList values; + private readonly ExifParts allowedParts; + private readonly IList values; private List dataOffsets; private readonly List ifdIndexes; private readonly List exifIndexes; diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs index ee91ad7a18..b27083dc49 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Curves.cs @@ -41,10 +41,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc public IccResponseCurve ReadResponseCurve(int channelCount) { var type = (IccCurveMeasurementEncodings)this.ReadUInt32(); - uint[] measurment = new uint[channelCount]; + uint[] measurement = new uint[channelCount]; for (int i = 0; i < channelCount; i++) { - measurment[i] = this.ReadUInt32(); + measurement[i] = this.ReadUInt32(); } Vector3[] xyzValues = new Vector3[channelCount]; @@ -56,8 +56,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc IccResponseNumber[][] response = new IccResponseNumber[channelCount][]; for (int i = 0; i < channelCount; i++) { - response[i] = new IccResponseNumber[measurment[i]]; - for (uint j = 0; j < measurment[i]; j++) + response[i] = new IccResponseNumber[measurement[i]]; + for (uint j = 0; j < measurement[i]; j++) { response[i][j] = this.ReadResponseNumber(); } diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs index 5be0060f61..bb85a5ca3e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.Primitives.cs @@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc } Guard.MustBeGreaterThan(length, 0, nameof(length)); - string value = AsciiEncoding.GetString(this.data, this.AddIndex(length), length); + string value = Encoding.ASCII.GetString(this.data, this.AddIndex(length), length); // remove data after (potential) null terminator int pos = value.IndexOf('\0'); diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs index e41d9b3b8c..c572b7f210 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs @@ -628,16 +628,16 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { int start = this.currentIndex - 8; // 8 is the tag header size ushort channelCount = this.ReadUInt16(); - ushort measurmentCount = this.ReadUInt16(); + ushort measurementCount = this.ReadUInt16(); - uint[] offset = new uint[measurmentCount]; - for (int i = 0; i < measurmentCount; i++) + uint[] offset = new uint[measurementCount]; + for (int i = 0; i < measurementCount; i++) { offset[i] = this.ReadUInt32(); } - var curves = new IccResponseCurve[measurmentCount]; - for (int i = 0; i < measurmentCount; i++) + var curves = new IccResponseCurve[measurementCount]; + for (int i = 0; i < measurementCount; i++) { this.currentIndex = (int)(start + offset[i]); curves[i] = this.ReadResponseCurve(channelCount); diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs index cc0f8f34dc..91a28fd743 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.cs @@ -11,8 +11,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// internal sealed partial class IccDataReader { - private static readonly Encoding AsciiEncoding = Encoding.GetEncoding("ASCII"); - /// /// The data that is read /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs index a58f62519c..404285b500 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Primitives.cs @@ -178,7 +178,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc return 0; } - byte[] data = AsciiEncoding.GetBytes(value); + byte[] data = Encoding.ASCII.GetBytes(value); this.dataStream.Write(data, 0, data.Length); return data.Length; } @@ -215,7 +215,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc value = value.Substring(0, Math.Min(length - lengthAdjust, value.Length)); - byte[] textData = AsciiEncoding.GetBytes(value); + byte[] textData = Encoding.ASCII.GetBytes(value); int actualLength = Math.Min(length - lengthAdjust, textData.Length); this.dataStream.Write(textData, 0, actualLength); for (int i = 0; i < length - actualLength; i++) diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs index cfcc66c8e4..21b7b6421b 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using System.Text; namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { @@ -12,9 +11,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// internal sealed partial class IccDataWriter : IDisposable { - private static readonly bool IsLittleEndian = BitConverter.IsLittleEndian; - private static readonly Encoding AsciiEncoding = Encoding.GetEncoding("ASCII"); - /// /// The underlying stream where the data is written to /// @@ -181,7 +177,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// The number of bytes written private unsafe int WriteBytes(byte* data, int length) { - if (IsLittleEndian) + if (BitConverter.IsLittleEndian) { for (int i = length - 1; i >= 0; i--) { diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs index 71c27ca611..8f0427db27 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileClass.cs @@ -39,15 +39,15 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// This profile provides the relevant information to perform a transformation - /// between colour encodings and the PCS. This type of profile is based on - /// modelling rather than device measurement or characterization data. + /// between color encodings and the PCS. This type of profile is based on + /// modeling rather than device measurement or characterization data. /// ColorSpace profiles may be embedded in images. /// ColorSpace = 0x73706163, // spac /// /// This profile represents abstract transforms and does not represent any - /// device model. Colour transformations using Abstract profiles are performed + /// device model. Color transformations using Abstract profiles are performed /// from PCS to PCS. Abstract profiles cannot be embedded in images. /// Abstract = 0x61627374, // abst @@ -55,8 +55,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// NamedColor profiles can be thought of as sibling profiles to device profiles. /// For a given device there would be one or more device profiles to handle - /// process colour conversions and one or more named colour profiles to handle - /// named colours. + /// process color conversions and one or more named color profiles to handle + /// named colors. /// NamedColor = 0x6E6D636C, // nmcl } diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs index 3758be34b7..1e9ec18e89 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileFlag.cs @@ -29,12 +29,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc NotEmbedded = 0, /// - /// Profile cannot be used independently of the embedded colour data + /// Profile cannot be used independently of the embedded color data /// NotIndependent = 1 << 1, /// - /// Profile can be used independently of the embedded colour data + /// Profile can be used independently of the embedded color data /// Independent = 0, } diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs index 82b2969003..61d34dca10 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccProfileTag.cs @@ -19,18 +19,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc Unknown, /// - /// A2B0 - This tag defines a colour transform from Device, Colour Encoding or PCS, to PCS, or a colour transform + /// A2B0 - This tag defines a color transform from Device, Color Encoding or PCS, to PCS, or a color transform /// from Device 1 to Device 2, using lookup table tag element structures /// AToB0 = 0x41324230, /// - /// A2B2 - This tag describes the colour transform from Device or Colour Encoding to PCS using lookup table tag element structures + /// A2B2 - This tag describes the color transform from Device or Color Encoding to PCS using lookup table tag element structures /// AToB1 = 0x41324231, /// - /// A2B2 - This tag describes the colour transform from Device or Colour Encoding to PCS using lookup table tag element structures + /// A2B2 - This tag describes the color transform from Device or Color Encoding to PCS using lookup table tag element structures /// AToB2 = 0x41324232, @@ -46,40 +46,40 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc BlueTrc = 0x62545243, /// - /// B2A0 - This tag defines a colour transform from PCS to Device or Colour Encoding using the lookup table tag element structures + /// B2A0 - This tag defines a color transform from PCS to Device or Color Encoding using the lookup table tag element structures /// BToA0 = 0x42324130, /// - /// B2A1 - This tag defines a colour transform from PCS to Device or Colour Encoding using the lookup table tag element structures. + /// B2A1 - This tag defines a color transform from PCS to Device or Color Encoding using the lookup table tag element structures. /// BToA1 = 0x42324131, /// - /// B2A2 - This tag defines a colour transform from PCS to Device or Colour Encoding using the lookup table tag element structures. + /// B2A2 - This tag defines a color transform from PCS to Device or Color Encoding using the lookup table tag element structures. /// BToA2 = 0x42324132, /// - /// B2D0 - This tag defines a colour transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and + /// B2D0 - This tag defines a color transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and /// provides a means to override the BToA0 tag. /// BToD0 = 0x42324430, /// - /// B2D1 - This tag defines a colour transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and + /// B2D1 - This tag defines a color transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and /// provides a means to override the BToA1 tag. /// BToD1 = 0x42324431, /// - /// B2D2 - This tag defines a colour transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and + /// B2D2 - This tag defines a color transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and /// provides a means to override the BToA2 tag. /// BToD2 = 0x42324432, /// - /// B2D3 - This tag defines a colour transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and + /// B2D3 - This tag defines a color transform from PCS to Device. It supports float32Number-encoded input range, output range and transform, and /// provides a means to override the BToA1 tag. /// BToD3 = 0x42324433, @@ -97,8 +97,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc CharTarget = 0x74617267, /// - /// chad - This tag contains a matrix, which shall be invertible, and which converts an nCIEXYZ colour, measured using the actual illumination - /// conditions and relative to the actual adopted white, to an nCIEXYZ colour relative to the PCS adopted white + /// chad - This tag contains a matrix, which shall be invertible, and which converts an nCIEXYZ color, measured using the actual illumination + /// conditions and relative to the actual adopted white, to an nCIEXYZ color relative to the PCS adopted white /// ChromaticAdaptation = 0x63686164, @@ -166,33 +166,33 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc DeviceSettings = 0x64657673, /// - /// D2B0 - This tag defines a colour transform from Device to PCS. It supports float32Number-encoded + /// D2B0 - This tag defines a color transform from Device to PCS. It supports float32Number-encoded /// input range, output range and transform, and provides a means to override the AToB0 tag /// DToB0 = 0x44324230, /// - /// D2B1 - This tag defines a colour transform from Device to PCS. It supports float32Number-encoded + /// D2B1 - This tag defines a color transform from Device to PCS. It supports float32Number-encoded /// input range, output range and transform, and provides a means to override the AToB1 tag /// DToB1 = 0x44324230, /// - /// D2B2 - This tag defines a colour transform from Device to PCS. It supports float32Number-encoded + /// D2B2 - This tag defines a color transform from Device to PCS. It supports float32Number-encoded /// input range, output range and transform, and provides a means to override the AToB1 tag /// DToB2 = 0x44324230, /// - /// D2B3 - This tag defines a colour transform from Device to PCS. It supports float32Number-encoded + /// D2B3 - This tag defines a color transform from Device to PCS. It supports float32Number-encoded /// input range, output range and transform, and provides a means to override the AToB1 tag /// DToB3 = 0x44324230, /// /// gamt - This tag provides a table in which PCS values are the input and a single - /// output value for each input value is the output. If the output value is 0, the PCS colour is in-gamut. - /// If the output is non-zero, the PCS colour is out-of-gamut + /// output value for each input value is the output. If the output value is 0, the PCS color is in-gamut. + /// If the output is non-zero, the PCS color is out-of-gamut /// Gamut = 0x67616D74, @@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc GreenTrc = 0x67545243, /// - /// lumi - This tag contains the absolute luminance of emissive devices in candelas per square metre as described by the Y channel. + /// lumi - This tag contains the absolute luminance of emissive devices in candelas per square meter as described by the Y channel. /// Luminance = 0x6C756d69, @@ -240,8 +240,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc NamedColor = 0x6E636f6C, /// - /// ncl2 - This tag contains the named colour information providing a PCS and optional device representation - /// for a list of named colours. + /// ncl2 - This tag contains the named color information providing a PCS and optional device representation + /// for a list of named colors. /// NamedColor2 = 0x6E636C32, diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs index e0504b24cb..7cb9c00f39 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccRenderingIntent.cs @@ -10,11 +10,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { /// /// In perceptual transforms the PCS values represent hypothetical - /// measurements of a colour reproduction on the reference reflective + /// measurements of a color reproduction on the reference reflective /// medium. By extension, for the perceptual intent, the PCS represents /// the appearance of that reproduction as viewed in the reference viewing /// environment by a human observer adapted to that environment. The exact - /// colour rendering of the perceptual intent is vendor specific. + /// color rendering of the perceptual intent is vendor specific. /// Perceptual = 0, @@ -27,15 +27,15 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc MediaRelativeColorimetric = 1, /// - /// The exact colour rendering of the saturation intent is vendor + /// The exact color rendering of the saturation intent is vendor /// specific and involves compromises such as trading off - /// preservation of hue in order to preserve the vividness of pure colours. + /// preservation of hue in order to preserve the vividness of pure colors. /// Saturation = 2, /// /// Transformations for this intent shall leave the chromatically - /// adapted nCIEXYZ tristimulus values of the in-gamut colours unchanged. + /// adapted nCIEXYZ tristimulus values of the in-gamut colors unchanged. /// AbsoluteColorimetric = 3, } diff --git a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs index 1493ecc6bb..ad0db4df93 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/Enums/IccTypeSignature.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// This is an optional tag which specifies the laydown order in which colorants /// will be printed on an n-colorant device. The laydown order may be the same /// as the channel generation order listed in the colorantTableTag or the channel - /// order of a colour encoding type such as CMYK, in which case this tag is not + /// order of a color encoding type such as CMYK, in which case this tag is not /// needed. When this is not the case (for example, ink-towers sometimes use /// the order KCMY), this tag may be used to specify the laydown order of the /// colorants @@ -59,25 +59,25 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc DateTime = 0x6474696D, /// - /// This structure represents a colour transform using tables with 16-bit + /// This structure represents a color transform using tables with 16-bit /// precision. This type contains four processing elements: a 3 × 3 matrix - /// (which shall be the identity matrix unless the input colour space is + /// (which shall be the identity matrix unless the input color space is /// PCSXYZ), a set of one-dimensional input tables, a multi-dimensional /// lookup table, and a set of one-dimensional output tables /// Lut16 = 0x6D667432, /// - /// This structure represents a colour transform using tables of 8-bit + /// This structure represents a color transform using tables of 8-bit /// precision. This type contains four processing elements: a 3 × 3 matrix - /// (which shall be the identity matrix unless the input colour space is + /// (which shall be the identity matrix unless the input color space is /// PCSXYZ), a set of one-dimensional input tables, a multi-dimensional /// lookup table, and a set of one-dimensional output tables. /// Lut8 = 0x6D667431, /// - /// This structure represents a colour transform. The type contains up + /// This structure represents a color transform. The type contains up /// to five processing elements which are stored in the AToBTag tag /// in the following order: a set of one-dimensional curves, a 3 × 3 /// matrix with offset terms, a set of one-dimensional curves, a @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc LutAToB = 0x6D414220, /// - /// This structure represents a colour transform. The type contains + /// This structure represents a color transform. The type contains /// up to five processing elements which are stored in the BToATag /// in the following order: a set of one-dimensional curves, a 3 × 3 /// matrix with offset terms, a set of one-dimensional curves, a @@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc MultiLocalizedUnicode = 0x6D6C7563, /// - /// This structure represents a colour transform, containing a sequence + /// This structure represents a color transform, containing a sequence /// of processing elements. The processing elements contained in the /// structure are defined in the structure itself, allowing for a flexible /// structure. Currently supported processing elements are: a set of one @@ -123,15 +123,15 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc MultiProcessElements = 0x6D706574, /// - /// This type is a count value and array of structures that provide colour - /// coordinates for colour names. For each named colour, a PCS and optional - /// device representation of the colour are given. Both representations are + /// This type is a count value and array of structures that provide color + /// coordinates for color names. For each named color, a PCS and optional + /// device representation of the color are given. Both representations are /// 16-bit values and PCS values shall be relative colorimetric. The device - /// representation corresponds to the header’s "data colour space" field. + /// representation corresponds to the header’s "data color space" field. /// This representation should be consistent with the "number of device /// coordinates" field in the namedColor2Type. If this field is 0, device /// coordinates are not provided. The PCS representation corresponds to the - /// header's PCS field. The PCS representation is always provided. Colour + /// header's PCS field. The PCS representation is always provided. Color /// names are fixed-length, 32-byte fields including null termination. In /// order to maintain maximum portability, it is strongly recommended that /// special characters of the 7-bit ASCII set not be used. @@ -168,7 +168,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// so that corrections can be made for variation in the device without /// having to produce a new profile. The mechanism can be used by applications /// to allow users with relatively inexpensive and readily available - /// instrumentation to apply corrections to individual output colour + /// instrumentation to apply corrections to individual output color /// channels in order to achieve consistent results. /// ResponseCurveSet16 = 0x72637332, diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs index 44990b7ecc..5d75a6df9d 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccProfile.cs @@ -2,11 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; - -#if !NETSTANDARD1_1 using System.Security.Cryptography; -#endif namespace SixLabors.ImageSharp.MetaData.Profiles.Icc { @@ -23,7 +19,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// The backing file for the property /// - private List entries; + private IccTagDataEntry[] entries; /// /// ICC profile header @@ -49,13 +45,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// The profile header /// The actual profile data - internal IccProfile(IccProfileHeader header, IEnumerable entries) + internal IccProfile(IccProfileHeader header, IccTagDataEntry[] entries) { - Guard.NotNull(header, nameof(header)); - Guard.NotNull(entries, nameof(entries)); - - this.header = header; - this.entries = new List(entries); + this.header = header ?? throw new ArgumentNullException(nameof(header)); + this.entries = entries ?? throw new ArgumentNullException(nameof(entries)); } /// @@ -88,7 +81,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// Gets the actual profile data /// - public List Entries + public IccTagDataEntry[] Entries { get { @@ -100,8 +93,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// public IccProfile DeepClone() => new IccProfile(this); -#if !NETSTANDARD1_1 - /// /// Calculates the MD5 hash value of an ICC profile /// @@ -147,8 +138,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc } } -#endif - /// /// Checks for signs of a corrupt profile. /// @@ -219,12 +208,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc if (this.data is null) { - this.entries = new List(); + this.entries = Array.Empty(); return; } var reader = new IccReader(); - this.entries = new List(reader.ReadTagData(this.data)); + this.entries = reader.ReadTagData(this.data); } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs index da47b565e3..9f9d373ae1 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccReader.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Collections.Generic; namespace SixLabors.ImageSharp.MetaData.Profiles.Icc @@ -126,7 +127,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc // A normal profile usually has 5-15 entries if (tagCount > 100) { - return new IccTagTableEntry[0]; + return Array.Empty(); } var table = new List((int)tagCount); diff --git a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs b/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs index c42e32d55a..91a3bba549 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/IccWriter.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.Collections.Generic; using System.Linq; @@ -51,12 +50,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc writer.WriteXyzNumber(header.PcsIlluminant); writer.WriteAsciiString(header.CreatorSignature, 4, false); -#if !NETSTANDARD1_1 IccProfileId id = IccProfile.CalculateHash(writer.GetData()); writer.WriteProfileId(id); -#else - writer.WriteProfileId(IccProfileId.Zero); -#endif } private void WriteTagTable(IccDataWriter writer, IccTagTableEntry[] table) @@ -73,12 +68,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc } } - private IccTagTableEntry[] WriteTagData(IccDataWriter writer, List entries) + private IccTagTableEntry[] WriteTagData(IccDataWriter writer, IccTagDataEntry[] entries) { IEnumerable> grouped = entries.GroupBy(t => t); // (Header size) + (entry count) + (nr of entries) * (size of table entry) - writer.SetIndex(128 + 4 + (entries.Count * 12)); + writer.SetIndex(128 + 4 + (entries.Length * 12)); var table = new List(); foreach (IGrouping group in grouped) diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs index 38a2f4522c..77a913b14e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccCurveTagDataEntry.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// Initializes a new instance of the class. /// public IccCurveTagDataEntry() - : this(new float[0], IccProfileTag.Unknown) + : this(Array.Empty(), IccProfileTag.Unknown) { } @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// /// Tag Signature public IccCurveTagDataEntry(IccProfileTag tagSignature) - : this(new float[0], tagSignature) + : this(Array.Empty(), tagSignature) { } @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc public IccCurveTagDataEntry(float[] curveData, IccProfileTag tagSignature) : base(IccTypeSignature.Curve, tagSignature) { - this.CurveData = curveData ?? new float[0]; + this.CurveData = curveData ?? Array.Empty(); } /// diff --git a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs index 9b24bffe85..4510882904 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/TagDataEntries/IccDataTagDataEntry.cs @@ -12,8 +12,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// internal sealed class IccDataTagDataEntry : IccTagDataEntry, IEquatable { - private static readonly Encoding AsciiEncoding = Encoding.GetEncoding("ASCII"); - /// /// Initializes a new instance of the class. /// @@ -60,7 +58,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc /// Gets the decoded as 7bit ASCII. /// If is false, returns null /// - public string AsciiString => this.IsAscii ? AsciiEncoding.GetString(this.Data, 0, this.Data.Length) : null; + public string AsciiString => this.IsAscii ? Encoding.ASCII.GetString(this.Data, 0, this.Data.Length) : null; /// public override bool Equals(IccTagDataEntry other) diff --git a/src/ImageSharp/PixelFormats/Alpha8.cs b/src/ImageSharp/PixelFormats/Alpha8.cs deleted file mode 100644 index a8d97d31a2..0000000000 --- a/src/ImageSharp/PixelFormats/Alpha8.cs +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed pixel type containing a single 8 bit normalized W values. - /// - /// Ranges from [0, 0, 0, 0] to [0, 0, 0, 1] in vector form. - /// - /// - public struct Alpha8 : IPixel, IPackedVector - { - /// - /// Initializes a new instance of the struct. - /// - /// The alpha component - public Alpha8(float alpha) - { - this.PackedValue = Pack(alpha); - } - - /// - public byte PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Alpha8 left, Alpha8 right) - { - return left.PackedValue == right.PackedValue; - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Alpha8 left, Alpha8 right) - { - return left.PackedValue != right.PackedValue; - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - return this.ToVector4(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.PackedValue = Pack(vector.W); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4(0, 0, 0, this.PackedValue / 255F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - this.PackedValue = source.A; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.PackedValue = source.A; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - this.PackedValue = source.A; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - dest = default(Rgb24); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - dest.R = 0; - dest.G = 0; - dest.B = 0; - dest.A = this.PackedValue; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToArgb32(ref Argb32 dest) - { - dest.R = 0; - dest.G = 0; - dest.B = 0; - dest.A = this.PackedValue; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgr24(ref Bgr24 dest) - { - dest = default(Bgr24); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32(ref Bgra32 dest) - { - dest.R = 0; - dest.G = 0; - dest.B = 0; - dest.A = this.PackedValue; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgb48(Rgb48 source) => this.PackedValue = byte.MaxValue; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) - { - dest.R = 0; - dest.G = 0; - dest.B = 0; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - /// Compares an object with the packed vector. - /// - /// The object to compare. - /// True if the object is equal to the packed vector. - public override bool Equals(object obj) - { - return obj is Alpha8 other && this.Equals(other); - } - - /// - /// Compares another Alpha8 packed vector with the packed vector. - /// - /// The Alpha8 packed vector to compare. - /// True if the packed vectors are equal. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Alpha8 other) - { - return this.PackedValue == other.PackedValue; - } - - /// - /// Gets a string representation of the packed vector. - /// - /// A string representation of the packed vector. - public override string ToString() - { - return (this.PackedValue / 255F).ToString(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - /// - /// Packs a into a byte. - /// - /// The float containing the value to pack. - /// The containing the packed values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte Pack(float alpha) - { - return (byte)Math.Round(alpha.Clamp(0, 1) * 255F); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Bgr24.cs b/src/ImageSharp/PixelFormats/Bgr24.cs deleted file mode 100644 index fc283b5684..0000000000 --- a/src/ImageSharp/PixelFormats/Bgr24.cs +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Pixel type containing three 8-bit unsigned normalized values ranging from 0 to 255. - /// The color components are stored in blue, green, red order (least significant to most significant byte). - /// - /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. - /// - /// - [StructLayout(LayoutKind.Explicit)] - public struct Bgr24 : IPixel - { - /// - /// The blue component. - /// - [FieldOffset(0)] - public byte B; - - /// - /// The green component. - /// - [FieldOffset(1)] - public byte G; - - /// - /// The red component. - /// - [FieldOffset(2)] - 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 is Bgr24 other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - { - int hash = HashHelpers.Combine(this.R.GetHashCode(), this.G.GetHashCode()); - return HashHelpers.Combine(hash, this.B.GetHashCode()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - this = source.Bgr; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - return this.ToVector4(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - Rgba32 rgba = default; - 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 = byte.MaxValue; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToArgb32(ref Argb32 dest) - { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = byte.MaxValue; - } - - /// - 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 = byte.MaxValue; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgb48(Rgb48 source) - { - this.R = (byte)(((source.R * 255) + 32895) >> 16); - this.G = (byte)(((source.G * 255) + 32895) >> 16); - this.B = (byte)(((source.B * 255) + 32895) >> 16); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) - { - this.R = (byte)(((source.R * 255) + 32895) >> 16); - this.G = (byte)(((source.G * 255) + 32895) >> 16); - this.B = (byte)(((source.B * 255) + 32895) >> 16); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Bgr565.cs b/src/ImageSharp/PixelFormats/Bgr565.cs deleted file mode 100644 index 570b975dba..0000000000 --- a/src/ImageSharp/PixelFormats/Bgr565.cs +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x and z components use 5 bits, and the y component uses 6 bits. - /// - /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. - /// - /// - public struct Bgr565 : IPixel, IPackedVector - { - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - /// The z-component - public Bgr565(float x, float y, float z) - { - this.PackedValue = Pack(x, y, z); - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The vector containing the components for the packed value. - /// - public Bgr565(Vector3 vector) - { - this.PackedValue = Pack(vector.X, vector.Y, vector.Z); - } - - /// - public ushort PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Bgr565 left, Bgr565 right) - { - return left.PackedValue == right.PackedValue; - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Bgr565 left, Bgr565 right) - { - return left.PackedValue != right.PackedValue; - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - /// Expands the packed representation into a . - /// The vector components are typically expanded in least to greatest significance order. - /// - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector3 ToVector3() - { - return new Vector3( - ((this.PackedValue >> 11) & 0x1F) * (1F / 31F), - ((this.PackedValue >> 5) & 0x3F) * (1F / 63F), - (this.PackedValue & 0x1F) * (1F / 31F)); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - return this.ToVector4(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y, vector.Z); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4(this.ToVector3(), 1F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - Vector4 vector = this.ToVector4() * 255F; - 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 ToRgba32(ref Rgba32 dest) - { - Vector4 vector = this.ToVector4() * 255F; - 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 ToArgb32(ref Argb32 dest) - { - Vector4 vector = this.ToVector4() * 255F; - 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 ToBgr24(ref Bgr24 dest) - { - Vector4 vector = this.ToVector4() * 255F; - 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 ToBgra32(ref Bgra32 dest) - { - Vector4 vector = this.ToVector4() * 255F; - 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 PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - public override bool Equals(object obj) - { - return obj is Bgr565 other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Bgr565 other) - { - return this.PackedValue == other.PackedValue; - } - - /// - public override string ToString() - { - return this.ToVector3().ToString(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - /// - /// Packs the components into a . - /// - /// The x-component - /// The y-component - /// The z-component - /// The containing the packed values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ushort Pack(float x, float y, float z) - { - return (ushort)((((int)Math.Round(x.Clamp(0, 1) * 31F) & 0x1F) << 11) - | (((int)Math.Round(y.Clamp(0, 1) * 63F) & 0x3F) << 5) - | ((int)Math.Round(z.Clamp(0, 1) * 31F) & 0x1F)); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Bgra4444.cs b/src/ImageSharp/PixelFormats/Bgra4444.cs deleted file mode 100644 index 90f967f898..0000000000 --- a/src/ImageSharp/PixelFormats/Bgra4444.cs +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed pixel type containing unsigned normalized values, ranging from 0 to 1, using 4 bits each for x, y, z, and w. - /// - /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. - /// - /// - public struct Bgra4444 : IPixel, IPackedVector - { - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - public Bgra4444(float x, float y, float z, float w) - { - this.PackedValue = Pack(x, y, z, w); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the components for the packed vector. - public Bgra4444(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); - } - - /// - public ushort PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Bgra4444 left, Bgra4444 right) - { - return left.PackedValue == right.PackedValue; - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Bgra4444 left, Bgra4444 right) - { - return left.PackedValue != right.PackedValue; - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - return this.ToVector4(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - 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); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - Vector4 vector = this.ToVector4() * 255F; - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = (byte)vector.Z; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - Vector4 vector = this.ToVector4() * 255F; - 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 ToArgb32(ref Argb32 dest) - { - Vector4 vector = this.ToVector4() * 255F; - 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 ToBgr24(ref Bgr24 dest) - { - Vector4 vector = this.ToVector4() * 255F; - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = (byte)vector.Z; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32(ref Bgra32 dest) - { - Vector4 vector = this.ToVector4() * 255F; - 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 PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - public override bool Equals(object obj) - { - return obj is Bgra4444 other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Bgra4444 other) - { - return this.PackedValue == other.PackedValue; - } - - /// - public override string ToString() - { - return this.ToVector4().ToString(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - /// - /// Packs the components into a . - /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - /// The containing the packed values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - 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)); - } - } -} diff --git a/src/ImageSharp/PixelFormats/Bgra5551.cs b/src/ImageSharp/PixelFormats/Bgra5551.cs deleted file mode 100644 index 3a18c03e83..0000000000 --- a/src/ImageSharp/PixelFormats/Bgra5551.cs +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x , y and z components use 5 bits, and the w component uses 1 bit. - /// - /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. - /// - /// - public struct Bgra5551 : IPixel, IPackedVector - { - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - public Bgra5551(float x, float y, float z, float w) - { - this.PackedValue = Pack(x, y, z, w); - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The vector containing the components for the packed vector. - /// - public Bgra5551(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); - } - - /// - public ushort PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Bgra5551 left, Bgra5551 right) - { - return left.PackedValue == right.PackedValue; - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Bgra5551 left, Bgra5551 right) - { - return left.PackedValue != right.PackedValue; - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - return this.ToVector4(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4( - ((this.PackedValue >> 10) & 0x1F) / 31F, - ((this.PackedValue >> 5) & 0x1F) / 31F, - ((this.PackedValue >> 0) & 0x1F) / 31F, - (this.PackedValue >> 15) & 0x01); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = (byte)vector.Z; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToArgb32(ref Argb32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToBgr24(ref Bgr24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = (byte)vector.Z; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32(ref Bgra32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - public override bool Equals(object obj) - { - return obj is Bgra5551 other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Bgra5551 other) - { - return this.PackedValue == other.PackedValue; - } - - /// - /// Gets a string representation of the packed vector. - /// - /// A string representation of the packed vector. - public override string ToString() - { - return this.ToVector4().ToString(); - } - - /// - /// Gets a hash code of the packed vector. - /// - /// The hash code for the packed vector. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - /// - /// Packs the components into a . - /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - /// The containing the packed values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ushort Pack(float x, float y, float z, float w) - { - return (ushort)( - (((int)Math.Round(x.Clamp(0, 1) * 31F) & 0x1F) << 10) - | (((int)Math.Round(y.Clamp(0, 1) * 31F) & 0x1F) << 5) - | (((int)Math.Round(z.Clamp(0, 1) * 31F) & 0x1F) << 0) - | (((int)Math.Round(w.Clamp(0, 1)) & 0x1) << 15)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Vector4 ToByteScaledVector4() => this.ToVector4() * 255f; - } -} diff --git a/src/ImageSharp/PixelFormats/Byte4.cs b/src/ImageSharp/PixelFormats/Byte4.cs deleted file mode 100644 index bb1b350f07..0000000000 --- a/src/ImageSharp/PixelFormats/Byte4.cs +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed pixel type containing four 8-bit unsigned integer values, ranging from 0 to 255. - /// - /// Ranges from [0, 0, 0, 0] to [255, 255, 255, 255] in vector form. - /// - /// - public struct Byte4 : IPixel, IPackedVector - { - /// - /// Initializes a new instance of the struct. - /// - /// - /// A vector containing the initial values for the components of the Byte4 structure. - /// - public Byte4(Vector4 vector) - { - this.PackedValue = Pack(ref vector); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - public Byte4(float x, float y, float z, float w) - { - var vector = new Vector4(x, y, z, w); - this.PackedValue = Pack(ref vector); - } - - /// - public uint PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Byte4 left, Byte4 right) - { - return left.PackedValue == right.PackedValue; - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Byte4 left, Byte4 right) - { - return left.PackedValue != right.PackedValue; - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - this.PackFromVector4(vector * 255F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - return this.ToVector4() / 255F; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.PackedValue = Pack(ref vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4( - this.PackedValue & 0xFF, - (this.PackedValue >> 0x8) & 0xFF, - (this.PackedValue >> 0x10) & 0xFF, - (this.PackedValue >> 0x18) & 0xFF); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - this.PackFromVector4(source.ToByteScaledVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.PackFromVector4(source.ToByteScaledVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - this.PackFromVector4(source.ToByteScaledVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - var vector = this.ToVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = (byte)vector.Z; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - var vector = this.ToVector4(); - 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 ToArgb32(ref Argb32 dest) - { - var vector = this.ToVector4(); - 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 ToBgr24(ref Bgr24 dest) - { - var vector = this.ToVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = (byte)vector.Z; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32(ref Bgra32 dest) - { - var vector = this.ToVector4(); - 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 PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - public override bool Equals(object obj) - { - return obj is Byte4 byte4 && this.Equals(byte4); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Byte4 other) - { - return this == other; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - /// - /// Returns a string representation of the current instance. - /// - /// String that represents the object. - public override string ToString() - { - return this.PackedValue.ToString("x8"); - } - - /// - /// Packs a vector into a uint. - /// - /// The vector containing the values to pack. - /// The containing the packed values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(ref Vector4 vector) - { - const float Max = 255F; - const float Min = 0F; - - // Clamp the value between min and max values - // TODO: Use Vector4.Clamp() here! - uint byte4 = (uint)Math.Round(vector.X.Clamp(Min, Max)) & 0xFF; - uint byte3 = ((uint)Math.Round(vector.Y.Clamp(Min, Max)) & 0xFF) << 0x8; - uint byte2 = ((uint)Math.Round(vector.Z.Clamp(Min, Max)) & 0xFF) << 0x10; - uint byte1 = ((uint)Math.Round(vector.W.Clamp(Min, Max)) & 0xFF) << 0x18; - - return byte4 | byte3 | byte2 | byte1; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs b/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs index cf66f5d5e8..2572b32933 100644 --- a/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The hexadecimal representation of the combined color components arranged /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. /// - /// Returns a that represents the color defined by the provided RGBA heax string. + /// Returns a that represents the color defined by the provided RGBA hex string. public static TPixel FromHex(string hex) { Guard.NotNullOrWhiteSpace(hex, nameof(hex)); @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.PixelFormats TPixel result = default; var rgba = new Rgba32(BinaryPrimitives.ReverseEndianness(packedValue)); - result.PackFromRgba32(rgba); + result.FromRgba32(rgba); return result; } @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.PixelFormats public static TPixel FromRGBA(byte red, byte green, byte blue, byte alpha) { TPixel color = default; - color.PackFromRgba32(new Rgba32(red, green, blue, alpha)); + color.FromRgba32(new Rgba32(red, green, blue, alpha)); return color; } diff --git a/src/ImageSharp/PixelFormats/ColorConstants.cs b/src/ImageSharp/PixelFormats/ColorConstants.cs index bac05c53d2..14df385697 100644 --- a/src/ImageSharp/PixelFormats/ColorConstants.cs +++ b/src/ImageSharp/PixelFormats/ColorConstants.cs @@ -11,157 +11,268 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Gets a collection of named, web safe, colors as defined in the CSS Color Module Level 4. /// - public static readonly Rgba32[] WebSafeColors = GetWebSafeColors(); + public static readonly Rgba32[] WebSafeColors = + { + Rgba32.AliceBlue, + Rgba32.AntiqueWhite, + Rgba32.Aqua, + Rgba32.Aquamarine, + Rgba32.Azure, + Rgba32.Beige, + Rgba32.Bisque, + Rgba32.Black, + Rgba32.BlanchedAlmond, + Rgba32.Blue, + Rgba32.BlueViolet, + Rgba32.Brown, + Rgba32.BurlyWood, + Rgba32.CadetBlue, + Rgba32.Chartreuse, + Rgba32.Chocolate, + Rgba32.Coral, + Rgba32.CornflowerBlue, + Rgba32.Cornsilk, + Rgba32.Crimson, + Rgba32.Cyan, + Rgba32.DarkBlue, + Rgba32.DarkCyan, + Rgba32.DarkGoldenrod, + Rgba32.DarkGray, + Rgba32.DarkGreen, + Rgba32.DarkKhaki, + Rgba32.DarkMagenta, + Rgba32.DarkOliveGreen, + Rgba32.DarkOrange, + Rgba32.DarkOrchid, + Rgba32.DarkRed, + Rgba32.DarkSalmon, + Rgba32.DarkSeaGreen, + Rgba32.DarkSlateBlue, + Rgba32.DarkSlateGray, + Rgba32.DarkTurquoise, + Rgba32.DarkViolet, + Rgba32.DeepPink, + Rgba32.DeepSkyBlue, + Rgba32.DimGray, + Rgba32.DodgerBlue, + Rgba32.Firebrick, + Rgba32.FloralWhite, + Rgba32.ForestGreen, + Rgba32.Fuchsia, + Rgba32.Gainsboro, + Rgba32.GhostWhite, + Rgba32.Gold, + Rgba32.Goldenrod, + Rgba32.Gray, + Rgba32.Green, + Rgba32.GreenYellow, + Rgba32.Honeydew, + Rgba32.HotPink, + Rgba32.IndianRed, + Rgba32.Indigo, + Rgba32.Ivory, + Rgba32.Khaki, + Rgba32.Lavender, + Rgba32.LavenderBlush, + Rgba32.LawnGreen, + Rgba32.LemonChiffon, + Rgba32.LightBlue, + Rgba32.LightCoral, + Rgba32.LightCyan, + Rgba32.LightGoldenrodYellow, + Rgba32.LightGray, + Rgba32.LightGreen, + Rgba32.LightPink, + Rgba32.LightSalmon, + Rgba32.LightSeaGreen, + Rgba32.LightSkyBlue, + Rgba32.LightSlateGray, + Rgba32.LightSteelBlue, + Rgba32.LightYellow, + Rgba32.Lime, + Rgba32.LimeGreen, + Rgba32.Linen, + Rgba32.Magenta, + Rgba32.Maroon, + Rgba32.MediumAquamarine, + Rgba32.MediumBlue, + Rgba32.MediumOrchid, + Rgba32.MediumPurple, + Rgba32.MediumSeaGreen, + Rgba32.MediumSlateBlue, + Rgba32.MediumSpringGreen, + Rgba32.MediumTurquoise, + Rgba32.MediumVioletRed, + Rgba32.MidnightBlue, + Rgba32.MintCream, + Rgba32.MistyRose, + Rgba32.Moccasin, + Rgba32.NavajoWhite, + Rgba32.Navy, + Rgba32.OldLace, + Rgba32.Olive, + Rgba32.OliveDrab, + Rgba32.Orange, + Rgba32.OrangeRed, + Rgba32.Orchid, + Rgba32.PaleGoldenrod, + Rgba32.PaleGreen, + Rgba32.PaleTurquoise, + Rgba32.PaleVioletRed, + Rgba32.PapayaWhip, + Rgba32.PeachPuff, + Rgba32.Peru, + Rgba32.Pink, + Rgba32.Plum, + Rgba32.PowderBlue, + Rgba32.Purple, + Rgba32.RebeccaPurple, + Rgba32.Red, + Rgba32.RosyBrown, + Rgba32.RoyalBlue, + Rgba32.SaddleBrown, + Rgba32.Salmon, + Rgba32.SandyBrown, + Rgba32.SeaGreen, + Rgba32.SeaShell, + Rgba32.Sienna, + Rgba32.Silver, + Rgba32.SkyBlue, + Rgba32.SlateBlue, + Rgba32.SlateGray, + Rgba32.Snow, + Rgba32.SpringGreen, + Rgba32.SteelBlue, + Rgba32.Tan, + Rgba32.Teal, + Rgba32.Thistle, + Rgba32.Tomato, + Rgba32.Transparent, + Rgba32.Turquoise, + Rgba32.Violet, + Rgba32.Wheat, + Rgba32.White, + Rgba32.WhiteSmoke, + Rgba32.Yellow, + Rgba32.YellowGreen + }; /// - /// Returns an array of web safe colors. + /// Gets a collection of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821. + /// The hex codes were collected and defined by Nicholas Rougeux /// - /// The - private static Rgba32[] GetWebSafeColors() - => new Rgba32[] - { - Rgba32.AliceBlue, - Rgba32.AntiqueWhite, - Rgba32.Aqua, - Rgba32.Aquamarine, - Rgba32.Azure, - Rgba32.Beige, - Rgba32.Bisque, - Rgba32.Black, - Rgba32.BlanchedAlmond, - Rgba32.Blue, - Rgba32.BlueViolet, - Rgba32.Brown, - Rgba32.BurlyWood, - Rgba32.CadetBlue, - Rgba32.Chartreuse, - Rgba32.Chocolate, - Rgba32.Coral, - Rgba32.CornflowerBlue, - Rgba32.Cornsilk, - Rgba32.Crimson, - Rgba32.Cyan, - Rgba32.DarkBlue, - Rgba32.DarkCyan, - Rgba32.DarkGoldenrod, - Rgba32.DarkGray, - Rgba32.DarkGreen, - Rgba32.DarkKhaki, - Rgba32.DarkMagenta, - Rgba32.DarkOliveGreen, - Rgba32.DarkOrange, - Rgba32.DarkOrchid, - Rgba32.DarkRed, - Rgba32.DarkSalmon, - Rgba32.DarkSeaGreen, - Rgba32.DarkSlateBlue, - Rgba32.DarkSlateGray, - Rgba32.DarkTurquoise, - Rgba32.DarkViolet, - Rgba32.DeepPink, - Rgba32.DeepSkyBlue, - Rgba32.DimGray, - Rgba32.DodgerBlue, - Rgba32.Firebrick, - Rgba32.FloralWhite, - Rgba32.ForestGreen, - Rgba32.Fuchsia, - Rgba32.Gainsboro, - Rgba32.GhostWhite, - Rgba32.Gold, - Rgba32.Goldenrod, - Rgba32.Gray, - Rgba32.Green, - Rgba32.GreenYellow, - Rgba32.Honeydew, - Rgba32.HotPink, - Rgba32.IndianRed, - Rgba32.Indigo, - Rgba32.Ivory, - Rgba32.Khaki, - Rgba32.Lavender, - Rgba32.LavenderBlush, - Rgba32.LawnGreen, - Rgba32.LemonChiffon, - Rgba32.LightBlue, - Rgba32.LightCoral, - Rgba32.LightCyan, - Rgba32.LightGoldenrodYellow, - Rgba32.LightGray, - Rgba32.LightGreen, - Rgba32.LightPink, - Rgba32.LightSalmon, - Rgba32.LightSeaGreen, - Rgba32.LightSkyBlue, - Rgba32.LightSlateGray, - Rgba32.LightSteelBlue, - Rgba32.LightYellow, - Rgba32.Lime, - Rgba32.LimeGreen, - Rgba32.Linen, - Rgba32.Magenta, - Rgba32.Maroon, - Rgba32.MediumAquamarine, - Rgba32.MediumBlue, - Rgba32.MediumOrchid, - Rgba32.MediumPurple, - Rgba32.MediumSeaGreen, - Rgba32.MediumSlateBlue, - Rgba32.MediumSpringGreen, - Rgba32.MediumTurquoise, - Rgba32.MediumVioletRed, - Rgba32.MidnightBlue, - Rgba32.MintCream, - Rgba32.MistyRose, - Rgba32.Moccasin, - Rgba32.NavajoWhite, - Rgba32.Navy, - Rgba32.OldLace, - Rgba32.Olive, - Rgba32.OliveDrab, - Rgba32.Orange, - Rgba32.OrangeRed, - Rgba32.Orchid, - Rgba32.PaleGoldenrod, - Rgba32.PaleGreen, - Rgba32.PaleTurquoise, - Rgba32.PaleVioletRed, - Rgba32.PapayaWhip, - Rgba32.PeachPuff, - Rgba32.Peru, - Rgba32.Pink, - Rgba32.Plum, - Rgba32.PowderBlue, - Rgba32.Purple, - Rgba32.RebeccaPurple, - Rgba32.Red, - Rgba32.RosyBrown, - Rgba32.RoyalBlue, - Rgba32.SaddleBrown, - Rgba32.Salmon, - Rgba32.SandyBrown, - Rgba32.SeaGreen, - Rgba32.SeaShell, - Rgba32.Sienna, - Rgba32.Silver, - Rgba32.SkyBlue, - Rgba32.SlateBlue, - Rgba32.SlateGray, - Rgba32.Snow, - Rgba32.SpringGreen, - Rgba32.SteelBlue, - Rgba32.Tan, - Rgba32.Teal, - Rgba32.Thistle, - Rgba32.Tomato, - Rgba32.Transparent, - Rgba32.Turquoise, - Rgba32.Violet, - Rgba32.Wheat, - Rgba32.White, - Rgba32.WhiteSmoke, - Rgba32.Yellow, - Rgba32.YellowGreen - }; + public static readonly Rgba32[] WernerColors = + { + Rgba32.FromHex("#f1e9cd"), + Rgba32.FromHex("#f2e7cf"), + Rgba32.FromHex("#ece6d0"), + Rgba32.FromHex("#f2eacc"), + Rgba32.FromHex("#f3e9ca"), + Rgba32.FromHex("#f2ebcd"), + Rgba32.FromHex("#e6e1c9"), + Rgba32.FromHex("#e2ddc6"), + Rgba32.FromHex("#cbc8b7"), + Rgba32.FromHex("#bfbbb0"), + Rgba32.FromHex("#bebeb3"), + Rgba32.FromHex("#b7b5ac"), + Rgba32.FromHex("#bab191"), + Rgba32.FromHex("#9c9d9a"), + Rgba32.FromHex("#8a8d84"), + Rgba32.FromHex("#5b5c61"), + Rgba32.FromHex("#555152"), + Rgba32.FromHex("#413f44"), + Rgba32.FromHex("#454445"), + Rgba32.FromHex("#423937"), + Rgba32.FromHex("#433635"), + Rgba32.FromHex("#252024"), + Rgba32.FromHex("#241f20"), + Rgba32.FromHex("#281f3f"), + Rgba32.FromHex("#1c1949"), + Rgba32.FromHex("#4f638d"), + Rgba32.FromHex("#383867"), + Rgba32.FromHex("#5c6b8f"), + Rgba32.FromHex("#657abb"), + Rgba32.FromHex("#6f88af"), + Rgba32.FromHex("#7994b5"), + Rgba32.FromHex("#6fb5a8"), + Rgba32.FromHex("#719ba2"), + Rgba32.FromHex("#8aa1a6"), + Rgba32.FromHex("#d0d5d3"), + Rgba32.FromHex("#8590ae"), + Rgba32.FromHex("#3a2f52"), + Rgba32.FromHex("#39334a"), + Rgba32.FromHex("#6c6d94"), + Rgba32.FromHex("#584c77"), + Rgba32.FromHex("#533552"), + Rgba32.FromHex("#463759"), + Rgba32.FromHex("#bfbac0"), + Rgba32.FromHex("#77747f"), + Rgba32.FromHex("#4a475c"), + Rgba32.FromHex("#b8bfaf"), + Rgba32.FromHex("#b2b599"), + Rgba32.FromHex("#979c84"), + Rgba32.FromHex("#5d6161"), + Rgba32.FromHex("#61ac86"), + Rgba32.FromHex("#a4b6a7"), + Rgba32.FromHex("#adba98"), + Rgba32.FromHex("#93b778"), + Rgba32.FromHex("#7d8c55"), + Rgba32.FromHex("#33431e"), + Rgba32.FromHex("#7c8635"), + Rgba32.FromHex("#8e9849"), + Rgba32.FromHex("#c2c190"), + Rgba32.FromHex("#67765b"), + Rgba32.FromHex("#ab924b"), + Rgba32.FromHex("#c8c76f"), + Rgba32.FromHex("#ccc050"), + Rgba32.FromHex("#ebdd99"), + Rgba32.FromHex("#ab9649"), + Rgba32.FromHex("#dbc364"), + Rgba32.FromHex("#e6d058"), + Rgba32.FromHex("#ead665"), + Rgba32.FromHex("#d09b2c"), + Rgba32.FromHex("#a36629"), + Rgba32.FromHex("#a77d35"), + Rgba32.FromHex("#f0d696"), + Rgba32.FromHex("#d7c485"), + Rgba32.FromHex("#f1d28c"), + Rgba32.FromHex("#efcc83"), + Rgba32.FromHex("#f3daa7"), + Rgba32.FromHex("#dfa837"), + Rgba32.FromHex("#ebbc71"), + Rgba32.FromHex("#d17c3f"), + Rgba32.FromHex("#92462f"), + Rgba32.FromHex("#be7249"), + Rgba32.FromHex("#bb603c"), + Rgba32.FromHex("#c76b4a"), + Rgba32.FromHex("#a75536"), + Rgba32.FromHex("#b63e36"), + Rgba32.FromHex("#b5493a"), + Rgba32.FromHex("#cd6d57"), + Rgba32.FromHex("#711518"), + Rgba32.FromHex("#e9c49d"), + Rgba32.FromHex("#eedac3"), + Rgba32.FromHex("#eecfbf"), + Rgba32.FromHex("#ce536b"), + Rgba32.FromHex("#b74a70"), + Rgba32.FromHex("#b7757c"), + Rgba32.FromHex("#612741"), + Rgba32.FromHex("#7a4848"), + Rgba32.FromHex("#3f3033"), + Rgba32.FromHex("#8d746f"), + Rgba32.FromHex("#4d3635"), + Rgba32.FromHex("#6e3b31"), + Rgba32.FromHex("#864735"), + Rgba32.FromHex("#553d3a"), + Rgba32.FromHex("#613936"), + Rgba32.FromHex("#7a4b3a"), + Rgba32.FromHex("#946943"), + Rgba32.FromHex("#c39e6d"), + Rgba32.FromHex("#513e32"), + Rgba32.FromHex("#8b7859"), + Rgba32.FromHex("#9b856b"), + Rgba32.FromHex("#766051"), + Rgba32.FromHex("#453b32") + }; } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/ComponentOrder.cs b/src/ImageSharp/PixelFormats/ComponentOrder.cs deleted file mode 100644 index 868d082599..0000000000 --- a/src/ImageSharp/PixelFormats/ComponentOrder.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Enumerates the various component orders. - /// - internal enum ComponentOrder - { - /// - /// Z-> Y-> X order. Equivalent to B-> G-> R in - /// - Zyx, - - /// - /// Z-> Y-> X-> W order. Equivalent to B-> G-> R-> A in - /// - Zyxw, - - /// - /// X-> Y-> Z order. Equivalent to R-> G-> B in - /// - Xyz, - - /// - /// X-> Y-> Z-> W order. Equivalent to R-> G-> B-> A in - /// - Xyzw, - } -} diff --git a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs b/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs deleted file mode 100644 index f644fbefb5..0000000000 --- a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs +++ /dev/null @@ -1,512 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -// -namespace SixLabors.ImageSharp.PixelFormats -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - 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 PackFromRgba64(ReadOnlySpan source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(source); - ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); - - var rgba = new Rgba64(0, 0, 0, 65535); - - for (int i = 0; i < count; i++) - { - ref TPixel dp = ref Unsafe.Add(ref destRef, i); - rgba = Unsafe.Add(ref sourceRef, i); - dp.PackFromRgba64(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 PackFromRgba64Bytes(ReadOnlySpan sourceBytes, Span destPixels, int count) - { - this.PackFromRgba64(MemoryMarshal.Cast(sourceBytes), 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 ToRgba64(ReadOnlySpan sourcePixels, Span dest, int count) - { - GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba64 destBaseRef = ref MemoryMarshal.GetReference(dest); - - for (int i = 0; i < count; i++) - { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref Rgba64 dp = ref Unsafe.Add(ref destBaseRef, i); - sp.ToRgba64(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 ToRgba64Bytes(ReadOnlySpan sourceColors, Span destBytes, int count) - { - this.ToRgba64(sourceColors, MemoryMarshal.Cast(destBytes), 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 PackFromRgb48(ReadOnlySpan source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(source); - ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); - - var rgb = default(Rgb48); - - for (int i = 0; i < count; i++) - { - ref TPixel dp = ref Unsafe.Add(ref destRef, i); - rgb = Unsafe.Add(ref sourceRef, i); - dp.PackFromRgb48(rgb); - } - } - - /// - /// 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 PackFromRgb48Bytes(ReadOnlySpan sourceBytes, Span destPixels, int count) - { - this.PackFromRgb48(MemoryMarshal.Cast(sourceBytes), 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 ToRgb48(ReadOnlySpan sourcePixels, Span dest, int count) - { - GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb48 destBaseRef = ref MemoryMarshal.GetReference(dest); - - for (int i = 0; i < count; i++) - { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref Rgb48 dp = ref Unsafe.Add(ref destBaseRef, i); - sp.ToRgb48(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 ToRgb48Bytes(ReadOnlySpan sourceColors, Span destBytes, int count) - { - this.ToRgb48(sourceColors, MemoryMarshal.Cast(destBytes), 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 PackFromRgba32(ReadOnlySpan source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(source); - ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); - - var 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(ReadOnlySpan sourceBytes, Span destPixels, int count) - { - this.PackFromRgba32(MemoryMarshal.Cast(sourceBytes), 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(ReadOnlySpan sourcePixels, Span dest, int count) - { - GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgba32 destBaseRef = ref MemoryMarshal.GetReference(dest); - - 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(ReadOnlySpan sourceColors, Span destBytes, int count) - { - this.ToRgba32(sourceColors, MemoryMarshal.Cast(destBytes), 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(ReadOnlySpan source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(source); - ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); - - var bgra = new Bgra32(0, 0, 0, 255); - - for (int i = 0; i < count; i++) - { - ref TPixel dp = ref Unsafe.Add(ref destRef, i); - bgra = Unsafe.Add(ref sourceRef, i); - dp.PackFromBgra32(bgra); - } - } - - /// - /// 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(ReadOnlySpan sourceBytes, Span destPixels, int count) - { - this.PackFromBgra32(MemoryMarshal.Cast(sourceBytes), 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(ReadOnlySpan sourcePixels, Span dest, int count) - { - GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra32 destBaseRef = ref MemoryMarshal.GetReference(dest); - - 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(ReadOnlySpan sourceColors, Span destBytes, int count) - { - this.ToBgra32(sourceColors, MemoryMarshal.Cast(destBytes), 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(ReadOnlySpan source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(source); - ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); - - var 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(ReadOnlySpan sourceBytes, Span destPixels, int count) - { - this.PackFromRgb24(MemoryMarshal.Cast(sourceBytes), 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(ReadOnlySpan sourcePixels, Span dest, int count) - { - GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb24 destBaseRef = ref MemoryMarshal.GetReference(dest); - - 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(ReadOnlySpan sourceColors, Span destBytes, int count) - { - this.ToRgb24(sourceColors, MemoryMarshal.Cast(destBytes), 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(ReadOnlySpan source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(source); - ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); - - var 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(ReadOnlySpan sourceBytes, Span destPixels, int count) - { - this.PackFromBgr24(MemoryMarshal.Cast(sourceBytes), 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(ReadOnlySpan sourcePixels, Span dest, int count) - { - GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgr24 destBaseRef = ref MemoryMarshal.GetReference(dest); - - 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(ReadOnlySpan sourceColors, Span destBytes, int count) - { - this.ToBgr24(sourceColors, MemoryMarshal.Cast(destBytes), 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 PackFromArgb32(ReadOnlySpan source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref Argb32 sourceRef = ref MemoryMarshal.GetReference(source); - ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); - - var argb = new Argb32(0, 0, 0, 255); - - for (int i = 0; i < count; i++) - { - ref TPixel dp = ref Unsafe.Add(ref destRef, i); - argb = Unsafe.Add(ref sourceRef, i); - dp.PackFromArgb32(argb); - } - } - - /// - /// 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 PackFromArgb32Bytes(ReadOnlySpan sourceBytes, Span destPixels, int count) - { - this.PackFromArgb32(MemoryMarshal.Cast(sourceBytes), 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 ToArgb32(ReadOnlySpan sourcePixels, Span dest, int count) - { - GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Argb32 destBaseRef = ref MemoryMarshal.GetReference(dest); - - for (int i = 0; i < count; i++) - { - ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); - ref Argb32 dp = ref Unsafe.Add(ref destBaseRef, i); - sp.ToArgb32(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 ToArgb32Bytes(ReadOnlySpan sourceColors, Span destBytes, int count) - { - this.ToArgb32(sourceColors, MemoryMarshal.Cast(destBytes), 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 deleted file mode 100644 index 1a6ac60f58..0000000000 --- a/src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.tt +++ /dev/null @@ -1,303 +0,0 @@ -<# -// Copyright (c) Six Labors 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" #> -<#@ import namespace="System.Runtime.InteropServices" #> -<#@ 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#>(ReadOnlySpan sourcePixels, Span<<#=pixelType#>> dest, int count) - { - GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - - ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); - ref <#=pixelType#> destBaseRef = ref MemoryMarshal.GetReference(dest); - - 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(ReadOnlySpan sourceColors, Span destBytes, int count) - { - this.To<#=pixelType#>(sourceColors, MemoryMarshal.Cast>(destBytes), count); - } - <# - } - - void GeneratePackFromMethodUsingPackFromRgba64(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#>(ReadOnlySpan<<#=pixelType#>> source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref <#=pixelType#> sourceRef = ref MemoryMarshal.GetReference(source); - ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); - - var rgba = new Rgba64(0, 0, 0, 65535); - - for (int i = 0; i < count; i++) - { - ref TPixel dp = ref Unsafe.Add(ref destRef, i); - <#=rgbaOperationCode#> - dp.PackFromRgba64(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(ReadOnlySpan sourceBytes, Span destPixels, int count) - { - this.PackFrom<#=pixelType#>(MemoryMarshal.Cast>(sourceBytes), destPixels, count); - } - <# - } - - void GeneratePackFromMethodUsingPackFromRgb48(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#>(ReadOnlySpan<<#=pixelType#>> source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref <#=pixelType#> sourceRef = ref MemoryMarshal.GetReference(source); - ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); - - var rgb = default(Rgb48); - - for (int i = 0; i < count; i++) - { - ref TPixel dp = ref Unsafe.Add(ref destRef, i); - <#=rgbaOperationCode#> - dp.PackFromRgb48(rgb); - } - } - - /// - /// 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(ReadOnlySpan sourceBytes, Span destPixels, int count) - { - this.PackFrom<#=pixelType#>(MemoryMarshal.Cast>(sourceBytes), destPixels, 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#>(ReadOnlySpan<<#=pixelType#>> source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref <#=pixelType#> sourceRef = ref MemoryMarshal.GetReference(source); - ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); - - var 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(ReadOnlySpan sourceBytes, Span destPixels, int count) - { - this.PackFrom<#=pixelType#>(MemoryMarshal.Cast>(sourceBytes), destPixels, count); - } - <# - } - - void GeneratePackFromMethodUsingPackFromArgb32(string pixelType, string argbOperationCode) - { - #> - - /// - /// 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#>(ReadOnlySpan<<#=pixelType#>> source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref <#=pixelType#> sourceRef = ref MemoryMarshal.GetReference(source); - ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); - - var argb = new Argb32(0, 0, 0, 255); - - for (int i = 0; i < count; i++) - { - ref TPixel dp = ref Unsafe.Add(ref destRef, i); - <#=argbOperationCode#> - dp.PackFromArgb32(argb); - } - } - - /// - /// 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(ReadOnlySpan sourceBytes, Span destPixels, int count) - { - this.PackFrom<#=pixelType#>(MemoryMarshal.Cast>(sourceBytes), destPixels, count); - } - <# - } - - void GeneratePackFromMethodUsingPackFromBgra32(string pixelType, string bgraOperationCode) - { - #> - - /// - /// 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#>(ReadOnlySpan<<#=pixelType#>> source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref <#=pixelType#> sourceRef = ref MemoryMarshal.GetReference(source); - ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); - - var bgra = new Bgra32(0, 0, 0, 255); - - for (int i = 0; i < count; i++) - { - ref TPixel dp = ref Unsafe.Add(ref destRef, i); - <#=bgraOperationCode#> - dp.PackFromBgra32(bgra); - } - } - - /// - /// 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(ReadOnlySpan sourceBytes, Span destPixels, int count) - { - this.PackFrom<#=pixelType#>(MemoryMarshal.Cast>(sourceBytes), destPixels, count); - } - <# - } - -#> -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -// -namespace SixLabors.ImageSharp.PixelFormats -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - public partial class PixelOperations - { - <# - - GeneratePackFromMethodUsingPackFromRgba64("Rgba64", "rgba = Unsafe.Add(ref sourceRef, i);"); - GenerateToDestFormatMethods("Rgba64"); - - GeneratePackFromMethodUsingPackFromRgb48("Rgb48", "rgb = Unsafe.Add(ref sourceRef, i);"); - GenerateToDestFormatMethods("Rgb48"); - - GeneratePackFromMethodUsingPackFromRgba32("Rgba32", "rgba = Unsafe.Add(ref sourceRef, i);"); - GenerateToDestFormatMethods("Rgba32"); - - GeneratePackFromMethodUsingPackFromBgra32("Bgra32", "bgra = Unsafe.Add(ref sourceRef, i);"); - GenerateToDestFormatMethods("Bgra32"); - - GeneratePackFromMethodUsingPackFromRgba32("Rgb24", "rgba.Rgb = Unsafe.Add(ref sourceRef, i);"); - GenerateToDestFormatMethods("Rgb24"); - - GeneratePackFromMethodUsingPackFromRgba32("Bgr24", "rgba.Bgr = Unsafe.Add(ref sourceRef, i);"); - GenerateToDestFormatMethods("Bgr24"); - - GeneratePackFromMethodUsingPackFromArgb32("Argb32", "argb = Unsafe.Add(ref sourceRef, i);"); - GenerateToDestFormatMethods("Argb32"); - - #> - - } -} \ 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 deleted file mode 100644 index e68efba252..0000000000 --- a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.cs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -// -namespace SixLabors.ImageSharp.PixelFormats -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - /// - /// Provides optimized overrides for bulk operations. - /// - public partial struct Rgba32 - { - internal partial class PixelOperations - { - - /// - internal override void PackFromRgb24(ReadOnlySpan source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); - - 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(ReadOnlySpan sourcePixels, Span dest, int count) - { - GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - - ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Rgb24 destRef = ref MemoryMarshal.GetReference(dest); - - 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(ReadOnlySpan source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); - - 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(ReadOnlySpan sourcePixels, Span dest, int count) - { - GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - - ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgr24 destRef = ref MemoryMarshal.GetReference(dest); - - 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(ReadOnlySpan source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); - - 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(ReadOnlySpan sourcePixels, Span dest, int count) - { - GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - - ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Bgra32 destRef = ref MemoryMarshal.GetReference(dest); - - 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(); - } - } - - /// - internal override void PackFromArgb32(ReadOnlySpan source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref Argb32 sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); - - for (int i = 0; i < count; i++) - { - ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); - dp = sp.ToRgba32(); - } - } - - /// - internal override void ToArgb32(ReadOnlySpan sourcePixels, Span dest, int count) - { - GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - - ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref Argb32 destRef = ref MemoryMarshal.GetReference(dest); - - for (int i = 0; i < count; i++) - { - ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); - ref Argb32 dp = ref Unsafe.Add(ref destRef, i); - dp = sp.ToArgb32(); - } - } - - } - } -} \ 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 deleted file mode 100644 index a734333390..0000000000 --- a/src/ImageSharp/PixelFormats/Generated/Rgba32.PixelOperations.Generated.tt +++ /dev/null @@ -1,89 +0,0 @@ -<# -// Copyright (c) Six Labors 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#>(ReadOnlySpan<<#=pixelType#>> source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - ref <#=pixelType#> sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); - - 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#>(ReadOnlySpan sourcePixels, Span<<#=pixelType#>> dest, int count) - { - GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - - ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); - ref <#=pixelType#> destRef = ref MemoryMarshal.GetReference(dest); - - 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) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -// -namespace SixLabors.ImageSharp.PixelFormats -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - /// - /// Provides optimized overrides for bulk operations. - /// - public partial struct Rgba32 - { - internal partial class 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();"); - - GeneratePackFromMethod("Argb32", "dp = sp.ToRgba32();"); - GenerateConvertToMethod("Argb32", "dp = sp.ToArgb32();"); - #> - - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/HalfSingle.cs b/src/ImageSharp/PixelFormats/HalfSingle.cs deleted file mode 100644 index 09b4636492..0000000000 --- a/src/ImageSharp/PixelFormats/HalfSingle.cs +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed pixel type containing a single 16 bit floating point value. - /// - /// Ranges from [-1, 0, 0, 1] to [1, 0, 0, 1] in vector form. - /// - /// - public struct HalfSingle : IPixel, IPackedVector - { - /// - /// The maximum byte value. - /// - private static readonly Vector4 MaxBytes = new Vector4(255); - - /// - /// The half vector value. - /// - private static readonly Vector4 Half = new Vector4(0.5F); - - /// - /// Initializes a new instance of the struct. - /// - /// The single component. - public HalfSingle(float single) - { - this.PackedValue = HalfTypeHelper.Pack(single); - } - - /// - public ushort PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(HalfSingle left, HalfSingle right) - { - return left.PackedValue == right.PackedValue; - } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(HalfSingle left, HalfSingle right) - { - return left.PackedValue != right.PackedValue; - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - /// Expands the packed representation into a . - /// - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float ToSingle() - { - return HalfTypeHelper.Unpack(this.PackedValue); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - float scaled = vector.X; - scaled *= 2F; - scaled -= 1F; - this.PackedValue = HalfTypeHelper.Pack(scaled); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - float single = this.ToSingle() + 1F; - single /= 2F; - return new Vector4(single, 0, 0, 1); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.PackedValue = HalfTypeHelper.Pack(vector.X); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4(this.ToSingle(), 0, 0, 1); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = (byte)vector.Z; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToArgb32(ref Argb32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToBgr24(ref Bgr24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = (byte)vector.Z; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32(ref Bgra32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - public override bool Equals(object obj) - { - return obj is HalfSingle other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(HalfSingle other) - { - return this.PackedValue == other.PackedValue; - } - - /// - public override string ToString() - { - return this.ToSingle().ToString(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Vector4 ToByteScaledVector4() - { - var 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/HalfTypeHelper.cs b/src/ImageSharp/PixelFormats/HalfTypeHelper.cs index cf8b9f4a23..e8cfaa462e 100644 --- a/src/ImageSharp/PixelFormats/HalfTypeHelper.cs +++ b/src/ImageSharp/PixelFormats/HalfTypeHelper.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Helper methods for packing and unpacking floating point values /// - internal class HalfTypeHelper + internal static class HalfTypeHelper { /// /// Packs a into an @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.PixelFormats return (ushort)s; } - m = m | 0x00800000; + m |= 0x00800000; int t = 14 - e; int a = (1 << (t - 1)) - 1; @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.PixelFormats if ((m & 0x00800000) != 0) { m = 0; - e += 1; + e++; } if (e > 30) @@ -97,11 +97,11 @@ namespace SixLabors.ImageSharp.PixelFormats while ((mantissa & 1024) == 0) { exponent--; - mantissa = mantissa << 1; + mantissa <<= 1; } mantissa &= 0xfffffbff; - result = ((uint)((((uint)value & 0x8000) << 16) | ((exponent + 127) << 23))) | (mantissa << 13); + result = (((uint)value & 0x8000) << 16) | ((exponent + 127) << 23) | (mantissa << 13); } else { @@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.PixelFormats } else { - result = ((((uint)value & 0x8000) << 16) | ((((((uint)value >> 10) & 0x1f) - 15) + 127) << 23)) | (mantissa << 13); + result = (((uint)value & 0x8000) << 16) | ((((((uint)value >> 10) & 0x1f) - 15) + 127) << 23) | (mantissa << 13); } var uif = new Uif { U = result }; diff --git a/src/ImageSharp/PixelFormats/HalfVector2.cs b/src/ImageSharp/PixelFormats/HalfVector2.cs deleted file mode 100644 index befa49736c..0000000000 --- a/src/ImageSharp/PixelFormats/HalfVector2.cs +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed pixel type containing two 16-bit floating-point values. - /// - /// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form. - /// - /// - public struct HalfVector2 : IPixel, IPackedVector - { - /// - /// The maximum byte value. - /// - private static readonly Vector4 MaxBytes = new Vector4(255); - - /// - /// The half vector value. - /// - private static readonly Vector4 Half = new Vector4(0.5F); - - /// - /// Initializes a new instance of the struct. - /// - /// The x-component. - /// The y-component. - public HalfVector2(float x, float y) - { - this.PackedValue = Pack(x, y); - } - - /// - /// Initializes a new instance of the struct. - /// - /// A vector containing the initial values for the components. - public HalfVector2(Vector2 vector) - { - this.PackedValue = Pack(vector.X, vector.Y); - } - - /// - public uint PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(HalfVector2 left, HalfVector2 right) - { - return left.Equals(right); - } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(HalfVector2 left, HalfVector2 right) - { - return !left.Equals(right); - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - /// Expands the packed representation into a . - /// - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector2 ToVector2() - { - Vector2 vector; - vector.X = HalfTypeHelper.Unpack((ushort)this.PackedValue); - vector.Y = HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x10)); - return vector; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - Vector2 scaled = new Vector2(vector.X, vector.Y) * 2F; - scaled -= Vector2.One; - this.PackedValue = Pack(scaled.X, scaled.Y); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - var scaled = this.ToVector2(); - scaled += Vector2.One; - scaled /= 2F; - return new Vector4(scaled, 0F, 1F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - var vector = this.ToVector2(); - return new Vector4(vector.X, vector.Y, 0F, 1F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = 0; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = 0; - dest.A = 255; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToArgb32(ref Argb32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = 0; - dest.A = 255; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgr24(ref Bgr24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = 0; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32(ref Bgra32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = 0; - dest.A = 255; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - public override string ToString() - { - return this.ToVector2().ToString(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - /// - public override bool Equals(object obj) - { - return obj is HalfVector2 other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(HalfVector2 other) - { - return this.PackedValue.Equals(other.PackedValue); - } - - /// - /// Packs the components into a . - /// - /// The x-component - /// The y-component - /// The containing the packed values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(float x, float y) - { - uint num2 = HalfTypeHelper.Pack(x); - uint num = (uint)(HalfTypeHelper.Pack(y) << 0x10); - return num2 | num; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Vector4 ToByteScaledVector4() - { - var 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 deleted file mode 100644 index 885e022921..0000000000 --- a/src/ImageSharp/PixelFormats/HalfVector4.cs +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed pixel type containing four 16-bit floating-point values. - /// - /// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form. - /// - /// - public struct HalfVector4 : IPixel, IPackedVector - { - /// - /// The maximum byte value. - /// - private static readonly Vector4 MaxBytes = new Vector4(255); - - /// - /// The half vector value. - /// - private static readonly Vector4 Half = new Vector4(0.5F); - - /// - /// Initializes a new instance of the struct. - /// - /// The x-component. - /// The y-component. - /// The z-component. - /// The w-component. - public HalfVector4(float x, float y, float z, float w) - { - var vector = new Vector4(x, y, z, w); - this.PackedValue = Pack(ref vector); - } - - /// - /// Initializes a new instance of the struct. - /// - /// A vector containing the initial values for the components - public HalfVector4(Vector4 vector) - { - this.PackedValue = Pack(ref vector); - } - - /// - public ulong PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(HalfVector4 left, HalfVector4 right) - { - return left.Equals(right); - } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(HalfVector4 left, HalfVector4 right) - { - return !left.Equals(right); - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - vector *= 2F; - vector -= Vector4.One; - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - var scaled = this.ToVector4(); - scaled += Vector4.One; - scaled /= 2F; - return scaled; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.PackedValue = Pack(ref vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4( - HalfTypeHelper.Unpack((ushort)this.PackedValue), - HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x10)), - HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x20)), - HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x30))); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = (byte)vector.Z; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToArgb32(ref Argb32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToBgr24(ref Bgr24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = (byte)vector.Z; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32(ref Bgra32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - public override string ToString() - { - return this.ToVector4().ToString(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - /// - public override bool Equals(object obj) - { - return obj is HalfVector4 other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(HalfVector4 other) - { - return this.PackedValue.Equals(other.PackedValue); - } - - /// - /// Packs a into a . - /// - /// The vector containing the values to pack. - /// The containing the packed values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong Pack(ref Vector4 vector) - { - ulong num4 = HalfTypeHelper.Pack(vector.X); - ulong num3 = (ulong)HalfTypeHelper.Pack(vector.Y) << 0x10; - ulong num2 = (ulong)HalfTypeHelper.Pack(vector.Z) << 0x20; - ulong num1 = (ulong)HalfTypeHelper.Pack(vector.W) << 0x30; - return num4 | num3 | num2 | num1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Vector4 ToByteScaledVector4() - { - var 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 ae09af626c..1277406869 100644 --- a/src/ImageSharp/PixelFormats/IPixel.cs +++ b/src/ImageSharp/PixelFormats/IPixel.cs @@ -24,107 +24,95 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - /// An interface that represents a pixel type. + /// A base interface for all pixels, defining the mandatory operations to be implemented by a pixel type. /// public interface IPixel { /// - /// Sets the packed representation from a . + /// Initializes the pixel instance from a generic ("scaled") . /// - /// The vector to create the packed representation from. - void PackFromVector4(Vector4 vector); + /// The vector to load the pixel from. + void FromScaledVector4(Vector4 vector); /// - /// Sets the packed representation from a scaled . - /// - /// The vector to create the packed representation from. - void PackFromScaledVector4(Vector4 vector); - - /// - /// Expands the packed representation into a scaled - /// with values clamped between 0 and 1. + /// Expands the pixel into a generic ("scaled") representation + /// with values scaled and clamped between 0 and 1. /// The vector components are typically expanded in least to greatest significance order. /// /// The . Vector4 ToScaledVector4(); /// - /// Expands the packed representation into a . - /// The vector components are typically expanded in least to greatest significance order. + /// Initializes the pixel instance from a which is specific to the current pixel type. /// - /// The . - Vector4 ToVector4(); + /// The vector to load the pixel from. + void FromVector4(Vector4 vector); /// - /// Packs the pixel from an value. - /// - /// The value. - void PackFromRgba32(Rgba32 source); - - /// - /// Packs the pixel from an value. + /// Expands the pixel into a which is specific to the current pixel type. + /// The vector components are typically expanded in least to greatest significance order. /// - /// The value. - void PackFromRgb48(Rgb48 source); + /// The . + Vector4 ToVector4(); /// - /// Packs the pixel from an value. + /// Initializes the pixel instance from an value. /// - /// The value. - void PackFromRgba64(Rgba64 source); + /// The value. + void FromArgb32(Argb32 source); /// - /// Packs the pixel from an value. + /// Initializes the pixel instance from an value. /// - /// The value. - void PackFromArgb32(Argb32 source); + /// The value. + void FromBgr24(Bgr24 source); /// - /// Packs the pixel from an value. + /// Initializes the pixel instance from an value. /// /// The value. - void PackFromBgra32(Bgra32 source); + void FromBgra32(Bgra32 source); /// - /// Converts the pixel to format. + /// Initializes the pixel instance from an value. /// - /// The destination pixel to write to - void ToRgb24(ref Rgb24 dest); + /// The value. + void FromGray8(Gray8 source); /// - /// Converts the pixel to format. + /// Initializes the pixel instance from an value. /// - /// The destination pixel to write to - void ToRgba32(ref Rgba32 dest); + /// The value. + void FromGray16(Gray16 source); /// - /// Converts the pixel to format. + /// Initializes the pixel instance from an value. /// - /// The destination pixel to write to - void ToRgb48(ref Rgb48 dest); + /// The value. + void FromRgb24(Rgb24 source); /// - /// Converts the pixel to format. + /// Initializes the pixel instance from an value. /// - /// The destination pixel to write to - void ToRgba64(ref Rgba64 dest); + /// The value. + void FromRgba32(Rgba32 source); /// - /// Converts the pixel to format. + /// Convert the pixel instance into representation. /// - /// The destination pixel to write to - void ToArgb32(ref Argb32 dest); + /// The reference to the destination pixel + void ToRgba32(ref Rgba32 dest); /// - /// Converts the pixel to format. + /// Initializes the pixel instance from an value. /// - /// The destination pixel to write to - void ToBgr24(ref Bgr24 dest); + /// The value. + void FromRgb48(Rgb48 source); /// - /// Converts the pixel to format. + /// Initializes the pixel instance from an value. /// - /// The destination pixel to write to - void ToBgra32(ref Bgra32 dest); + /// The value. + void FromRgba64(Rgba64 source); } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs index 0f42e182c5..7e093de042 100644 --- a/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs @@ -14,9 +14,10 @@ namespace SixLabors.ImageSharp.PixelFormats where TPixel : struct, IPixel { /// - /// Thread-safe backing field for . + /// Thread-safe backing field for the constant palettes. /// private static readonly Lazy WebSafePaletteLazy = new Lazy(GetWebSafePalette, true); + private static readonly Lazy WernerPaletteLazy = new Lazy(GetWernerPalette, true); /// /// Represents a matching the W3C definition that has an hex value of #F0F8FF. @@ -729,18 +730,32 @@ namespace SixLabors.ImageSharp.PixelFormats public static readonly TPixel YellowGreen = ColorBuilder.FromRGBA(154, 205, 50, 255); /// - /// Gets a matching the W3C definition of web safe colors. + /// Gets a collection of web safe, colors as defined in the CSS Color Module Level 4. /// public static TPixel[] WebSafePalette => WebSafePaletteLazy.Value; - private static TPixel[] GetWebSafePalette() + /// + /// Gets a collection of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821. + /// The hex codes were collected and defined by Nicholas Rougeux + /// + public static TPixel[] WernerPalette => WernerPaletteLazy.Value; + + private static TPixel[] GetWebSafePalette() => GetPalette(ColorConstants.WebSafeColors); + + private static TPixel[] GetWernerPalette() => GetPalette(ColorConstants.WernerColors); + + private static TPixel[] GetPalette(Rgba32[] palette) { - Rgba32[] constants = ColorConstants.WebSafeColors; - var safe = new TPixel[constants.Length + 1]; + var converted = new TPixel[palette.Length]; + + Span constantsBytes = MemoryMarshal.Cast(palette.AsSpan()); + PixelOperations.Instance.FromRgba32Bytes( + Configuration.Default, + constantsBytes, + converted, + palette.Length); - Span constantsBytes = MemoryMarshal.Cast(constants.AsSpan()); - PixelOperations.Instance.PackFromRgba32Bytes(constantsBytes, safe, constants.Length); - return safe; + return converted; } } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/NormalizedByte2.cs deleted file mode 100644 index 8592fdd6a7..0000000000 --- a/src/ImageSharp/PixelFormats/NormalizedByte2.cs +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed packed pixel type containing two 8-bit signed normalized values, ranging from −1 to 1. - /// - /// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form. - /// - /// - public struct NormalizedByte2 : IPixel, IPackedVector - { - /// - /// The maximum byte value. - /// - private static readonly Vector4 MaxBytes = new Vector4(255); - - /// - /// The half the maximum byte value. - /// - private static readonly Vector4 Half = new Vector4(127); - - /// - /// The vector value used for rounding. - /// - private static readonly Vector4 Round = new Vector4(.5F); - - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public NormalizedByte2(Vector2 vector) - { - this.PackedValue = Pack(vector.X, vector.Y); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The x-component. - /// The y-component. - public NormalizedByte2(float x, float y) - { - this.PackedValue = Pack(x, y); - } - - /// - public ushort PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(NormalizedByte2 left, NormalizedByte2 right) - { - return left.PackedValue == right.PackedValue; - } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(NormalizedByte2 left, NormalizedByte2 right) - { - return left.PackedValue != right.PackedValue; - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - /// Expands the packed representation into a . - /// The vector components are typically expanded in least to greatest significance order. - /// - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector2 ToVector2() - { - return new Vector2( - (sbyte)((this.PackedValue >> 0) & 0xFF) / 127F, - (sbyte)((this.PackedValue >> 8) & 0xFF) / 127F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - Vector2 scaled = new Vector2(vector.X, vector.Y) * 2F; - scaled -= Vector2.One; - this.PackedValue = Pack(scaled.X, scaled.Y); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - var scaled = this.ToVector2(); - scaled += Vector2.One; - scaled /= 2F; - return new Vector4(scaled, 0F, 1F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4(this.ToVector2(), 0F, 1F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - Vector4 vector = source.ToByteScaledVector4(); - vector -= Round; - vector -= Half; - vector -= Round; - vector /= Half; - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - Vector4 vector = source.ToByteScaledVector4(); - vector -= Round; - vector -= Half; - vector -= Round; - vector /= Half; - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - Vector4 vector = source.ToByteScaledVector4(); - vector -= Round; - vector -= Half; - vector -= Round; - vector /= Half; - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = 0; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = 0; - dest.A = 255; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToArgb32(ref Argb32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = 0; - dest.A = 255; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgr24(ref Bgr24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = 0; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32(ref Bgra32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = 0; - dest.A = 255; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - public override bool Equals(object obj) - { - return obj is NormalizedByte2 other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(NormalizedByte2 other) - { - return this.PackedValue == other.PackedValue; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - /// - public override string ToString() - { - return this.PackedValue.ToString("X"); - } - - /// - /// Packs the components into a . - /// - /// The x-component - /// The y-component - /// The containing the packed values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ushort Pack(float x, float y) - { - int byte2 = ((ushort)Math.Round(x.Clamp(-1F, 1F) * 127F) & 0xFF) << 0; - int byte1 = ((ushort)Math.Round(y.Clamp(-1F, 1F) * 127F) & 0xFF) << 8; - - return (ushort)(byte2 | byte1); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Vector4 ToByteScaledVector4() - { - var 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 deleted file mode 100644 index 293d536e53..0000000000 --- a/src/ImageSharp/PixelFormats/NormalizedByte4.cs +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed pixel type containing four 8-bit signed normalized values, ranging from −1 to 1. - /// - /// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form. - /// - /// - public struct NormalizedByte4 : IPixel, IPackedVector - { - /// - /// The maximum byte value. - /// - private static readonly Vector4 MaxBytes = new Vector4(255); - - /// - /// The half the maximum byte value. - /// - private static readonly Vector4 Half = new Vector4(127); - - /// - /// The vector value used for rounding. - /// - private static readonly Vector4 Round = new Vector4(.5F); - - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public NormalizedByte4(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The x-component. - /// The y-component. - /// The z-component. - /// The w-component. - public NormalizedByte4(float x, float y, float z, float w) - { - this.PackedValue = Pack(x, y, z, w); - } - - /// - public uint PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(NormalizedByte4 left, NormalizedByte4 right) - { - return left.PackedValue == right.PackedValue; - } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(NormalizedByte4 left, NormalizedByte4 right) - { - return left.PackedValue != right.PackedValue; - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - vector *= 2F; - vector -= Vector4.One; - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - var scaled = this.ToVector4(); - scaled += Vector4.One; - scaled /= 2F; - return scaled; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4( - (sbyte)((this.PackedValue >> 0) & 0xFF) / 127F, - (sbyte)((this.PackedValue >> 8) & 0xFF) / 127F, - (sbyte)((this.PackedValue >> 16) & 0xFF) / 127F, - (sbyte)((this.PackedValue >> 24) & 0xFF) / 127F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - Vector4 vector = source.ToByteScaledVector4(); - vector -= Round; - vector -= Half; - vector -= Round; - vector /= Half; - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - Vector4 vector = source.ToByteScaledVector4(); - vector -= Round; - vector -= Half; - vector -= Round; - vector /= Half; - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - Vector4 vector = source.ToByteScaledVector4(); - vector -= Round; - vector -= Half; - vector -= Round; - vector /= Half; - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = (byte)vector.Z; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToArgb32(ref Argb32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToBgr24(ref Bgr24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = (byte)vector.Z; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32(ref Bgra32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - public override bool Equals(object obj) - { - return obj is NormalizedByte4 other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(NormalizedByte4 other) - { - return this.PackedValue == other.PackedValue; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - /// - public override string ToString() - { - return this.PackedValue.ToString("X"); - } - - /// - /// Packs the components into a . - /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - /// The containing the packed values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(float x, float y, float z, float w) - { - uint byte4 = ((uint)Math.Round(x.Clamp(-1F, 1F) * 127F) & 0xFF) << 0; - uint byte3 = ((uint)Math.Round(y.Clamp(-1F, 1F) * 127F) & 0xFF) << 8; - uint byte2 = ((uint)Math.Round(z.Clamp(-1F, 1F) * 127F) & 0xFF) << 16; - uint byte1 = ((uint)Math.Round(w.Clamp(-1F, 1F) * 127F) & 0xFF) << 24; - - return byte4 | byte3 | byte2 | byte1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Vector4 ToByteScaledVector4() - { - var 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 deleted file mode 100644 index 1ced412d06..0000000000 --- a/src/ImageSharp/PixelFormats/NormalizedShort2.cs +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed pixel type containing two 16-bit signed normalized values, ranging from −1 to 1. - /// - /// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form. - /// - /// - public struct NormalizedShort2 : IPixel, IPackedVector - { - /// - /// The maximum byte value. - /// - private static readonly Vector4 MaxBytes = new Vector4(255); - - /// - /// The half the maximum byte value. - /// - private static readonly Vector4 Half = new Vector4(127); - - /// - /// The vector value used for rounding. - /// - private static readonly Vector4 Round = new Vector4(.5F); - - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public NormalizedShort2(Vector2 vector) - { - this.PackedValue = Pack(vector.X, vector.Y); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The x-component. - /// The y-component. - public NormalizedShort2(float x, float y) - { - this.PackedValue = Pack(x, y); - } - - /// - public uint PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(NormalizedShort2 left, NormalizedShort2 right) - { - return left.Equals(right); - } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(NormalizedShort2 left, NormalizedShort2 right) - { - return !left.Equals(right); - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - Vector2 scaled = new Vector2(vector.X, vector.Y) * 2F; - scaled -= Vector2.One; - this.PackedValue = Pack(scaled.X, scaled.Y); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - var scaled = this.ToVector2(); - scaled += Vector2.One; - scaled /= 2F; - return new Vector4(scaled, 0F, 1F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4(this.ToVector2(), 0, 1); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - Vector4 vector = source.ToByteScaledVector4(); - vector -= Round; - vector -= Half; - vector -= Round; - vector /= Half; - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - Vector4 vector = source.ToByteScaledVector4(); - vector -= Round; - vector -= Half; - vector -= Round; - vector /= Half; - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - Vector4 vector = source.ToByteScaledVector4(); - vector -= Round; - vector -= Half; - vector -= Round; - vector /= Half; - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)MathF.Round(vector.X); - dest.G = (byte)MathF.Round(vector.Y); - dest.B = 0; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToArgb32(ref Argb32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToBgr24(ref Bgr24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)MathF.Round(vector.X); - dest.G = (byte)MathF.Round(vector.Y); - dest.B = 0; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32(ref Bgra32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - /// Expands the packed representation into a . - /// The vector components are typically expanded in least to greatest significance order. - /// - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector2 ToVector2() - { - const float MaxVal = 0x7FFF; - - return new Vector2( - (short)(this.PackedValue & 0xFFFF) / MaxVal, - (short)(this.PackedValue >> 0x10) / MaxVal); - } - - /// - public override bool Equals(object obj) - { - return obj is NormalizedShort2 other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(NormalizedShort2 other) - { - return this.PackedValue.Equals(other.PackedValue); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - { - return this.PackedValue.GetHashCode(); - } - - /// - public override string ToString() - { - return this.PackedValue.ToString("X"); - } - - /// - /// Packs the components into a . - /// - /// The x-component - /// The y-component - /// The containing the packed values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(float x, float y) - { - const float MaxPos = 0x7FFF; - const float MinNeg = -MaxPos; - - // Clamp the value between min and max values - // Round rather than truncate. - uint word2 = (uint)((int)MathF.Round(x * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF); - uint word1 = (uint)(((int)MathF.Round(y * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x10); - - return word2 | word1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Vector4 ToByteScaledVector4() - { - var 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 deleted file mode 100644 index 25b26fa7f7..0000000000 --- a/src/ImageSharp/PixelFormats/NormalizedShort4.cs +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed pixel type containing four 16-bit signed normalized values, ranging from −1 to 1. - /// - /// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form. - /// - /// - public struct NormalizedShort4 : IPixel, IPackedVector - { - /// - /// The maximum byte value. - /// - private static readonly Vector4 MaxBytes = new Vector4(255); - - /// - /// The half the maximum byte value. - /// - private static readonly Vector4 Half = new Vector4(127); - - /// - /// The vector value used for rounding. - /// - private static readonly Vector4 Round = new Vector4(.5F); - - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public NormalizedShort4(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The x-component. - /// The y-component. - /// The z-component. - /// The w-component. - public NormalizedShort4(float x, float y, float z, float w) - { - this.PackedValue = Pack(x, y, z, w); - } - - /// - public ulong PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(NormalizedShort4 left, NormalizedShort4 right) - { - return left.Equals(right); - } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(NormalizedShort4 left, NormalizedShort4 right) - { - return !left.Equals(right); - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - vector *= 2F; - vector -= Vector4.One; - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - var scaled = this.ToVector4(); - scaled += Vector4.One; - scaled /= 2F; - return scaled; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - const float MaxVal = 0x7FFF; - - return new Vector4( - (short)((this.PackedValue >> 0x00) & 0xFFFF) / MaxVal, - (short)((this.PackedValue >> 0x10) & 0xFFFF) / MaxVal, - (short)((this.PackedValue >> 0x20) & 0xFFFF) / MaxVal, - (short)((this.PackedValue >> 0x30) & 0xFFFF) / MaxVal); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - Vector4 vector = source.ToByteScaledVector4(); - vector -= Round; - vector -= Half; - vector -= Round; - vector /= Half; - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - Vector4 vector = source.ToByteScaledVector4(); - vector -= Round; - vector -= Half; - vector -= Round; - vector /= Half; - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - Vector4 vector = source.ToByteScaledVector4(); - vector -= Round; - vector -= Half; - vector -= Round; - vector /= Half; - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToRgba32(ref Rgba32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToArgb32(ref Argb32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToBgr24(ref Bgr24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToBgra32(ref Bgra32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - public override bool Equals(object obj) - { - return obj is NormalizedShort4 other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(NormalizedShort4 other) - { - return this.PackedValue.Equals(other.PackedValue); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - { - return this.PackedValue.GetHashCode(); - } - - /// - public override string ToString() - { - return this.PackedValue.ToString("X"); - } - - /// - /// Packs the components into a . - /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - /// The containing the packed values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong Pack(float x, float y, float z, float w) - { - const float MaxPos = 0x7FFF; - const float MinNeg = -MaxPos; - - // Clamp the value between min and max values - ulong word4 = ((ulong)MathF.Round(x * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x00; - ulong word3 = ((ulong)MathF.Round(y * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x10; - ulong word2 = ((ulong)MathF.Round(z * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x20; - ulong word1 = ((ulong)MathF.Round(w * MaxPos).Clamp(MinNeg, MaxPos) & 0xFFFF) << 0x30; - - return word4 | word3 | word2 | word1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Vector4 ToByteScaledVector4() - { - var 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/PixelBlenders/DefaultPixelBlenders.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs index 19d1c5dad1..1d3cb53afc 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs @@ -1,11 +1,4 @@ - - - - - - - -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. // @@ -33,7 +26,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders where TPixel : struct, IPixel { - internal class NormalSrc : PixelBlender { /// @@ -45,7 +37,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.NormalSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.NormalSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -69,7 +61,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplySrc : PixelBlender { /// @@ -81,7 +72,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.MultiplySrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.MultiplySrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -105,7 +96,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddSrc : PixelBlender { /// @@ -117,7 +107,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.AddSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.AddSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -141,7 +131,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractSrc : PixelBlender { /// @@ -153,7 +142,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.SubtractSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.SubtractSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -177,7 +166,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenSrc : PixelBlender { /// @@ -189,7 +177,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.ScreenSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.ScreenSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -213,7 +201,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenSrc : PixelBlender { /// @@ -225,7 +212,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.DarkenSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.DarkenSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -249,7 +236,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenSrc : PixelBlender { /// @@ -261,7 +247,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.LightenSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.LightenSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -285,7 +271,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlaySrc : PixelBlender { /// @@ -297,7 +282,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.OverlaySrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.OverlaySrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -321,7 +306,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightSrc : PixelBlender { /// @@ -333,7 +317,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.HardLightSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.HardLightSrc(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -357,7 +341,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalSrcAtop : PixelBlender { /// @@ -369,7 +352,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.NormalSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.NormalSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -393,7 +376,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplySrcAtop : PixelBlender { /// @@ -405,7 +387,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.MultiplySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.MultiplySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -429,7 +411,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddSrcAtop : PixelBlender { /// @@ -441,7 +422,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.AddSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.AddSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -465,7 +446,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractSrcAtop : PixelBlender { /// @@ -477,7 +457,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.SubtractSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.SubtractSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -501,7 +481,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenSrcAtop : PixelBlender { /// @@ -513,7 +492,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.ScreenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.ScreenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -537,7 +516,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenSrcAtop : PixelBlender { /// @@ -549,7 +527,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.DarkenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.DarkenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -573,7 +551,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenSrcAtop : PixelBlender { /// @@ -585,7 +562,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.LightenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.LightenSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -609,7 +586,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlaySrcAtop : PixelBlender { /// @@ -621,7 +597,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.OverlaySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.OverlaySrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -645,7 +621,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightSrcAtop : PixelBlender { /// @@ -657,7 +632,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.HardLightSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.HardLightSrcAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -681,7 +656,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalSrcOver : PixelBlender { /// @@ -693,7 +667,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.NormalSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.NormalSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -717,7 +691,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplySrcOver : PixelBlender { /// @@ -729,7 +702,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.MultiplySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.MultiplySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -753,7 +726,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddSrcOver : PixelBlender { /// @@ -765,7 +737,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.AddSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.AddSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -789,7 +761,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractSrcOver : PixelBlender { /// @@ -801,7 +772,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.SubtractSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.SubtractSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -825,7 +796,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenSrcOver : PixelBlender { /// @@ -837,7 +807,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.ScreenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.ScreenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -861,7 +831,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenSrcOver : PixelBlender { /// @@ -873,7 +842,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.DarkenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.DarkenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -897,7 +866,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenSrcOver : PixelBlender { /// @@ -909,7 +877,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.LightenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.LightenSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -933,7 +901,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlaySrcOver : PixelBlender { /// @@ -945,7 +912,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.OverlaySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.OverlaySrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -969,7 +936,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightSrcOver : PixelBlender { /// @@ -981,7 +947,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.HardLightSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.HardLightSrcOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1005,7 +971,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalSrcIn : PixelBlender { /// @@ -1017,7 +982,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.NormalSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.NormalSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1041,7 +1006,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplySrcIn : PixelBlender { /// @@ -1053,7 +1017,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.MultiplySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.MultiplySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1077,7 +1041,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddSrcIn : PixelBlender { /// @@ -1089,7 +1052,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.AddSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.AddSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1113,7 +1076,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractSrcIn : PixelBlender { /// @@ -1125,7 +1087,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.SubtractSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.SubtractSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1149,7 +1111,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenSrcIn : PixelBlender { /// @@ -1161,7 +1122,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.ScreenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.ScreenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1185,7 +1146,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenSrcIn : PixelBlender { /// @@ -1197,7 +1157,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.DarkenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.DarkenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1221,7 +1181,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenSrcIn : PixelBlender { /// @@ -1233,7 +1192,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.LightenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.LightenSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1257,7 +1216,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlaySrcIn : PixelBlender { /// @@ -1269,7 +1227,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.OverlaySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.OverlaySrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1293,7 +1251,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightSrcIn : PixelBlender { /// @@ -1305,7 +1262,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.HardLightSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.HardLightSrcIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1329,7 +1286,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalSrcOut : PixelBlender { /// @@ -1341,7 +1297,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.NormalSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.NormalSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1365,7 +1321,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplySrcOut : PixelBlender { /// @@ -1377,7 +1332,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.MultiplySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.MultiplySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1401,7 +1356,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddSrcOut : PixelBlender { /// @@ -1413,7 +1367,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.AddSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.AddSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1437,7 +1391,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractSrcOut : PixelBlender { /// @@ -1449,7 +1402,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.SubtractSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.SubtractSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1473,7 +1426,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenSrcOut : PixelBlender { /// @@ -1485,7 +1437,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.ScreenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.ScreenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1509,7 +1461,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenSrcOut : PixelBlender { /// @@ -1521,7 +1472,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.DarkenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.DarkenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1545,7 +1496,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenSrcOut : PixelBlender { /// @@ -1557,7 +1507,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.LightenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.LightenSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1581,7 +1531,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlaySrcOut : PixelBlender { /// @@ -1593,7 +1542,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.OverlaySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.OverlaySrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1617,7 +1566,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightSrcOut : PixelBlender { /// @@ -1629,7 +1577,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.HardLightSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.HardLightSrcOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1653,7 +1601,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalDest : PixelBlender { /// @@ -1665,7 +1612,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.NormalDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.NormalDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1689,7 +1636,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyDest : PixelBlender { /// @@ -1701,7 +1647,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.MultiplyDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.MultiplyDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1725,7 +1671,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddDest : PixelBlender { /// @@ -1737,7 +1682,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.AddDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.AddDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1761,7 +1706,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractDest : PixelBlender { /// @@ -1773,7 +1717,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.SubtractDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.SubtractDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1797,7 +1741,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenDest : PixelBlender { /// @@ -1809,7 +1752,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.ScreenDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.ScreenDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1833,7 +1776,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenDest : PixelBlender { /// @@ -1845,7 +1787,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.DarkenDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.DarkenDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1869,7 +1811,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenDest : PixelBlender { /// @@ -1881,7 +1822,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.LightenDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.LightenDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1905,7 +1846,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayDest : PixelBlender { /// @@ -1917,7 +1857,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.OverlayDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.OverlayDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1941,7 +1881,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightDest : PixelBlender { /// @@ -1953,7 +1892,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.HardLightDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.HardLightDest(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -1977,7 +1916,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalDestAtop : PixelBlender { /// @@ -1989,7 +1927,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.NormalDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.NormalDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2013,7 +1951,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyDestAtop : PixelBlender { /// @@ -2025,7 +1962,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.MultiplyDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.MultiplyDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2049,7 +1986,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddDestAtop : PixelBlender { /// @@ -2061,7 +1997,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.AddDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.AddDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2085,7 +2021,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractDestAtop : PixelBlender { /// @@ -2097,7 +2032,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.SubtractDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.SubtractDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2121,7 +2056,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenDestAtop : PixelBlender { /// @@ -2133,7 +2067,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.ScreenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.ScreenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2157,7 +2091,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenDestAtop : PixelBlender { /// @@ -2169,7 +2102,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.DarkenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.DarkenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2193,7 +2126,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenDestAtop : PixelBlender { /// @@ -2205,7 +2137,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.LightenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.LightenDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2229,7 +2161,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayDestAtop : PixelBlender { /// @@ -2241,7 +2172,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.OverlayDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.OverlayDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2265,7 +2196,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightDestAtop : PixelBlender { /// @@ -2277,7 +2207,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.HardLightDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.HardLightDestAtop(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2301,7 +2231,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalDestOver : PixelBlender { /// @@ -2313,7 +2242,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.NormalDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.NormalDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2337,7 +2266,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyDestOver : PixelBlender { /// @@ -2349,7 +2277,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.MultiplyDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.MultiplyDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2373,7 +2301,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddDestOver : PixelBlender { /// @@ -2385,7 +2312,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.AddDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.AddDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2409,7 +2336,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractDestOver : PixelBlender { /// @@ -2421,7 +2347,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.SubtractDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.SubtractDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2445,7 +2371,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenDestOver : PixelBlender { /// @@ -2457,7 +2382,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.ScreenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.ScreenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2481,7 +2406,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenDestOver : PixelBlender { /// @@ -2493,7 +2417,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.DarkenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.DarkenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2517,7 +2441,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenDestOver : PixelBlender { /// @@ -2529,7 +2452,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.LightenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.LightenDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2553,7 +2476,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayDestOver : PixelBlender { /// @@ -2565,7 +2487,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.OverlayDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.OverlayDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2589,7 +2511,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightDestOver : PixelBlender { /// @@ -2601,7 +2522,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.HardLightDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.HardLightDestOver(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2625,7 +2546,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalDestIn : PixelBlender { /// @@ -2637,7 +2557,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.NormalDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.NormalDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2661,7 +2581,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyDestIn : PixelBlender { /// @@ -2673,7 +2592,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.MultiplyDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.MultiplyDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2697,7 +2616,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddDestIn : PixelBlender { /// @@ -2709,7 +2627,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.AddDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.AddDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2733,7 +2651,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractDestIn : PixelBlender { /// @@ -2745,7 +2662,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.SubtractDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.SubtractDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2769,7 +2686,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenDestIn : PixelBlender { /// @@ -2781,7 +2697,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.ScreenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.ScreenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2805,7 +2721,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenDestIn : PixelBlender { /// @@ -2817,7 +2732,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.DarkenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.DarkenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2841,7 +2756,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenDestIn : PixelBlender { /// @@ -2853,7 +2767,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.LightenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.LightenDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2877,7 +2791,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayDestIn : PixelBlender { /// @@ -2889,7 +2802,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.OverlayDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.OverlayDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2913,7 +2826,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightDestIn : PixelBlender { /// @@ -2925,7 +2837,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.HardLightDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.HardLightDestIn(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2949,7 +2861,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalDestOut : PixelBlender { /// @@ -2961,7 +2872,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.NormalDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.NormalDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -2985,7 +2896,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyDestOut : PixelBlender { /// @@ -2997,7 +2907,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.MultiplyDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.MultiplyDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3021,7 +2931,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddDestOut : PixelBlender { /// @@ -3033,7 +2942,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.AddDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.AddDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3057,7 +2966,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractDestOut : PixelBlender { /// @@ -3069,7 +2977,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.SubtractDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.SubtractDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3093,7 +3001,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenDestOut : PixelBlender { /// @@ -3105,7 +3012,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.ScreenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.ScreenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3129,7 +3036,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenDestOut : PixelBlender { /// @@ -3141,7 +3047,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.DarkenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.DarkenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3165,7 +3071,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenDestOut : PixelBlender { /// @@ -3177,7 +3082,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.LightenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.LightenDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3201,7 +3106,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayDestOut : PixelBlender { /// @@ -3213,7 +3117,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.OverlayDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.OverlayDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3237,7 +3141,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightDestOut : PixelBlender { /// @@ -3249,7 +3152,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.HardLightDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.HardLightDestOut(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3273,7 +3176,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalClear : PixelBlender { /// @@ -3285,7 +3187,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.NormalClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.NormalClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3309,7 +3211,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyClear : PixelBlender { /// @@ -3321,7 +3222,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.MultiplyClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.MultiplyClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3345,7 +3246,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddClear : PixelBlender { /// @@ -3357,7 +3257,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.AddClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.AddClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3381,7 +3281,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractClear : PixelBlender { /// @@ -3393,7 +3292,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.SubtractClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.SubtractClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3417,7 +3316,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenClear : PixelBlender { /// @@ -3429,7 +3327,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.ScreenClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.ScreenClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3453,7 +3351,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenClear : PixelBlender { /// @@ -3465,7 +3362,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.DarkenClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.DarkenClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3489,7 +3386,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenClear : PixelBlender { /// @@ -3501,7 +3397,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.LightenClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.LightenClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3525,7 +3421,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayClear : PixelBlender { /// @@ -3537,7 +3432,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.OverlayClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.OverlayClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3561,7 +3456,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightClear : PixelBlender { /// @@ -3573,7 +3467,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.HardLightClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.HardLightClear(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3597,7 +3491,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class NormalXor : PixelBlender { /// @@ -3609,7 +3502,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.NormalXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.NormalXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3633,7 +3526,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class MultiplyXor : PixelBlender { /// @@ -3645,7 +3537,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.MultiplyXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.MultiplyXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3669,7 +3561,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class AddXor : PixelBlender { /// @@ -3681,7 +3572,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.AddXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.AddXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3705,7 +3596,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class SubtractXor : PixelBlender { /// @@ -3717,7 +3607,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.SubtractXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.SubtractXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3741,7 +3631,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class ScreenXor : PixelBlender { /// @@ -3753,7 +3642,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.ScreenXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.ScreenXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3777,7 +3666,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class DarkenXor : PixelBlender { /// @@ -3789,7 +3677,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.DarkenXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.DarkenXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3813,7 +3701,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class LightenXor : PixelBlender { /// @@ -3825,7 +3712,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.LightenXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.LightenXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3849,7 +3736,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class OverlayXor : PixelBlender { /// @@ -3861,7 +3747,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.OverlayXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.OverlayXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3885,7 +3771,6 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - internal class HardLightXor : PixelBlender { /// @@ -3897,7 +3782,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders public override TPixel Blend(TPixel background, TPixel source, float amount) { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.HardLightXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + dest.FromScaledVector4(PorterDuffFunctions.HardLightXor(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); return dest; } @@ -3921,6 +3806,5 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders } } - } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt index f776da7a02..b7ea7a9d43 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt @@ -1,114 +1,114 @@ -<# -// Copyright (c) Six Labors 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" #> -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -// +<# +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. +#> +<#@ 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" #> +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// using System; using System.Numerics; using System.Buffers; using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; - -namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders -{ - /// - /// Collection of Porter Duff alpha blending functions applying different composition models. - /// - /// - /// These functions are designed to be a general solution for all color cases, - /// that is, they take in account the alpha value of both the backdrop - /// and source, and there's no need to alpha-premultiply neither the backdrop - /// nor the source. - /// Note there are faster functions for when the backdrop color is known - /// to be opaque - /// - internal static class DefaultPixelBlenders - where TPixel : struct, IPixel - { - -<# - string[] composers = new []{ - "Src", - "SrcAtop", - "SrcOver", - "SrcIn", - "SrcOut", - "Dest", - "DestAtop", - "DestOver", - "DestIn", - "DestOut", - "Clear", - "Xor", - }; - - string[] blenders = new []{ - "Normal", - "Multiply", - "Add", - "Subtract", - "Screen", - "Darken", - "Lighten", - "Overlay", - "HardLight" - }; - - foreach(var composer in composers) { - foreach(var blender in blenders) { - - string blender_composer= $"{blender}{composer}"; - -#> - internal class <#= blender_composer#> : PixelBlender - { - /// - /// Gets the static instance of this blender. - /// - public static <#=blender_composer#> Instance { get; } = new <#=blender_composer#>(); - - /// - public override TPixel Blend(TPixel background, TPixel source, float amount) - { +using SixLabors.Memory; + +namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders +{ + /// + /// Collection of Porter Duff alpha blending functions applying different composition models. + /// + /// + /// These functions are designed to be a general solution for all color cases, + /// that is, they take in account the alpha value of both the backdrop + /// and source, and there's no need to alpha-premultiply neither the backdrop + /// nor the source. + /// Note there are faster functions for when the backdrop color is known + /// to be opaque + /// + internal static class DefaultPixelBlenders + where TPixel : struct, IPixel + { + +<# + string[] composers = new []{ + "Src", + "SrcAtop", + "SrcOver", + "SrcIn", + "SrcOut", + "Dest", + "DestAtop", + "DestOver", + "DestIn", + "DestOut", + "Clear", + "Xor", + }; + + string[] blenders = new []{ + "Normal", + "Multiply", + "Add", + "Subtract", + "Screen", + "Darken", + "Lighten", + "Overlay", + "HardLight" + }; + + foreach(var composer in composers) { + foreach(var blender in blenders) { + + string blender_composer= $"{blender}{composer}"; + +#> + internal class <#= blender_composer#> : PixelBlender + { + /// + /// Gets the static instance of this blender. + /// + public static <#=blender_composer#> Instance { get; } = new <#=blender_composer#>(); + + /// + public override TPixel Blend(TPixel background, TPixel source, float amount) + { TPixel dest = default; - dest.PackFromScaledVector4(PorterDuffFunctions.<#=blender_composer#>(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); - return dest; - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) - { - amount = amount.Clamp(0, 1); - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); - } - } - - /// - protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) - { - for (int i = 0; i < destination.Length; i++) - { - destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount[i].Clamp(0, 1)); - } - } - } - -<# - } - } - -#> - } + dest.FromScaledVector4(PorterDuffFunctions.<#=blender_composer#>(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); + return dest; + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + { + amount = amount.Clamp(0, 1); + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); + } + } + + /// + protected override void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount[i].Clamp(0, 1)); + } + } + } + +<# + } + } + +#> + } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs index 0a6ef60eca..64148746e0 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs @@ -118,7 +118,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(NormalSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(NormalSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -130,7 +130,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(NormalSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(NormalSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -142,7 +142,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(NormalSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(NormalSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -154,7 +154,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(NormalSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(NormalSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -166,7 +166,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(NormalSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(NormalSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -178,7 +178,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(NormalDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(NormalDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -190,7 +190,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(NormalDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(NormalDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -202,7 +202,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(NormalDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(NormalDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -214,7 +214,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(NormalDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(NormalDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -226,7 +226,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(NormalDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(NormalDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -238,7 +238,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(NormalClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(NormalClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -250,7 +250,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(NormalXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(NormalXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -356,7 +356,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(MultiplySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(MultiplySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -368,7 +368,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(MultiplySrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(MultiplySrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -380,7 +380,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(MultiplySrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(MultiplySrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -392,7 +392,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(MultiplySrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(MultiplySrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -404,7 +404,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(MultiplySrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(MultiplySrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -416,7 +416,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(MultiplyDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(MultiplyDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -428,7 +428,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(MultiplyDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(MultiplyDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -440,7 +440,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(MultiplyDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(MultiplyDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -452,7 +452,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(MultiplyDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(MultiplyDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -464,7 +464,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(MultiplyDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(MultiplyDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -476,7 +476,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(MultiplyClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(MultiplyClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -488,7 +488,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(MultiplyXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(MultiplyXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -594,7 +594,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(AddSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(AddSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -606,7 +606,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(AddSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(AddSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -618,7 +618,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(AddSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(AddSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -630,7 +630,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(AddSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(AddSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -642,7 +642,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(AddSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(AddSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -654,7 +654,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(AddDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(AddDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -666,7 +666,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(AddDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(AddDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -678,7 +678,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(AddDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(AddDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -690,7 +690,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(AddDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(AddDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -702,7 +702,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(AddDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(AddDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -714,7 +714,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(AddClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(AddClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -726,7 +726,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(AddXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(AddXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -832,7 +832,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(SubtractSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(SubtractSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -844,7 +844,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(SubtractSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(SubtractSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -856,7 +856,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(SubtractSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(SubtractSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -868,7 +868,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(SubtractSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(SubtractSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -880,7 +880,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(SubtractSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(SubtractSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -892,7 +892,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(SubtractDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(SubtractDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -904,7 +904,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(SubtractDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(SubtractDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -916,7 +916,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(SubtractDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(SubtractDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -928,7 +928,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(SubtractDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(SubtractDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -940,7 +940,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(SubtractDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(SubtractDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -952,7 +952,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(SubtractClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(SubtractClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -964,7 +964,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(SubtractXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(SubtractXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1070,7 +1070,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(ScreenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(ScreenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1082,7 +1082,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(ScreenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(ScreenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1094,7 +1094,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(ScreenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(ScreenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1106,7 +1106,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(ScreenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(ScreenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1118,7 +1118,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(ScreenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(ScreenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1130,7 +1130,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(ScreenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(ScreenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1142,7 +1142,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(ScreenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(ScreenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1154,7 +1154,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(ScreenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(ScreenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1166,7 +1166,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(ScreenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(ScreenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1178,7 +1178,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(ScreenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(ScreenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1190,7 +1190,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(ScreenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(ScreenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1202,7 +1202,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(ScreenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(ScreenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1308,7 +1308,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(DarkenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(DarkenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1320,7 +1320,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(DarkenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(DarkenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1332,7 +1332,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(DarkenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(DarkenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1344,7 +1344,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(DarkenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(DarkenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1356,7 +1356,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(DarkenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(DarkenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1368,7 +1368,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(DarkenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(DarkenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1380,7 +1380,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(DarkenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(DarkenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1392,7 +1392,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(DarkenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(DarkenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1404,7 +1404,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(DarkenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(DarkenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1416,7 +1416,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(DarkenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(DarkenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1428,7 +1428,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(DarkenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(DarkenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1440,7 +1440,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(DarkenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(DarkenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1546,7 +1546,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(LightenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(LightenSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1558,7 +1558,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(LightenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(LightenSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1570,7 +1570,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(LightenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(LightenSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1582,7 +1582,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(LightenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(LightenSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1594,7 +1594,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(LightenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(LightenSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1606,7 +1606,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(LightenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(LightenDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1618,7 +1618,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(LightenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(LightenDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1630,7 +1630,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(LightenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(LightenDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1642,7 +1642,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(LightenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(LightenDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1654,7 +1654,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(LightenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(LightenDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1666,7 +1666,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(LightenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(LightenClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1678,7 +1678,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(LightenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(LightenXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1784,7 +1784,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(OverlaySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(OverlaySrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1796,7 +1796,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(OverlaySrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(OverlaySrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1808,7 +1808,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(OverlaySrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(OverlaySrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1820,7 +1820,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(OverlaySrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(OverlaySrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1832,7 +1832,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(OverlaySrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(OverlaySrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1844,7 +1844,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(OverlayDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(OverlayDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1856,7 +1856,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(OverlayDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(OverlayDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1868,7 +1868,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(OverlayDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(OverlayDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1880,7 +1880,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(OverlayDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(OverlayDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1892,7 +1892,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(OverlayDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(OverlayDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1904,7 +1904,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(OverlayClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(OverlayClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -1916,7 +1916,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(OverlayXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(OverlayXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -2022,7 +2022,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(HardLightSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(HardLightSrc(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -2034,7 +2034,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(HardLightSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(HardLightSrcAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -2046,7 +2046,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(HardLightSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(HardLightSrcOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -2058,7 +2058,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(HardLightSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(HardLightSrcIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -2070,7 +2070,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(HardLightSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(HardLightSrcOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -2082,7 +2082,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(HardLightDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(HardLightDest(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -2094,7 +2094,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(HardLightDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(HardLightDestAtop(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -2106,7 +2106,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(HardLightDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(HardLightDestOver(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -2118,7 +2118,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(HardLightDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(HardLightDestIn(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -2130,7 +2130,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(HardLightDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(HardLightDestOut(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -2142,7 +2142,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(HardLightClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(HardLightClear(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } @@ -2154,7 +2154,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(HardLightXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(HardLightXor(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } diff --git a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt index 73c835e606..e21a78031f 100644 --- a/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt +++ b/src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt @@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders { opacity = opacity.Clamp(0, 1); TPixel dest = default; - dest.PackFromScaledVector4(<#=blender#><#=composer#>(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); + dest.FromScaledVector4(<#=blender#><#=composer#>(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); return dest; } diff --git a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs index 63a101656e..5c8e506ae1 100644 --- a/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs @@ -1,31 +1,31 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; using System.Buffers; using System.Numerics; + using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Abstract base class for calling pixel composition functions - /// - /// The type of the pixel - internal abstract class PixelBlender - where TPixel : struct, IPixel - { - /// - /// Blend 2 pixels together. - /// - /// The background color. - /// The source color. - /// - /// A value between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. - /// - /// The final pixel value after composition + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Abstract base class for calling pixel composition functions + /// + /// The type of the pixel + internal abstract class PixelBlender + where TPixel : struct, IPixel + { + /// + /// Blend 2 pixels together. + /// + /// The background color. + /// The source color. + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// + /// The final pixel value after composition public abstract TPixel Blend(TPixel background, TPixel source, float amount); /// @@ -34,11 +34,15 @@ namespace SixLabors.ImageSharp.PixelFormats /// destination span /// the background span /// the source span - /// - /// A value between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// - protected abstract void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount); + protected abstract void BlendFunction( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount); /// /// Blend 2 rows together. @@ -46,59 +50,83 @@ namespace SixLabors.ImageSharp.PixelFormats /// destination span /// the background span /// the source span - /// - /// A span with values between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// - protected abstract void BlendFunction(Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount); + protected abstract void BlendFunction( + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount); /// /// Blends 2 rows together /// - /// memory manager to use internally + /// to use internally /// the destination span /// the background span /// the source span - /// - /// A span with values between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// - public void Blend(MemoryAllocator memoryManager, Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount) { - this.Blend(memoryManager, destination, background, source, amount); + this.Blend(configuration, destination, background, source, amount); } /// /// Blends 2 rows together /// /// the pixel format of the source span - /// memory manager to use internally + /// to use internally /// the destination span /// the background span /// the source span - /// - /// A span with values between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// + /// A span with values between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// - public void Blend(MemoryAllocator memoryManager, Span destination, ReadOnlySpan background, ReadOnlySpan source, ReadOnlySpan amount) + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + ReadOnlySpan amount) where TPixelSrc : struct, IPixel { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); - using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = + configuration.MemoryAllocator.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - PixelOperations.Instance.ToScaledVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToScaledVector4(source, sourceSpan, destination.Length); + PixelOperations.Instance.ToScaledVector4( + configuration, + background.Slice(0, background.Length), + backgroundSpan); + PixelOperations.Instance.ToScaledVector4( + configuration, + source.Slice(0, background.Length), + sourceSpan); this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount); - PixelOperations.Instance.PackFromScaledVector4(destinationSpan, destination, destination.Length); + PixelOperations.Instance.FromScaledVector4( + configuration, + destinationSpan.Slice(0, background.Length), + destination); } } @@ -106,34 +134,49 @@ namespace SixLabors.ImageSharp.PixelFormats /// Blends 2 rows together /// /// the pixel format of the source span - /// memory manager to use internally + /// to use internally /// the destination span /// the background span /// the source span - /// - /// A value between 0 and 1 indicating the weight of the second source vector. - /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. + /// + /// A value between 0 and 1 indicating the weight of the second source vector. + /// At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// - public void Blend(MemoryAllocator memoryManager, Span destination, ReadOnlySpan background, ReadOnlySpan source, float amount) + public void Blend( + Configuration configuration, + Span destination, + ReadOnlySpan background, + ReadOnlySpan source, + float amount) where TPixelSrc : struct, IPixel { Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); - using (IMemoryOwner buffer = memoryManager.Allocate(destination.Length * 3)) + using (IMemoryOwner buffer = + configuration.MemoryAllocator.Allocate(destination.Length * 3)) { Span destinationSpan = buffer.Slice(0, destination.Length); Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - PixelOperations.Instance.ToScaledVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToScaledVector4(source, sourceSpan, destination.Length); + PixelOperations.Instance.ToScaledVector4( + configuration, + background.Slice(0, background.Length), + backgroundSpan); + PixelOperations.Instance.ToScaledVector4( + configuration, + source.Slice(0, background.Length), + sourceSpan); this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount); - PixelOperations.Instance.PackFromScaledVector4(destinationSpan, destination, destination.Length); + PixelOperations.Instance.FromScaledVector4( + configuration, + destinationSpan.Slice(0, background.Length), + destination); } } - } -} + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Alpha8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Alpha8.cs new file mode 100644 index 0000000000..75b7ede827 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Alpha8.cs @@ -0,0 +1,155 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing a single 8 bit normalized W values. + /// + /// Ranges from [0, 0, 0, 0] to [0, 0, 0, 1] in vector form. + /// + /// + public struct Alpha8 : IPixel, IPackedVector + { + /// + /// Initializes a new instance of the struct. + /// + /// The alpha component. + public Alpha8(byte alpha) => this.PackedValue = alpha; + + /// + /// Initializes a new instance of the struct. + /// + /// The alpha component. + public Alpha8(float alpha) => this.PackedValue = Pack(alpha); + + /// + public byte PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Alpha8 left, Alpha8 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Alpha8 left, Alpha8 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.PackedValue = Pack(vector.W); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() => new Vector4(0, 0, 0, this.PackedValue / 255F); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.PackedValue = source.A; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.PackedValue = byte.MaxValue; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.PackedValue = source.A; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.PackedValue = byte.MaxValue; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.PackedValue = byte.MaxValue; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.PackedValue = byte.MaxValue; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.PackedValue = source.A; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest = default; + dest.A = this.PackedValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.PackedValue = byte.MaxValue; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + /// Compares an object with the packed vector. + /// + /// The object to compare. + /// True if the object is equal to the packed vector. + public override bool Equals(object obj) => obj is Alpha8 other && this.Equals(other); + + /// + /// Compares another Alpha8 packed vector with the packed vector. + /// + /// The Alpha8 packed vector to compare. + /// True if the packed vectors are equal. + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Alpha8 other) => this.PackedValue.Equals(other.PackedValue); + + /// + /// Gets a string representation of the packed vector. + /// + /// A string representation of the packed vector. + public override string ToString() => $"Alpha8({this.PackedValue})"; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + /// Packs a into a byte. + /// + /// The float containing the value to pack. + /// The containing the packed values. + [MethodImpl(InliningOptions.ShortMethod)] + private static byte Pack(float alpha) => (byte)Math.Round(alpha.Clamp(0, 1F) * 255F); + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Argb32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs similarity index 56% rename from src/ImageSharp/PixelFormats/Argb32.cs rename to src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs index 51d3964ef8..8fc3016314 100644 --- a/src/ImageSharp/PixelFormats/Argb32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Argb32.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// as it avoids the need to create new values for modification operations. /// [StructLayout(LayoutKind.Sequential)] - public struct Argb32 : IPixel, IPackedVector + public partial struct Argb32 : IPixel, IPackedVector { /// /// Gets or sets the alpha component. @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The red component. /// The green component. /// The blue component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Argb32(byte r, byte g, byte b) { this.R = r; @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The green component. /// The blue component. /// The alpha component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Argb32(byte r, byte g, byte b, byte a) { this.R = r; @@ -89,12 +89,9 @@ namespace SixLabors.ImageSharp.PixelFormats /// The green component. /// The blue component. /// The alpha component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Argb32(float r, float g, float b, float a = 1) - : this() - { - this.Pack(r, g, b, a); - } + : this() => this.Pack(r, g, b, a); /// /// Initializes a new instance of the struct. @@ -102,12 +99,9 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// The vector containing the components for the packed vector. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Argb32(Vector3 vector) - : this() - { - this.Pack(ref vector); - } + : this() => this.Pack(ref vector); /// /// Initializes a new instance of the struct. @@ -115,12 +109,9 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// The vector containing the components for the packed vector. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Argb32(Vector4 vector) - : this() - { - this.Pack(ref vector); - } + : this() => this.Pack(ref vector); /// /// Initializes a new instance of the struct. @@ -128,22 +119,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// The packed value. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Argb32(uint packed) - : this() - { - this.Argb = packed; - } + : this() => this.Argb = packed; /// /// Gets or sets the packed representation of the Argb32 struct. /// public uint Argb { - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] get => Unsafe.As(ref this); - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] set => Unsafe.As(ref this) = value; } @@ -157,20 +145,13 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// Compares two objects for equality. /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// + /// The on the left side of the operand. + /// The on the right side of the operand. /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Argb32 left, Argb32 right) - { - return left.Argb == right.Argb; - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Argb32 left, Argb32 right) => left.Equals(right); /// /// Compares two objects for equality. @@ -180,63 +161,45 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Argb32 left, Argb32 right) - { - return left.Argb != right.Argb; - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Argb32 left, Argb32 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.Pack(ref vector); - } + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - this.PackFromVector4(vector); - } + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.Pack(ref vector); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - return this.ToVector4(); - } + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; - } + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.PackedValue = source.PackedValue; /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) { this.R = source.R; this.G = source.G; this.B = source.B; - this.A = source.A; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.PackedValue = source.PackedValue; + this.A = byte.MaxValue; } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) { this.R = source.R; this.G = source.G; @@ -244,44 +207,50 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = source.A; } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; + this.R = source.PackedValue; + this.G = source.PackedValue; + this.B = source.PackedValue; + this.A = byte.MaxValue; } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = this.A; + byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(source.PackedValue); + this.R = rgb; + this.G = rgb; + this.B = rgb; + this.A = byte.MaxValue; } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToArgb32(ref Argb32 dest) + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) { - dest = this; + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = byte.MaxValue; } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgr24(ref Bgr24 dest) + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = source.A; } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32(ref Bgra32 dest) + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) { dest.R = this.R; dest.G = this.G; @@ -289,91 +258,43 @@ namespace SixLabors.ImageSharp.PixelFormats dest.A = this.A; } - /// - /// Converts the pixel to format. - /// - /// The RGBA value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgba32 ToRgba32() => new Rgba32(this.R, this.G, this.B, this.A); - - /// - /// Converts the pixel to format. - /// - /// The RGBA value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Bgra32 ToBgra32() => new Bgra32(this.R, this.G, this.B, this.A); - - /// - /// Converts the pixel to format. - /// - /// The RGBA value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Argb32 ToArgb32() => this; - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgb48(Rgb48 source) + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) { - this.R = (byte)(((source.R * 255) + 32895) >> 16); - this.G = (byte)(((source.G * 255) + 32895) >> 16); - this.B = (byte)(((source.B * 255) + 32895) >> 16); + this.R = ImageMaths.DownScaleFrom16BitTo8Bit(source.R); + this.G = ImageMaths.DownScaleFrom16BitTo8Bit(source.G); + this.B = ImageMaths.DownScaleFrom16BitTo8Bit(source.B); this.A = byte.MaxValue; } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) { - this.R = (byte)(((source.R * 255) + 32895) >> 16); - this.G = (byte)(((source.G * 255) + 32895) >> 16); - this.B = (byte)(((source.B * 255) + 32895) >> 16); - this.A = (byte)(((source.A * 255) + 32895) >> 16); + this.R = ImageMaths.DownScaleFrom16BitTo8Bit(source.R); + this.G = ImageMaths.DownScaleFrom16BitTo8Bit(source.G); + this.B = ImageMaths.DownScaleFrom16BitTo8Bit(source.B); + this.A = ImageMaths.DownScaleFrom16BitTo8Bit(source.A); } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - public override bool Equals(object obj) - { - return obj is Argb32 argb32 && this.Equals(argb32); - } + public override bool Equals(object obj) => obj is Argb32 argb32 && this.Equals(argb32); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Argb32 other) - { - return this.Argb == other.Argb; - } + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Argb32 other) => this.Argb == other.Argb; /// /// Gets a string representation of the packed vector. /// /// A string representation of the packed vector. - public override string ToString() - { - return $"({this.R},{this.G},{this.B},{this.A})"; - } + public override string ToString() => $"Argb({this.A}, {this.R}, {this.G}, {this.B})"; /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public override int GetHashCode() => this.Argb.GetHashCode(); - /// - /// Gets the representation without normalizing to [0, 1] - /// - /// A of values in [0, 255] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Vector4 ToByteScaledVector4() - { - return new Vector4(this.R, this.G, this.B, this.A); - } - /// /// Packs the four floats into a color. /// @@ -381,7 +302,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The y-component /// The z-component /// The w-component - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private void Pack(float x, float y, float z, float w) { var value = new Vector4(x, y, z, w); @@ -392,7 +313,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// Packs a into a uint. /// /// The vector containing the values to pack. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private void Pack(ref Vector3 vector) { var value = new Vector4(vector, 1); @@ -403,7 +324,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// Packs a into a color. /// /// The vector containing the values to pack. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private void Pack(ref Vector4 vector) { vector *= MaxBytes; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs new file mode 100644 index 0000000000..9207f046c4 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr24.cs @@ -0,0 +1,198 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Pixel type containing three 8-bit unsigned normalized values ranging from 0 to 255. + /// The color components are stored in blue, green, red order (least significant to most significant byte). + /// + /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. + /// + /// + [StructLayout(LayoutKind.Explicit)] + public partial struct Bgr24 : IPixel + { + /// + /// The blue component. + /// + [FieldOffset(0)] + public byte B; + + /// + /// The green component. + /// + [FieldOffset(1)] + public byte G; + + /// + /// The red component. + /// + [FieldOffset(2)] + public byte R; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + [MethodImpl(InliningOptions.ShortMethod)] + public Bgr24(byte r, byte g, byte b) + { + this.R = r; + this.G = g; + this.B = b; + } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Bgr24 left, Bgr24 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Bgr24 left, Bgr24 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) + { + Rgba32 rgba = default; + rgba.FromVector4(vector); + this.FromRgba32(rgba); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() => new Rgba32(this.R, this.G, this.B, byte.MaxValue).ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) + { + this.R = source.R; + this.G = source.G; + this.B = source.B; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this = source; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) + { + this.R = source.R; + this.G = source.G; + this.B = source.B; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) + { + this.R = source.PackedValue; + this.G = source.PackedValue; + this.B = source.PackedValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) + { + byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(source.PackedValue); + this.R = rgb; + this.G = rgb; + this.B = rgb; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) + { + this.R = source.R; + this.G = source.G; + this.B = source.B; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this = source.Bgr; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; + dest.A = byte.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) + { + this.R = ImageMaths.DownScaleFrom16BitTo8Bit(source.R); + this.G = ImageMaths.DownScaleFrom16BitTo8Bit(source.G); + this.B = ImageMaths.DownScaleFrom16BitTo8Bit(source.B); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) + { + this.R = ImageMaths.DownScaleFrom16BitTo8Bit(source.R); + this.G = ImageMaths.DownScaleFrom16BitTo8Bit(source.G); + this.B = ImageMaths.DownScaleFrom16BitTo8Bit(source.B); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Bgr24 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); + + /// + public override bool Equals(object obj) => obj is Bgr24 other && this.Equals(other); + + /// + public override string ToString() => $"Bgra({this.B}, {this.G}, {this.R})"; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() + { + int hash = HashHelpers.Combine(this.R.GetHashCode(), this.G.GetHashCode()); + return HashHelpers.Combine(hash, this.B.GetHashCode()); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs new file mode 100644 index 0000000000..a2e4dc8802 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgr565.cs @@ -0,0 +1,171 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. + /// The x and z components use 5 bits, and the y component uses 6 bits. + /// + /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. + /// + /// + public struct Bgr565 : IPixel, IPackedVector + { + /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + /// The z-component + public Bgr565(float x, float y, float z) + : this(new Vector3(x, y, z)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed value. + /// + public Bgr565(Vector3 vector) => this.PackedValue = Pack(ref vector); + + /// + public ushort PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Bgr565 left, Bgr565 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Bgr565 left, Bgr565 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) + { + var vector3 = new Vector3(vector.X, vector.Y, vector.Z); + this.PackedValue = Pack(ref vector3); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() => new Vector4(this.ToVector3(), 1F); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromVector4(source.ToVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromVector4(source.ToVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromVector4(source.ToVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.FromScaledVector4(this.ToScaledVector4()); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + /// Expands the packed representation into a . + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Vector3 ToVector3() + { + return new Vector3( + ((this.PackedValue >> 11) & 0x1F) * (1F / 31F), + ((this.PackedValue >> 5) & 0x3F) * (1F / 63F), + (this.PackedValue & 0x1F) * (1F / 31F)); + } + + /// + public override bool Equals(object obj) => obj is Bgr565 other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Bgr565 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + var vector = this.ToVector3(); + return FormattableString.Invariant($"Bgr565({vector.Z:#0.##}, {vector.Y:#0.##}, {vector.X:#0.##})"); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(InliningOptions.ShortMethod)] + private static ushort Pack(ref Vector3 vector) + { + vector = Vector3.Clamp(vector, Vector3.Zero, Vector3.One); + + return (ushort)((((int)Math.Round(vector.X * 31F) & 0x1F) << 11) + | (((int)Math.Round(vector.Y * 63F) & 0x3F) << 5) + | ((int)Math.Round(vector.Z * 31F) & 0x1F)); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Bgra32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs similarity index 51% rename from src/ImageSharp/PixelFormats/Bgra32.cs rename to src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs index 233df2f29e..1d156222ff 100644 --- a/src/ImageSharp/PixelFormats/Bgra32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra32.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// [StructLayout(LayoutKind.Sequential)] - public struct Bgra32 : IPixel, IPackedVector + public partial struct Bgra32 : IPixel, IPackedVector { /// /// Gets or sets the blue component. @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The red component. /// The green component. /// The blue component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Bgra32(byte r, byte g, byte b) { this.R = r; @@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The green component. /// The blue component. /// The alpha component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Bgra32(byte r, byte g, byte b, byte a) { this.R = r; @@ -84,10 +84,10 @@ namespace SixLabors.ImageSharp.PixelFormats /// public uint Bgra { - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] get => Unsafe.As(ref this); - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] set => Unsafe.As(ref this) = value; } @@ -98,62 +98,95 @@ namespace SixLabors.ImageSharp.PixelFormats set => this.Bgra = value; } + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Bgra32 left, Bgra32 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Bgra32 left, Bgra32 right) => !left.Equals(right); + /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); + public PixelOperations CreatePixelOperations() => new PixelOperations(); /// - public bool Equals(Bgra32 other) - { - return this.Bgra == other.Bgra; - } + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); /// - public override bool Equals(object obj) => obj is Bgra32 other && this.Equals(other); + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); /// - public override int GetHashCode() => this.Bgra.GetHashCode(); + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.Pack(ref vector); - /// - /// Gets the representation without normalizing to [0, 1] - /// - /// A of values in [0, 255] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Vector4 ToByteScaledVector4() - { - return new Vector4(this.R, this.G, this.B, this.A); - } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) { - this.PackFromVector4(vector); + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = source.A; } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) { - return this.ToVector4(); + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = byte.MaxValue; } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this = source; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) { - this.Pack(ref vector); + this.R = source.PackedValue; + this.G = source.PackedValue; + this.B = source.PackedValue; + this.A = byte.MaxValue; } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) { - return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; + byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(source.PackedValue); + this.R = rgb; + this.G = rgb; + this.B = rgb; + this.A = byte.MaxValue; } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) { this.R = source.R; this.G = source.G; @@ -162,33 +195,17 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) { this.R = source.R; this.G = source.G; this.B = source.B; - this.A = source.A; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - this.PackedValue = source.PackedValue; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; + this.A = byte.MaxValue; } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// + [MethodImpl(InliningOptions.ShortMethod)] public void ToRgba32(ref Rgba32 dest) { dest.R = this.R; @@ -198,77 +215,42 @@ namespace SixLabors.ImageSharp.PixelFormats } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToArgb32(ref Argb32 dest) + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = this.A; + this.R = ImageMaths.DownScaleFrom16BitTo8Bit(source.R); + this.G = ImageMaths.DownScaleFrom16BitTo8Bit(source.G); + this.B = ImageMaths.DownScaleFrom16BitTo8Bit(source.B); + this.A = byte.MaxValue; } /// - [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); - - /// - /// Converts the pixel to format. - /// - /// The RGBA value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Argb32 ToArgb32() => new Argb32(this.R, this.G, this.B, this.A); - - /// - /// Converts the pixel to format. - /// - /// The RGBA value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Bgra32 ToBgra32() => this; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgb48(Rgb48 source) + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) { - this.R = (byte)(((source.R * 255) + 32895) >> 16); - this.G = (byte)(((source.G * 255) + 32895) >> 16); - this.B = (byte)(((source.B * 255) + 32895) >> 16); - this.A = byte.MaxValue; + this.R = ImageMaths.DownScaleFrom16BitTo8Bit(source.R); + this.G = ImageMaths.DownScaleFrom16BitTo8Bit(source.G); + this.B = ImageMaths.DownScaleFrom16BitTo8Bit(source.B); + this.A = ImageMaths.DownScaleFrom16BitTo8Bit(source.A); } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); + public override bool Equals(object obj) => obj is Bgra32 other && this.Equals(other); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) - { - this.R = (byte)(((source.R * 255) + 32895) >> 16); - this.G = (byte)(((source.G * 255) + 32895) >> 16); - this.B = (byte)(((source.B * 255) + 32895) >> 16); - this.A = (byte)(((source.A * 255) + 32895) >> 16); - } + public bool Equals(Bgra32 other) => this.Bgra.Equals(other.Bgra); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); + public override int GetHashCode() => this.Bgra.GetHashCode(); + + /// + public override string ToString() => $"Bgra32({this.B}, {this.G}, {this.R}, {this.A})"; /// /// Packs a into a color. /// /// The vector containing the values to pack. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private void Pack(ref Vector4 vector) { vector *= MaxBytes; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs new file mode 100644 index 0000000000..110b51822d --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra4444.cs @@ -0,0 +1,160 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing unsigned normalized values, ranging from 0 to 1, using 4 bits each for x, y, z, and w. + /// + /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. + /// + /// + public struct Bgra4444 : IPixel, IPackedVector + { + /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + /// The z-component + /// The w-component + public Bgra4444(float x, float y, float z, float w) + : this(new Vector4(x, y, z, w)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the components for the packed vector. + public Bgra4444(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + public ushort PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Bgra4444 left, Bgra4444 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Bgra4444 left, Bgra4444 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() + { + const float Max = 1 / 15F; + + return new Vector4( + (this.PackedValue >> 8) & 0x0F, + (this.PackedValue >> 4) & 0x0F, + this.PackedValue & 0x0F, + (this.PackedValue >> 12) & 0x0F) * Max; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.FromScaledVector4(this.ToScaledVector4()); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object obj) => obj is Bgra4444 other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Bgra4444 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + var vector = this.ToVector4(); + return FormattableString.Invariant($"Bgra4444({vector.Z:#0.##}, {vector.Y:#0.##}, {vector.X:#0.##}, {vector.W:#0.##})"); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(InliningOptions.ShortMethod)] + private static ushort Pack(ref Vector4 vector) + { + vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); + return (ushort)((((int)Math.Round(vector.W * 15F) & 0x0F) << 12) + | (((int)Math.Round(vector.X * 15F) & 0x0F) << 8) + | (((int)Math.Round(vector.Y * 15F) & 0x0F) << 4) + | ((int)Math.Round(vector.Z * 15F) & 0x0F)); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs new file mode 100644 index 0000000000..dcfb25a64b --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Bgra5551.cs @@ -0,0 +1,162 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing unsigned normalized values ranging from 0 to 1. + /// The x , y and z components use 5 bits, and the w component uses 1 bit. + /// + /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. + /// + /// + public struct Bgra5551 : IPixel, IPackedVector + { + /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + /// The z-component + /// The w-component + public Bgra5551(float x, float y, float z, float w) + : this(new Vector4(x, y, z, w)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// + /// The vector containing the components for the packed vector. + /// + public Bgra5551(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + public ushort PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Bgra5551 left, Bgra5551 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Bgra5551 left, Bgra5551 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() + { + return new Vector4( + ((this.PackedValue >> 10) & 0x1F) / 31F, + ((this.PackedValue >> 5) & 0x1F) / 31F, + ((this.PackedValue >> 0) & 0x1F) / 31F, + (this.PackedValue >> 15) & 0x01); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.FromScaledVector4(this.ToScaledVector4()); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object obj) => obj is Bgra5551 other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Bgra5551 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + var vector = this.ToVector4(); + return FormattableString.Invariant($"Bgra5551({vector.Z:#0.##}, {vector.Y:#0.##}, {vector.X:#0.##}, {vector.W:#0.##})"); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(InliningOptions.ShortMethod)] + private static ushort Pack(ref Vector4 vector) + { + vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); + return (ushort)( + (((int)Math.Round(vector.X * 31F) & 0x1F) << 10) + | (((int)Math.Round(vector.Y * 31F) & 0x1F) << 5) + | (((int)Math.Round(vector.Z * 31F) & 0x1F) << 0) + | (((int)Math.Round(vector.W) & 0x1) << 15)); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs new file mode 100644 index 0000000000..43a03dc5d1 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Byte4.cs @@ -0,0 +1,172 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing four 8-bit unsigned integer values, ranging from 0 to 255. + /// + /// Ranges from [0, 0, 0, 0] to [255, 255, 255, 255] in vector form. + /// + /// + public struct Byte4 : IPixel, IPackedVector + { + /// + /// Initializes a new instance of the struct. + /// + /// + /// A vector containing the initial values for the components of the Byte4 structure. + /// + public Byte4(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + /// The z-component + /// The w-component + public Byte4(float x, float y, float z, float w) + { + var vector = new Vector4(x, y, z, w); + this.PackedValue = Pack(ref vector); + } + + /// + public uint PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Byte4 left, Byte4 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Byte4 left, Byte4 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector * 255F); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4() / 255F; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() + { + return new Vector4( + this.PackedValue & 0xFF, + (this.PackedValue >> 0x8) & 0xFF, + (this.PackedValue >> 0x10) & 0xFF, + (this.PackedValue >> 0x18) & 0xFF); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.FromScaledVector4(this.ToScaledVector4()); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object obj) => obj is Byte4 byte4 && this.Equals(byte4); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Byte4 other) => this.PackedValue.Equals(other.PackedValue); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override string ToString() + { + var vector = this.ToVector4(); + return FormattableString.Invariant($"Bgra5551({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + } + + /// + /// Packs a vector into a uint. + /// + /// The vector containing the values to pack. + /// The containing the packed values. + [MethodImpl(InliningOptions.ShortMethod)] + private static uint Pack(ref Vector4 vector) + { + const float Max = 255F; + + // Clamp the value between min and max values + vector = Vector4.Clamp(vector, Vector4.Zero, new Vector4(Max)); + + uint byte4 = (uint)Math.Round(vector.X) & 0xFF; + uint byte3 = ((uint)Math.Round(vector.Y) & 0xFF) << 0x8; + uint byte2 = ((uint)Math.Round(vector.Z) & 0xFF) << 0x10; + uint byte1 = ((uint)Math.Round(vector.W) & 0xFF) << 0x18; + + return byte4 | byte3 | byte2 | byte1; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs new file mode 100644 index 0000000000..2ec965dfb0 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs @@ -0,0 +1,240 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// + +using SixLabors.ImageSharp.PixelFormats.Utils; +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Argb32 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + /// + internal override void FromArgb32(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + source.CopyTo(destPixels); + } + + /// + internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + sourcePixels.CopyTo(destPixels); + } + + /// + internal override void FromVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels) + { + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); + } + + /// + internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) + { + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false); + } + + /// + internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels) + { + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true); + } + + /// + internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) + { + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true); + } + + /// + internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref uint sourceRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); + ref uint destRef = ref Unsafe.As(ref MemoryMarshal.GetReference(destPixels)); + + for (int i = 0; i < sourcePixels.Length; i++) + { + uint sp = Unsafe.Add(ref sourceRef, i); + Unsafe.Add(ref destRef, i) = PixelConverter.FromArgb32.ToRgba32(sp); + } + } + + /// + internal override void FromRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref uint sourceRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); + ref uint destRef = ref Unsafe.As(ref MemoryMarshal.GetReference(destPixels)); + + for (int i = 0; i < sourcePixels.Length; i++) + { + uint sp = Unsafe.Add(ref sourceRef, i); + Unsafe.Add(ref destRef, i) = PixelConverter.FromRgba32.ToArgb32(sp); + } + } + /// + internal override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref uint sourceRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); + ref uint destRef = ref Unsafe.As(ref MemoryMarshal.GetReference(destPixels)); + + for (int i = 0; i < sourcePixels.Length; i++) + { + uint sp = Unsafe.Add(ref sourceRef, i); + Unsafe.Add(ref destRef, i) = PixelConverter.FromArgb32.ToBgra32(sp); + } + } + + /// + internal override void FromBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref uint sourceRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); + ref uint destRef = ref Unsafe.As(ref MemoryMarshal.GetReference(destPixels)); + + for (int i = 0; i < sourcePixels.Length; i++) + { + uint sp = Unsafe.Add(ref sourceRef, i); + Unsafe.Add(ref destRef, i) = PixelConverter.FromBgra32.ToArgb32(sp); + } + } + + /// + internal override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgr24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromArgb32(sp); + } + } + + /// + internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromArgb32(sp); + } + } + + /// + internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray16 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromArgb32(sp); + } + } + + /// + internal override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromArgb32(sp); + } + } + + /// + internal override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb48 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromArgb32(sp); + } + } + + /// + internal override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Argb32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba64 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Argb32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromArgb32(sp); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.tt new file mode 100644 index 0000000000..0a58504e15 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.tt @@ -0,0 +1,19 @@ +<#@include file="_Common.ttinclude" #> +<#@ output extension=".cs" #> + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Argb32 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + <# GenerateAllDefaultConversionMethods("Argb32"); #> + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs new file mode 100644 index 0000000000..711a9d1c16 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.cs @@ -0,0 +1,214 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// + +using SixLabors.ImageSharp.PixelFormats.Utils; +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Bgr24 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + /// + internal override void FromBgr24(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + source.CopyTo(destPixels); + } + + /// + internal override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + sourcePixels.CopyTo(destPixels); + } + + /// + internal override void FromVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels) + { + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); + } + + /// + internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) + { + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false); + } + + /// + internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels) + { + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true); + } + + /// + internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) + { + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true); + } + + + /// + internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Argb32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Argb32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgr24(sp); + } + } + + /// + internal override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgr24(sp); + } + } + + /// + internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgr24(sp); + } + } + + /// + internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray16 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgr24(sp); + } + } + + /// + internal override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgr24(sp); + } + } + + /// + internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgr24(sp); + } + } + + /// + internal override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb48 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgr24(sp); + } + } + + /// + internal override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba64 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgr24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgr24(sp); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.tt new file mode 100644 index 0000000000..84b89aa32c --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgr24.PixelOperations.Generated.tt @@ -0,0 +1,19 @@ +<#@include file="_Common.ttinclude" #> +<#@ output extension=".cs" #> + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Bgr24 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + <# GenerateAllDefaultConversionMethods("Bgr24"); #> + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs new file mode 100644 index 0000000000..b669dd5348 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs @@ -0,0 +1,240 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// + +using SixLabors.ImageSharp.PixelFormats.Utils; +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Bgra32 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + /// + internal override void FromBgra32(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + source.CopyTo(destPixels); + } + + /// + internal override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + sourcePixels.CopyTo(destPixels); + } + + /// + internal override void FromVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels) + { + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); + } + + /// + internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) + { + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false); + } + + /// + internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels) + { + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true); + } + + /// + internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) + { + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true); + } + + /// + internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref uint sourceRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); + ref uint destRef = ref Unsafe.As(ref MemoryMarshal.GetReference(destPixels)); + + for (int i = 0; i < sourcePixels.Length; i++) + { + uint sp = Unsafe.Add(ref sourceRef, i); + Unsafe.Add(ref destRef, i) = PixelConverter.FromBgra32.ToRgba32(sp); + } + } + + /// + internal override void FromRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref uint sourceRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); + ref uint destRef = ref Unsafe.As(ref MemoryMarshal.GetReference(destPixels)); + + for (int i = 0; i < sourcePixels.Length; i++) + { + uint sp = Unsafe.Add(ref sourceRef, i); + Unsafe.Add(ref destRef, i) = PixelConverter.FromRgba32.ToBgra32(sp); + } + } + /// + internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref uint sourceRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); + ref uint destRef = ref Unsafe.As(ref MemoryMarshal.GetReference(destPixels)); + + for (int i = 0; i < sourcePixels.Length; i++) + { + uint sp = Unsafe.Add(ref sourceRef, i); + Unsafe.Add(ref destRef, i) = PixelConverter.FromBgra32.ToArgb32(sp); + } + } + + /// + internal override void FromArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref uint sourceRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); + ref uint destRef = ref Unsafe.As(ref MemoryMarshal.GetReference(destPixels)); + + for (int i = 0; i < sourcePixels.Length; i++) + { + uint sp = Unsafe.Add(ref sourceRef, i); + Unsafe.Add(ref destRef, i) = PixelConverter.FromArgb32.ToBgra32(sp); + } + } + + /// + internal override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgr24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra32(sp); + } + } + + /// + internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra32(sp); + } + } + + /// + internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray16 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra32(sp); + } + } + + /// + internal override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra32(sp); + } + } + + /// + internal override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb48 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra32(sp); + } + } + + /// + internal override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba64 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Bgra32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromBgra32(sp); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.tt new file mode 100644 index 0000000000..004ceff51e --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.tt @@ -0,0 +1,19 @@ +<#@include file="_Common.ttinclude" #> +<#@ output extension=".cs" #> + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Bgra32 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + <# GenerateAllDefaultConversionMethods("Bgra32"); #> + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs new file mode 100644 index 0000000000..288c5d92e4 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs @@ -0,0 +1,190 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// + +using SixLabors.ImageSharp.PixelFormats.Utils; +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Gray16 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + /// + internal override void FromGray16(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + source.CopyTo(destPixels); + } + + /// + internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + sourcePixels.CopyTo(destPixels); + } + + + /// + internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Gray16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Argb32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Gray16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Argb32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromGray16(sp); + } + } + + /// + internal override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Gray16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgr24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Gray16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromGray16(sp); + } + } + + /// + internal override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Gray16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Gray16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromGray16(sp); + } + } + + /// + internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Gray16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Gray16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromGray16(sp); + } + } + + /// + internal override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Gray16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Gray16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromGray16(sp); + } + } + + /// + internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Gray16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Gray16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromGray16(sp); + } + } + + /// + internal override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Gray16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb48 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Gray16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromGray16(sp); + } + } + + /// + internal override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Gray16 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba64 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Gray16 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromGray16(sp); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.tt new file mode 100644 index 0000000000..3cbc01e88c --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.tt @@ -0,0 +1,19 @@ +<#@include file="_Common.ttinclude" #> +<#@ output extension=".cs" #> + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Gray16 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + <# GenerateAllDefaultConversionMethods("Gray16"); #> + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs new file mode 100644 index 0000000000..f7e94788e3 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs @@ -0,0 +1,190 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// + +using SixLabors.ImageSharp.PixelFormats.Utils; +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Gray8 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + /// + internal override void FromGray8(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + source.CopyTo(destPixels); + } + + /// + internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + sourcePixels.CopyTo(destPixels); + } + + + /// + internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Gray8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Argb32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Gray8 sp = ref Unsafe.Add(ref sourceRef, i); + ref Argb32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromGray8(sp); + } + } + + /// + internal override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Gray8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgr24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Gray8 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromGray8(sp); + } + } + + /// + internal override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Gray8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Gray8 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromGray8(sp); + } + } + + /// + internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Gray8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray16 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Gray8 sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromGray8(sp); + } + } + + /// + internal override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Gray8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Gray8 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromGray8(sp); + } + } + + /// + internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Gray8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Gray8 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromGray8(sp); + } + } + + /// + internal override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Gray8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb48 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Gray8 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromGray8(sp); + } + } + + /// + internal override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Gray8 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba64 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Gray8 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromGray8(sp); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.tt new file mode 100644 index 0000000000..d35843ccda --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.tt @@ -0,0 +1,19 @@ +<#@include file="_Common.ttinclude" #> +<#@ output extension=".cs" #> + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Gray8 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + <# GenerateAllDefaultConversionMethods("Gray8"); #> + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs new file mode 100644 index 0000000000..dbf3102c4a --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs @@ -0,0 +1,214 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// + +using SixLabors.ImageSharp.PixelFormats.Utils; +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Rgb24 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + /// + internal override void FromRgb24(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + source.CopyTo(destPixels); + } + + /// + internal override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + sourcePixels.CopyTo(destPixels); + } + + /// + internal override void FromVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels) + { + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); + } + + /// + internal override void ToVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) + { + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false); + } + + /// + internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span destPixels) + { + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true); + } + + /// + internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan sourcePixels, Span destVectors) + { + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true); + } + + + /// + internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Argb32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Argb32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb24(sp); + } + } + + /// + internal override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgr24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb24(sp); + } + } + + /// + internal override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb24(sp); + } + } + + /// + internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb24(sp); + } + } + + /// + internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray16 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb24(sp); + } + } + + /// + internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb24(sp); + } + } + + /// + internal override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb48 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb24(sp); + } + } + + /// + internal override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba64 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb24 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb24(sp); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.tt new file mode 100644 index 0000000000..d96c3684b5 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.tt @@ -0,0 +1,19 @@ +<#@include file="_Common.ttinclude" #> +<#@ output extension=".cs" #> + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Rgb24 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + <# GenerateAllDefaultConversionMethods("Rgb24"); #> + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs new file mode 100644 index 0000000000..30c9972bbf --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs @@ -0,0 +1,190 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// + +using SixLabors.ImageSharp.PixelFormats.Utils; +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Rgb48 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + /// + internal override void FromRgb48(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + source.CopyTo(destPixels); + } + + /// + internal override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + sourcePixels.CopyTo(destPixels); + } + + + /// + internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Argb32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); + ref Argb32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb48(sp); + } + } + + /// + internal override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgr24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb48(sp); + } + } + + /// + internal override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb48(sp); + } + } + + /// + internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb48(sp); + } + } + + /// + internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray16 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb48(sp); + } + } + + /// + internal override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb48(sp); + } + } + + /// + internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb48(sp); + } + } + + /// + internal override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgb48 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba64 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgb48 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgb48(sp); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.tt new file mode 100644 index 0000000000..7bff336386 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.tt @@ -0,0 +1,19 @@ +<#@include file="_Common.ttinclude" #> +<#@ output extension=".cs" #> + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Rgb48 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + <# GenerateAllDefaultConversionMethods("Rgb48"); #> + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs new file mode 100644 index 0000000000..da2ce3770b --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs @@ -0,0 +1,216 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// + +using SixLabors.ImageSharp.PixelFormats.Utils; +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Rgba32 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal partial class PixelOperations : PixelOperations + { + /// + internal override void FromRgba32(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + source.CopyTo(destPixels); + } + + /// + internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + sourcePixels.CopyTo(destPixels); + } + + /// + internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref uint sourceRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); + ref uint destRef = ref Unsafe.As(ref MemoryMarshal.GetReference(destPixels)); + + for (int i = 0; i < sourcePixels.Length; i++) + { + uint sp = Unsafe.Add(ref sourceRef, i); + Unsafe.Add(ref destRef, i) = PixelConverter.FromRgba32.ToArgb32(sp); + } + } + + /// + internal override void FromArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref uint sourceRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); + ref uint destRef = ref Unsafe.As(ref MemoryMarshal.GetReference(destPixels)); + + for (int i = 0; i < sourcePixels.Length; i++) + { + uint sp = Unsafe.Add(ref sourceRef, i); + Unsafe.Add(ref destRef, i) = PixelConverter.FromArgb32.ToRgba32(sp); + } + } + /// + internal override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref uint sourceRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); + ref uint destRef = ref Unsafe.As(ref MemoryMarshal.GetReference(destPixels)); + + for (int i = 0; i < sourcePixels.Length; i++) + { + uint sp = Unsafe.Add(ref sourceRef, i); + Unsafe.Add(ref destRef, i) = PixelConverter.FromRgba32.ToBgra32(sp); + } + } + + /// + internal override void FromBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref uint sourceRef = ref Unsafe.As(ref MemoryMarshal.GetReference(sourcePixels)); + ref uint destRef = ref Unsafe.As(ref MemoryMarshal.GetReference(destPixels)); + + for (int i = 0; i < sourcePixels.Length; i++) + { + uint sp = Unsafe.Add(ref sourceRef, i); + Unsafe.Add(ref destRef, i) = PixelConverter.FromBgra32.ToRgba32(sp); + } + } + + /// + internal override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgr24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba32(sp); + } + } + + /// + internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba32(sp); + } + } + + /// + internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray16 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba32(sp); + } + } + + /// + internal override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba32(sp); + } + } + + /// + internal override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb48 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba32(sp); + } + } + + /// + internal override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba64 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba32 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba64 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba32(sp); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.tt new file mode 100644 index 0000000000..6b9e2d1248 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.tt @@ -0,0 +1,19 @@ +<#@include file="_Common.ttinclude" #> +<#@ output extension=".cs" #> + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Rgba32 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal partial class PixelOperations : PixelOperations + { + <# GenerateAllDefaultConversionMethods("Rgba32"); #> + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs new file mode 100644 index 0000000000..42c40ad5d7 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs @@ -0,0 +1,190 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// + +using SixLabors.ImageSharp.PixelFormats.Utils; +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Rgba64 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + /// + internal override void FromRgba64(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + source.CopyTo(destPixels); + } + + /// + internal override void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + sourcePixels.CopyTo(destPixels); + } + + + /// + internal override void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Argb32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); + ref Argb32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba64(sp); + } + } + + /// + internal override void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgr24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgr24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba64(sp); + } + } + + /// + internal override void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); + ref Bgra32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba64(sp); + } + } + + /// + internal override void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray8 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray8 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba64(sp); + } + } + + /// + internal override void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray16 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray16 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba64(sp); + } + } + + /// + internal override void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb24 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb24 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba64(sp); + } + } + + /// + internal override void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba32 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba64(sp); + } + } + + /// + internal override void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref Rgba64 sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb48 destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref Rgba64 sp = ref Unsafe.Add(ref sourceRef, i); + ref Rgb48 dp = ref Unsafe.Add(ref destRef, i); + + dp.FromRgba64(sp); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.tt b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.tt new file mode 100644 index 0000000000..d15945f947 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.tt @@ -0,0 +1,19 @@ +<#@include file="_Common.ttinclude" #> +<#@ output extension=".cs" #> + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Rgba64 + { + /// + /// Provides optimized overrides for bulk operations. + /// + internal class PixelOperations : PixelOperations + { + <# GenerateAllDefaultConversionMethods("Rgba64"); #> + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude new file mode 100644 index 0000000000..f0675cb5b3 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Generated/_Common.ttinclude @@ -0,0 +1,167 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// + +using SixLabors.ImageSharp.PixelFormats.Utils; +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +<#+ + static readonly string[] CommonPixelTypes = { "Argb32", "Bgr24", "Bgra32", "Gray8", "Gray16", "Rgb24", "Rgba32", "Rgb48", "Rgba64" }; + + static readonly string[] Optimized32BitTypes = { "Rgba32", "Argb32", "Bgra32" }; + + // Types with Rgba32-combatible to/from Vector4 conversion + static readonly string[] Rgba32CompatibleTypes = { "Argb32", "Bgra32", "Rgb24", "Bgr24" }; + + void GenerateDefaultSelfConversionMethods(string pixelType) + { +#> +/// + internal override void From<#=pixelType#>(Configuration configuration, ReadOnlySpan<<#=pixelType#>> source, Span<<#=pixelType#>> destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + source.CopyTo(destPixels); + } + + /// + internal override void To<#=pixelType#>(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span<<#=pixelType#>> destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + sourcePixels.CopyTo(destPixels); + } + +<#+ + } + + void GenerateDefaultConvertToMethod(string fromPixelType, string toPixelType) + { +#> + + /// + internal override void To<#=toPixelType#>(Configuration configuration, ReadOnlySpan<<#=fromPixelType#>> sourcePixels, Span<<#=toPixelType#>> destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref <#=fromPixelType#> sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref <#=toPixelType#> destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref <#=fromPixelType#> sp = ref Unsafe.Add(ref sourceRef, i); + ref <#=toPixelType#> dp = ref Unsafe.Add(ref destRef, i); + + dp.From<#=fromPixelType#>(sp); + } + } +<#+ + } + + void GenerateOptimized32BitConversionMethods(string thisPixelType, string otherPixelType) + { +#> + /// + internal override void To<#=otherPixelType#>(Configuration configuration, ReadOnlySpan<<#=thisPixelType#>> sourcePixels, Span<<#=otherPixelType#>> destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref uint sourceRef = ref Unsafe.As<<#=thisPixelType#>,uint>(ref MemoryMarshal.GetReference(sourcePixels)); + ref uint destRef = ref Unsafe.As<<#=otherPixelType#>, uint>(ref MemoryMarshal.GetReference(destPixels)); + + for (int i = 0; i < sourcePixels.Length; i++) + { + uint sp = Unsafe.Add(ref sourceRef, i); + Unsafe.Add(ref destRef, i) = PixelConverter.From<#=thisPixelType#>.To<#=otherPixelType#>(sp); + } + } + + /// + internal override void From<#=otherPixelType#>(Configuration configuration, ReadOnlySpan<<#=otherPixelType#>> sourcePixels, Span<<#=thisPixelType#>> destPixels) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref uint sourceRef = ref Unsafe.As<<#=otherPixelType#>,uint>(ref MemoryMarshal.GetReference(sourcePixels)); + ref uint destRef = ref Unsafe.As<<#=thisPixelType#>, uint>(ref MemoryMarshal.GetReference(destPixels)); + + for (int i = 0; i < sourcePixels.Length; i++) + { + uint sp = Unsafe.Add(ref sourceRef, i); + Unsafe.Add(ref destRef, i) = PixelConverter.From<#=otherPixelType#>.To<#=thisPixelType#>(sp); + } + } +<#+ + } + + void GenerateRgba32CompatibleVector4ConversionMethods(string pixelType) + { +#> + /// + internal override void FromVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span<<#=pixelType#>> destPixels) + { + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, false); + } + + /// + internal override void ToVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span destVectors) + { + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, false); + } + + /// + internal override void FromScaledVector4(Configuration configuration, ReadOnlySpan sourceVectors, Span<<#=pixelType#>> destPixels) + { + Vector4Converters.RgbaCompatible.FromVector4(configuration, this, sourceVectors, destPixels, true); + } + + /// + internal override void ToScaledVector4(Configuration configuration, ReadOnlySpan<<#=pixelType#>> sourcePixels, Span destVectors) + { + Vector4Converters.RgbaCompatible.ToVector4(configuration, this, sourcePixels, destVectors, true); + } + +<#+ + } + + void GenerateAllDefaultConversionMethods(string pixelType) + { + GenerateDefaultSelfConversionMethods(pixelType); + + if (Rgba32CompatibleTypes.Contains(pixelType)) + { + GenerateRgba32CompatibleVector4ConversionMethods(pixelType); + } + + var matching32BitTypes = Optimized32BitTypes.Contains(pixelType) ? + Optimized32BitTypes.Where(p => p != pixelType) : + Enumerable.Empty(); + + foreach (string destPixelType in matching32BitTypes) + { + GenerateOptimized32BitConversionMethods(pixelType, destPixelType); + } + + var otherCommonNon32Types = CommonPixelTypes + .Where(p => p != pixelType) + .Except(matching32BitTypes); + + foreach (string destPixelType in otherCommonNon32Types) + { + GenerateDefaultConvertToMethod(pixelType, destPixelType); + } + } +#> \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs new file mode 100644 index 0000000000..2e98a28ada --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Gray16.cs @@ -0,0 +1,180 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing a single 16 bit normalized gray values. + /// + /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. + /// + /// + public partial struct Gray16 : IPixel, IPackedVector + { + private const float Max = ushort.MaxValue; + private const float Average = 1 / 3F; + + /// + /// Initializes a new instance of the struct. + /// + /// The luminance component + public Gray16(ushort luminance) => this.PackedValue = luminance; + + /// + public ushort PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Gray16 left, Gray16 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Gray16 left, Gray16 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) + { + vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max * Average; + this.PackedValue = (ushort)MathF.Round(vector.X + vector.Y + vector.Z); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() + { + float scaled = this.PackedValue / Max; + return new Vector4(scaled, scaled, scaled, 1F); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) + { + this.PackedValue = ImageMaths.Get16BitBT709Luminance( + ImageMaths.UpscaleFrom8BitTo16Bit(source.R), + ImageMaths.UpscaleFrom8BitTo16Bit(source.G), + ImageMaths.UpscaleFrom8BitTo16Bit(source.B)); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) + { + this.PackedValue = ImageMaths.Get16BitBT709Luminance( + ImageMaths.UpscaleFrom8BitTo16Bit(source.R), + ImageMaths.UpscaleFrom8BitTo16Bit(source.G), + ImageMaths.UpscaleFrom8BitTo16Bit(source.B)); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) + { + this.PackedValue = ImageMaths.Get16BitBT709Luminance( + ImageMaths.UpscaleFrom8BitTo16Bit(source.R), + ImageMaths.UpscaleFrom8BitTo16Bit(source.G), + ImageMaths.UpscaleFrom8BitTo16Bit(source.B)); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.PackedValue = ImageMaths.UpscaleFrom8BitTo16Bit(source.PackedValue); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.PackedValue = source.PackedValue; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) + { + this.PackedValue = ImageMaths.Get16BitBT709Luminance( + ImageMaths.UpscaleFrom8BitTo16Bit(source.R), + ImageMaths.UpscaleFrom8BitTo16Bit(source.G), + ImageMaths.UpscaleFrom8BitTo16Bit(source.B)); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) + { + this.PackedValue = ImageMaths.Get16BitBT709Luminance( + ImageMaths.UpscaleFrom8BitTo16Bit(source.R), + ImageMaths.UpscaleFrom8BitTo16Bit(source.G), + ImageMaths.UpscaleFrom8BitTo16Bit(source.B)); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(this.PackedValue); + dest.R = rgb; + dest.G = rgb; + dest.B = rgb; + dest.A = byte.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.PackedValue = ImageMaths.Get16BitBT709Luminance(source.R, source.G, source.B); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.PackedValue = ImageMaths.Get16BitBT709Luminance(source.R, source.G, source.B); + + /// + public override bool Equals(object obj) => obj is Gray16 other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Gray16 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() => $"Gray16({this.PackedValue})"; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(InliningOptions.ShortMethod)] + internal void ConvertFromRgbaScaledVector4(Vector4 vector) + { + vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max; + this.PackedValue = ImageMaths.Get16BitBT709Luminance( + (ushort)MathF.Round(vector.X), + (ushort)MathF.Round(vector.Y), + (ushort)MathF.Round(vector.Z)); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs new file mode 100644 index 0000000000..512bee39de --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Gray8.cs @@ -0,0 +1,165 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing a single 8 bit normalized gray values. + /// + /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. + /// + /// + public partial struct Gray8 : IPixel, IPackedVector + { + private static readonly Vector4 MaxBytes = new Vector4(255F); + private static readonly Vector4 Half = new Vector4(0.5F); + private const float Average = 1 / 3F; + + private static readonly Vector4 Min = new Vector4(0, 0, 0, 1f); + private static readonly Vector4 Max = Vector4.One; + + private static readonly Vector4 Accumulator = new Vector4(255f * Average, 255f * Average, 255f * Average, 0.5f); + + /// + /// Initializes a new instance of the struct. + /// + /// The luminance component. + public Gray8(byte luminance) => this.PackedValue = luminance; + + /// + public byte PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Gray8 left, Gray8 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Gray8 left, Gray8 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) + { + vector = Vector4.Max(Min, vector); + vector = Vector4.Min(Max, vector); + + float roundedSum = Vector4.Dot(vector, Accumulator); + + this.PackedValue = (byte)roundedSum; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() + { + float rgb = this.PackedValue / 255F; + return new Vector4(rgb, rgb, rgb, 1F); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.PackedValue = ImageMaths.Get8BitBT709Luminance(source.R, source.G, source.B); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.PackedValue = ImageMaths.Get8BitBT709Luminance(source.R, source.G, source.B); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.PackedValue = ImageMaths.Get8BitBT709Luminance(source.R, source.G, source.B); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.PackedValue = source.PackedValue; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.PackedValue = ImageMaths.DownScaleFrom16BitTo8Bit(source.PackedValue); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.PackedValue = ImageMaths.Get8BitBT709Luminance(source.R, source.G, source.B); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.PackedValue = ImageMaths.Get8BitBT709Luminance(source.R, source.G, source.B); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.R = this.PackedValue; + dest.G = this.PackedValue; + dest.B = this.PackedValue; + dest.A = byte.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) + => this.PackedValue = ImageMaths.Get8BitBT709Luminance( + ImageMaths.DownScaleFrom16BitTo8Bit(source.R), + ImageMaths.DownScaleFrom16BitTo8Bit(source.G), + ImageMaths.DownScaleFrom16BitTo8Bit(source.B)); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) + => this.PackedValue = ImageMaths.Get8BitBT709Luminance( + ImageMaths.DownScaleFrom16BitTo8Bit(source.R), + ImageMaths.DownScaleFrom16BitTo8Bit(source.G), + ImageMaths.DownScaleFrom16BitTo8Bit(source.B)); + + /// + public override bool Equals(object obj) => obj is Gray8 other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Gray8 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() => $"Gray8({this.PackedValue})"; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(InliningOptions.ShortMethod)] + internal void ConvertFromRgbaScaledVector4(Vector4 vector) + { + vector *= MaxBytes; + vector += Half; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + this.PackedValue = ImageMaths.Get8BitBT709Luminance((byte)vector.X, (byte)vector.Y, (byte)vector.Z); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs new file mode 100644 index 0000000000..8323cf3e8a --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfSingle.cs @@ -0,0 +1,143 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing a single 16 bit floating point value. + /// + /// Ranges from [-1, 0, 0, 1] to [1, 0, 0, 1] in vector form. + /// + /// + public struct HalfSingle : IPixel, IPackedVector + { + /// + /// Initializes a new instance of the struct. + /// + /// The single component. + public HalfSingle(float single) => this.PackedValue = HalfTypeHelper.Pack(single); + + /// + public ushort PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(HalfSingle left, HalfSingle right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(HalfSingle left, HalfSingle right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) + { + float scaled = vector.X; + scaled *= 2F; + scaled--; + this.PackedValue = HalfTypeHelper.Pack(scaled); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() + { + float single = this.ToSingle() + 1F; + single /= 2F; + return new Vector4(single, 0, 0, 1F); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.PackedValue = HalfTypeHelper.Pack(vector.X); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() => new Vector4(this.ToSingle(), 0, 0, 1F); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.FromScaledVector4(this.ToScaledVector4()); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + /// Expands the packed representation into a . + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public float ToSingle() => HalfTypeHelper.Unpack(this.PackedValue); + + /// + public override bool Equals(object obj) => obj is HalfSingle other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(HalfSingle other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() => FormattableString.Invariant($"HalfSingle({this.ToSingle():#0.##})"); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs new file mode 100644 index 0000000000..cb915459bc --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector2.cs @@ -0,0 +1,172 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing two 16-bit floating-point values. + /// + /// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form. + /// + /// + public struct HalfVector2 : IPixel, IPackedVector + { + /// + /// Initializes a new instance of the struct. + /// + /// The x-component. + /// The y-component. + public HalfVector2(float x, float y) => this.PackedValue = Pack(x, y); + + /// + /// Initializes a new instance of the struct. + /// + /// A vector containing the initial values for the components. + public HalfVector2(Vector2 vector) => this.PackedValue = Pack(vector.X, vector.Y); + + /// + public uint PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(HalfVector2 left, HalfVector2 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(HalfVector2 left, HalfVector2 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) + { + Vector2 scaled = new Vector2(vector.X, vector.Y) * 2F; + scaled -= Vector2.One; + this.PackedValue = Pack(scaled.X, scaled.Y); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() + { + var scaled = this.ToVector2(); + scaled += Vector2.One; + scaled /= 2F; + return new Vector4(scaled, 0F, 1F); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.PackedValue = Pack(vector.X, vector.Y); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() + { + var vector = this.ToVector2(); + return new Vector4(vector.X, vector.Y, 0F, 1F); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.FromScaledVector4(this.ToScaledVector4()); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + /// Expands the packed representation into a . + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Vector2 ToVector2() + { + Vector2 vector; + vector.X = HalfTypeHelper.Unpack((ushort)this.PackedValue); + vector.Y = HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x10)); + return vector; + } + + /// + public override bool Equals(object obj) => obj is HalfVector2 other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(HalfVector2 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + var vector = this.ToVector2(); + return FormattableString.Invariant($"HalfVector2({vector.X:#0.##}, {vector.Y:#0.##})"); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(InliningOptions.ShortMethod)] + private static uint Pack(float x, float y) + { + uint num2 = HalfTypeHelper.Pack(x); + uint num = (uint)(HalfTypeHelper.Pack(y) << 0x10); + return num2 | num; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs new file mode 100644 index 0000000000..9f60ca8c77 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/HalfVector4.cs @@ -0,0 +1,174 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing four 16-bit floating-point values. + /// + /// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form. + /// + /// + public struct HalfVector4 : IPixel, IPackedVector + { + /// + /// Initializes a new instance of the struct. + /// + /// The x-component. + /// The y-component. + /// The z-component. + /// The w-component. + public HalfVector4(float x, float y, float z, float w) + : this(new Vector4(x, y, z, w)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// A vector containing the initial values for the components + public HalfVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + public ulong PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(HalfVector4 left, HalfVector4 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(HalfVector4 left, HalfVector4 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) + { + vector *= 2F; + vector -= Vector4.One; + this.FromVector4(vector); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() + { + var scaled = this.ToVector4(); + scaled += Vector4.One; + scaled /= 2F; + return scaled; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() + { + return new Vector4( + HalfTypeHelper.Unpack((ushort)this.PackedValue), + HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x10)), + HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x20)), + HalfTypeHelper.Unpack((ushort)(this.PackedValue >> 0x30))); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.FromScaledVector4(this.ToScaledVector4()); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object obj) => obj is HalfVector4 other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(HalfVector4 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + var vector = this.ToVector4(); + return FormattableString.Invariant($"HalfVector4({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + /// Packs a into a . + /// + /// The vector containing the values to pack. + /// The containing the packed values. + [MethodImpl(InliningOptions.ShortMethod)] + private static ulong Pack(ref Vector4 vector) + { + ulong num4 = HalfTypeHelper.Pack(vector.X); + ulong num3 = (ulong)HalfTypeHelper.Pack(vector.Y) << 0x10; + ulong num2 = (ulong)HalfTypeHelper.Pack(vector.Z) << 0x20; + ulong num1 = (ulong)HalfTypeHelper.Pack(vector.W) << 0x30; + return num4 | num3 | num2 | num1; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs new file mode 100644 index 0000000000..d39cfd402e --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte2.cs @@ -0,0 +1,181 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed packed pixel type containing two 8-bit signed normalized values, ranging from −1 to 1. + /// + /// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form. + /// + /// + public struct NormalizedByte2 : IPixel, IPackedVector + { + private static readonly Vector2 Half = new Vector2(127); + private static readonly Vector2 MinusOne = new Vector2(-1F); + + /// + /// Initializes a new instance of the struct. + /// + /// The x-component. + /// The y-component. + public NormalizedByte2(float x, float y) + : this(new Vector2(x, y)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public NormalizedByte2(Vector2 vector) => this.PackedValue = Pack(vector); + + /// + public ushort PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(NormalizedByte2 left, NormalizedByte2 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(NormalizedByte2 left, NormalizedByte2 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) + { + Vector2 scaled = new Vector2(vector.X, vector.Y) * 2F; + scaled -= Vector2.One; + this.PackedValue = Pack(scaled); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() + { + var scaled = this.ToVector2(); + scaled += Vector2.One; + scaled /= 2F; + return new Vector4(scaled, 0F, 1F); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) + { + var vector2 = new Vector2(vector.X, vector.Y); + this.PackedValue = Pack(vector2); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() => new Vector4(this.ToVector2(), 0F, 1F); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.FromScaledVector4(this.ToScaledVector4()); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + /// Expands the packed representation into a . + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Vector2 ToVector2() + { + return new Vector2( + (sbyte)((this.PackedValue >> 0) & 0xFF) / 127F, + (sbyte)((this.PackedValue >> 8) & 0xFF) / 127F); + } + + /// + public override bool Equals(object obj) => obj is NormalizedByte2 other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(NormalizedByte2 other) => this.PackedValue.Equals(other.PackedValue); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override string ToString() + { + var vector = this.ToVector2(); + return FormattableString.Invariant($"NormalizedByte2({vector.X:#0.##}, {vector.Y:#0.##})"); + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static ushort Pack(Vector2 vector) + { + vector = Vector2.Clamp(vector, MinusOne, Vector2.One) * Half; + + int byte2 = ((ushort)Math.Round(vector.X) & 0xFF) << 0; + int byte1 = ((ushort)Math.Round(vector.Y) & 0xFF) << 8; + + return (ushort)(byte2 | byte1); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs new file mode 100644 index 0000000000..82698d5085 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedByte4.cs @@ -0,0 +1,175 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing four 8-bit signed normalized values, ranging from −1 to 1. + /// + /// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form. + /// + /// + public struct NormalizedByte4 : IPixel, IPackedVector + { + private static readonly Vector4 Half = new Vector4(127); + private static readonly Vector4 MinusOne = new Vector4(-1F); + + /// + /// Initializes a new instance of the struct. + /// + /// The x-component. + /// The y-component. + /// The z-component. + /// The w-component. + public NormalizedByte4(float x, float y, float z, float w) + : this(new Vector4(x, y, z, w)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public NormalizedByte4(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + public uint PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(NormalizedByte4 left, NormalizedByte4 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(NormalizedByte4 left, NormalizedByte4 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) + { + vector *= 2F; + vector -= Vector4.One; + this.FromVector4(vector); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() + { + var scaled = this.ToVector4(); + scaled += Vector4.One; + scaled /= 2F; + return scaled; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() + { + return new Vector4( + (sbyte)((this.PackedValue >> 0) & 0xFF) / 127F, + (sbyte)((this.PackedValue >> 8) & 0xFF) / 127F, + (sbyte)((this.PackedValue >> 16) & 0xFF) / 127F, + (sbyte)((this.PackedValue >> 24) & 0xFF) / 127F); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.FromScaledVector4(this.ToScaledVector4()); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object obj) => obj is NormalizedByte4 other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(NormalizedByte4 other) => this.PackedValue.Equals(other.PackedValue); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override string ToString() + { + var vector = this.ToVector4(); + return FormattableString.Invariant($"NormalizedByte4({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static uint Pack(ref Vector4 vector) + { + vector = Vector4.Clamp(vector, MinusOne, Vector4.One) * Half; + + uint byte4 = ((uint)Math.Round(vector.X) & 0xFF) << 0; + uint byte3 = ((uint)Math.Round(vector.Y) & 0xFF) << 8; + uint byte2 = ((uint)Math.Round(vector.Z) & 0xFF) << 16; + uint byte1 = ((uint)Math.Round(vector.W) & 0xFF) << 24; + + return byte4 | byte3 | byte2 | byte1; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs new file mode 100644 index 0000000000..b9cab1e7d3 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort2.cs @@ -0,0 +1,185 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing two 16-bit signed normalized values, ranging from −1 to 1. + /// + /// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form. + /// + /// + public struct NormalizedShort2 : IPixel, IPackedVector + { + private static readonly Vector2 Max = new Vector2(0x7FFF); + private static readonly Vector2 Min = Vector2.Negate(Max); + + /// + /// Initializes a new instance of the struct. + /// + /// The x-component. + /// The y-component. + public NormalizedShort2(float x, float y) + : this(new Vector2(x, y)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public NormalizedShort2(Vector2 vector) => this.PackedValue = Pack(vector); + + /// + public uint PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(NormalizedShort2 left, NormalizedShort2 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(NormalizedShort2 left, NormalizedShort2 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) + { + Vector2 scaled = new Vector2(vector.X, vector.Y) * 2F; + scaled -= Vector2.One; + this.PackedValue = Pack(scaled); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() + { + var scaled = this.ToVector2(); + scaled += Vector2.One; + scaled /= 2F; + return new Vector4(scaled, 0F, 1F); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) + { + var vector2 = new Vector2(vector.X, vector.Y); + this.PackedValue = Pack(vector2); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() => new Vector4(this.ToVector2(), 0, 1); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.FromScaledVector4(this.ToScaledVector4()); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + /// Expands the packed representation into a . + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Vector2 ToVector2() + { + const float MaxVal = 0x7FFF; + + return new Vector2( + (short)(this.PackedValue & 0xFFFF) / MaxVal, + (short)(this.PackedValue >> 0x10) / MaxVal); + } + + /// + public override bool Equals(object obj) => obj is NormalizedShort2 other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(NormalizedShort2 other) => this.PackedValue.Equals(other.PackedValue); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override string ToString() + { + var vector = this.ToVector2(); + return FormattableString.Invariant($"NormalizedShort2({vector.X:#0.##}, {vector.Y:#0.##})"); + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static uint Pack(Vector2 vector) + { + vector *= Max; + vector = Vector2.Clamp(vector, Min, Max); + + // Round rather than truncate. + uint word2 = (uint)((int)MathF.Round(vector.X) & 0xFFFF); + uint word1 = (uint)(((int)MathF.Round(vector.Y) & 0xFFFF) << 0x10); + + return word2 | word1; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs new file mode 100644 index 0000000000..3bc74e6c67 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/NormalizedShort4.cs @@ -0,0 +1,179 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing four 16-bit signed normalized values, ranging from −1 to 1. + /// + /// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form. + /// + /// + public struct NormalizedShort4 : IPixel, IPackedVector + { + private static readonly Vector4 Max = new Vector4(0x7FFF); + private static readonly Vector4 Min = Vector4.Negate(Max); + + /// + /// Initializes a new instance of the struct. + /// + /// The x-component. + /// The y-component. + /// The z-component. + /// The w-component. + public NormalizedShort4(float x, float y, float z, float w) + : this(new Vector4(x, y, z, w)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public NormalizedShort4(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + public ulong PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(NormalizedShort4 left, NormalizedShort4 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(NormalizedShort4 left, NormalizedShort4 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) + { + vector *= 2F; + vector -= Vector4.One; + this.FromVector4(vector); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() + { + var scaled = this.ToVector4(); + scaled += Vector4.One; + scaled /= 2F; + return scaled; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() + { + const float MaxVal = 0x7FFF; + + return new Vector4( + (short)((this.PackedValue >> 0x00) & 0xFFFF) / MaxVal, + (short)((this.PackedValue >> 0x10) & 0xFFFF) / MaxVal, + (short)((this.PackedValue >> 0x20) & 0xFFFF) / MaxVal, + (short)((this.PackedValue >> 0x30) & 0xFFFF) / MaxVal); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.FromScaledVector4(this.ToScaledVector4()); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object obj) => obj is NormalizedShort4 other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(NormalizedShort4 other) => this.PackedValue.Equals(other.PackedValue); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override string ToString() + { + var vector = this.ToVector4(); + return FormattableString.Invariant($"NormalizedShort4({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static ulong Pack(ref Vector4 vector) + { + vector *= Max; + vector = Vector4.Clamp(vector, Min, Max); + + // Round rather than truncate. + ulong word4 = ((ulong)MathF.Round(vector.X) & 0xFFFF) << 0x00; + ulong word3 = ((ulong)MathF.Round(vector.Y) & 0xFFFF) << 0x10; + ulong word2 = ((ulong)MathF.Round(vector.Z) & 0xFFFF) << 0x20; + ulong word1 = ((ulong)MathF.Round(vector.W) & 0xFFFF) << 0x30; + + return word4 | word3 | word2 | word1; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs new file mode 100644 index 0000000000..6dc623518d --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rg32.cs @@ -0,0 +1,160 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing two 16-bit unsigned normalized values ranging from 0 to 1. + /// + /// Ranges from [0, 0, 0, 1] to [1, 1, 0, 1] in vector form. + /// + /// + public struct Rg32 : IPixel, IPackedVector + { + private static readonly Vector2 Max = new Vector2(ushort.MaxValue); + + /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + public Rg32(float x, float y) + : this(new Vector2(x, y)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public Rg32(Vector2 vector) => this.PackedValue = Pack(vector); + + /// + public uint PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Rg32 left, Rg32 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Rg32 left, Rg32 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) + { + var vector2 = new Vector2(vector.X, vector.Y); + this.PackedValue = Pack(vector2); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() => new Vector4(this.ToVector2(), 0F, 1F); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.FromScaledVector4(this.ToScaledVector4()); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + /// Expands the packed representation into a . + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Vector2 ToVector2() => new Vector2(this.PackedValue & 0xFFFF, (this.PackedValue >> 16) & 0xFFFF) / Max; + + /// + public override bool Equals(object obj) => obj is Rg32 other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Rg32 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() + { + var vector = this.ToVector2(); + return FormattableString.Invariant($"Rg32({vector.X:#0.##}, {vector.Y:#0.##})"); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(InliningOptions.ShortMethod)] + private static uint Pack(Vector2 vector) + { + vector = Vector2.Clamp(vector, Vector2.Zero, Vector2.One) * Max; + return (uint)(((int)Math.Round(vector.X) & 0xFFFF) | (((int)Math.Round(vector.Y) & 0xFFFF) << 16)); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs new file mode 100644 index 0000000000..293fe0acbf --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -0,0 +1,228 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Pixel type containing three 8-bit unsigned normalized values ranging from 0 to 255. + /// The color components are stored in red, green, blue order (least significant to most significant byte). + /// + /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. + /// + /// + [StructLayout(LayoutKind.Explicit)] + public partial struct Rgb24 : IPixel + { + /// + /// The red component. + /// + [FieldOffset(0)] + public byte R; + + /// + /// The green component. + /// + [FieldOffset(1)] + public byte G; + + /// + /// The blue component. + /// + [FieldOffset(2)] + public byte B; + + private static readonly Vector4 MaxBytes = new Vector4(byte.MaxValue); + 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(InliningOptions.ShortMethod)] + public Rgb24(byte r, byte g, byte b) + { + this.R = r; + this.G = g; + this.B = b; + } + + /// + /// Allows the implicit conversion of an instance of to a + /// . + /// + /// The instance of to convert. + /// An instance of . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Rgb24(ColorSpaces.Rgb color) + { + var vector = new Vector4(color.ToVector3(), 1F); + + Rgb24 rgb = default; + rgb.FromScaledVector4(vector); + return rgb; + } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Rgb24 left, Rgb24 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Rgb24 left, Rgb24 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.Pack(ref vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() => new Rgba32(this.R, this.G, this.B, byte.MaxValue).ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) + { + this.R = source.R; + this.G = source.G; + this.B = source.B; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) + { + this.R = source.R; + this.G = source.G; + this.B = source.B; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) + { + this.R = source.R; + this.G = source.G; + this.B = source.B; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) + { + this.R = source.PackedValue; + this.G = source.PackedValue; + this.B = source.PackedValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) + { + byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(source.PackedValue); + this.R = rgb; + this.G = rgb; + this.B = rgb; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this = source; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this = source.Rgb; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.R = this.R; + dest.G = this.G; + dest.B = this.B; + dest.A = byte.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) + { + this.R = ImageMaths.DownScaleFrom16BitTo8Bit(source.R); + this.G = ImageMaths.DownScaleFrom16BitTo8Bit(source.G); + this.B = ImageMaths.DownScaleFrom16BitTo8Bit(source.B); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) + { + this.R = ImageMaths.DownScaleFrom16BitTo8Bit(source.R); + this.G = ImageMaths.DownScaleFrom16BitTo8Bit(source.G); + this.B = ImageMaths.DownScaleFrom16BitTo8Bit(source.B); + } + + /// + public override bool Equals(object obj) => obj is Rgb24 other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Rgb24 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() + { + int hash = HashHelpers.Combine(this.R.GetHashCode(), this.G.GetHashCode()); + return HashHelpers.Combine(hash, this.B.GetHashCode()); + } + + /// + public override string ToString() => $"Rgb24({this.R}, {this.G}, {this.B})"; + + /// + /// Packs a into a color. + /// + /// The vector containing the values to pack. + [MethodImpl(InliningOptions.ShortMethod)] + private void Pack(ref Vector4 vector) + { + vector *= MaxBytes; + vector += Half; + vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); + + this.R = (byte)vector.X; + this.G = (byte)vector.Y; + this.B = (byte)vector.Z; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs new file mode 100644 index 0000000000..81497e5f18 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb48.cs @@ -0,0 +1,199 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing three 16-bit unsigned normalized values ranging from 0 to 635535. + /// + /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. + /// + /// + [StructLayout(LayoutKind.Sequential)] + public partial struct Rgb48 : IPixel + { + private const float Max = ushort.MaxValue; + + /// + /// Gets or sets the red component. + /// + public ushort R; + + /// + /// Gets or sets the green component. + /// + public ushort G; + + /// + /// Gets or sets the blue component. + /// + public ushort B; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + public Rgb48(ushort r, ushort g, ushort b) + : this() + { + this.R = r; + this.G = g; + this.B = b; + } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Rgb48 left, Rgb48 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Rgb48 left, Rgb48 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) + { + vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max; + this.R = (ushort)MathF.Round(vector.X); + this.G = (ushort)MathF.Round(vector.Y); + this.B = (ushort)MathF.Round(vector.Z); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() => new Vector4(this.R / Max, this.G / Max, this.B / Max, 1F); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this = source.Rgb; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) + { + ushort rgb = ImageMaths.UpscaleFrom8BitTo16Bit(source.PackedValue); + this.R = rgb; + this.G = rgb; + this.B = rgb; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) + { + this.R = source.PackedValue; + this.G = source.PackedValue; + this.B = source.PackedValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.R = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); + dest.G = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); + dest.B = ImageMaths.DownScaleFrom16BitTo8Bit(this.B); + dest.A = byte.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this = source; + + /// + public override bool Equals(object obj) => obj is Rgb48 rgb48 && this.Equals(rgb48); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Rgb48 other) => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); + + /// + public override string ToString() => $"Rgb48({this.R}, {this.G}, {this.B})"; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() + { + return HashHelpers.Combine( + this.R.GetHashCode(), + HashHelpers.Combine(this.G.GetHashCode(), this.B.GetHashCode())); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs new file mode 100644 index 0000000000..895added1b --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba1010102.cs @@ -0,0 +1,163 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed vector type containing unsigned normalized values ranging from 0 to 1. + /// The x, y and z components use 10 bits, and the w component uses 2 bits. + /// + /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. + /// + /// + public struct Rgba1010102 : IPixel, IPackedVector + { + private static readonly Vector4 Multiplier = new Vector4(1023F, 1023F, 1023F, 3F); + + /// + /// Initializes a new instance of the struct. + /// + /// The x-component + /// The y-component + /// The z-component + /// The w-component + public Rgba1010102(float x, float y, float z, float w) + : this(new Vector4(x, y, z, w)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public Rgba1010102(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + public uint PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Rgba1010102 left, Rgba1010102 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Rgba1010102 left, Rgba1010102 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() + { + return new Vector4( + (this.PackedValue >> 0) & 0x03FF, + (this.PackedValue >> 10) & 0x03FF, + (this.PackedValue >> 20) & 0x03FF, + (this.PackedValue >> 30) & 0x03) / Multiplier; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.FromScaledVector4(this.ToScaledVector4()); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object obj) => obj is Rgba1010102 other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Rgba1010102 other) => this.PackedValue == other.PackedValue; + + /// + public override string ToString() + { + var vector = this.ToVector4(); + return FormattableString.Invariant($"Rgba1010102({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + [MethodImpl(InliningOptions.ShortMethod)] + private static uint Pack(ref Vector4 vector) + { + vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Multiplier; + + return (uint)( + (((int)Math.Round(vector.X) & 0x03FF) << 0) + | (((int)Math.Round(vector.Y) & 0x03FF) << 10) + | (((int)Math.Round(vector.Z) & 0x03FF) << 20) + | (((int)Math.Round(vector.W) & 0x03) << 30)); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Rgba32.Definitions.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.Definitions.cs similarity index 100% rename from src/ImageSharp/PixelFormats/Rgba32.Definitions.cs rename to src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.Definitions.cs diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs new file mode 100644 index 0000000000..004b25cd33 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.PixelOperations.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using SixLabors.Memory; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct Rgba32 + { + /// + /// implementation optimized for . + /// + internal partial class PixelOperations : PixelOperations + { + /// + internal override void ToVector4( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destVectors) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors)); + + destVectors = destVectors.Slice(0, sourcePixels.Length); + + SimdUtils.BulkConvertByteToNormalizedFloat( + MemoryMarshal.Cast(sourcePixels), + MemoryMarshal.Cast(destVectors)); + } + + /// + internal override void FromVector4( + Configuration configuration, + ReadOnlySpan sourceVectors, + Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels)); + + destPixels = destPixels.Slice(0, sourceVectors.Length); + + SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows( + MemoryMarshal.Cast(sourceVectors), + MemoryMarshal.Cast(destPixels)); + } + + /// + internal override void ToScaledVector4( + Configuration configuration, + ReadOnlySpan sourceColors, + Span destinationVectors) + { + this.ToVector4(configuration, sourceColors, destinationVectors); + } + + /// + internal override void FromScaledVector4( + Configuration configuration, + ReadOnlySpan sourceVectors, + Span destinationColors) + { + this.FromVector4(configuration, sourceVectors, destinationColors); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs similarity index 55% rename from src/ImageSharp/PixelFormats/Rgba32.cs rename to src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index cf66538c52..5a16704ef0 100644 --- a/src/ImageSharp/PixelFormats/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -41,34 +41,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// public byte A; - /// - /// The shift count for the red component - /// - private const int RedShift = 0; - - /// - /// The shift count for the green component - /// - private const int GreenShift = 8; - - /// - /// The shift count for the blue component - /// - private const int BlueShift = 16; - - /// - /// The shift count for the alpha component - /// - private const int AlphaShift = 24; - - /// - /// The maximum byte value. - /// - private static readonly Vector4 MaxBytes = new Vector4(255); - - /// - /// The half vector value. - /// + private static readonly Vector4 MaxBytes = new Vector4(byte.MaxValue); private static readonly Vector4 Half = new Vector4(0.5F); /// @@ -77,7 +50,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The red component. /// The green component. /// The blue component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Rgba32(byte r, byte g, byte b) { this.R = r; @@ -93,7 +66,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The green component. /// The blue component. /// The alpha component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Rgba32(byte r, byte g, byte b, byte a) { this.R = r; @@ -109,12 +82,9 @@ namespace SixLabors.ImageSharp.PixelFormats /// The green component. /// The blue component. /// The alpha component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Rgba32(float r, float g, float b, float a = 1) - : this() - { - this.Pack(r, g, b, a); - } + : this() => this.Pack(r, g, b, a); /// /// Initializes a new instance of the struct. @@ -122,12 +92,9 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// The vector containing the components for the packed vector. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Rgba32(Vector3 vector) - : this() - { - this.Pack(ref vector); - } + : this() => this.Pack(ref vector); /// /// Initializes a new instance of the struct. @@ -135,12 +102,9 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// The vector containing the components for the packed vector. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Rgba32(Vector4 vector) - : this() - { - this = PackNew(ref vector); - } + : this() => this = PackNew(ref vector); /// /// Initializes a new instance of the struct. @@ -148,22 +112,19 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// The packed value. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Rgba32(uint packed) - : this() - { - this.Rgba = packed; - } + : this() => this.Rgba = packed; /// /// Gets or sets the packed representation of the Rgba32 struct. /// public uint Rgba { - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] get => Unsafe.As(ref this); - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] set => Unsafe.As(ref this) = value; } @@ -172,10 +133,12 @@ namespace SixLabors.ImageSharp.PixelFormats /// public Rgb24 Rgb { - [MethodImpl(MethodImplOptions.AggressiveInlining)] + // If this is changed to ShortMethod then several jpeg encoding tests fail + // on 32 bit Net 4.6.2 and NET 4.7.1 + [MethodImpl(InliningOptions.ColdPath)] get => Unsafe.As(ref this); - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] set => Unsafe.As(ref this) = value; } @@ -184,10 +147,10 @@ namespace SixLabors.ImageSharp.PixelFormats /// public Bgr24 Bgr { - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] get => new Bgr24(this.R, this.G, this.B); - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] set { this.R = value.R; @@ -199,30 +162,39 @@ namespace SixLabors.ImageSharp.PixelFormats /// public uint PackedValue { - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] get => this.Rgba; - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] set => this.Rgba = value; } + /// + /// Allows the implicit conversion of an instance of to a + /// . + /// + /// The instance of to convert. + /// An instance of . + [MethodImpl(InliningOptions.ShortMethod)] + public static implicit operator Rgba32(ColorSpaces.Rgb color) + { + var vector = new Vector4(color.ToVector3(), 1F); + + Rgba32 rgba = default; + rgba.FromScaledVector4(vector); + return rgba; + } + /// /// Compares two objects for equality. /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// + /// The on the left side of the operand. + /// The on the right side of the operand. /// /// True if the parameter is equal to the parameter; otherwise, false. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rgba32 left, Rgba32 right) - { - return left.Rgba == right.Rgba; - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Rgba32 left, Rgba32 right) => left.Equals(right); /// /// Compares two objects for equality. @@ -232,11 +204,8 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// True if the parameter is not equal to the parameter; otherwise, false. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rgba32 left, Rgba32 right) - { - return left.Rgba != right.Rgba; - } + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Rgba32 left, Rgba32 right) => !left.Equals(right); /// /// Creates a new instance of the struct. @@ -248,34 +217,30 @@ namespace SixLabors.ImageSharp.PixelFormats /// /// The . /// - public static Rgba32 FromHex(string hex) - { - return ColorBuilder.FromHex(hex); - } + public static Rgba32 FromHex(string hex) => ColorBuilder.FromHex(hex); /// public PixelOperations CreatePixelOperations() => new PixelOperations(); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - this = source; - } + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - this.A = source.A; - } + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.Pack(ref vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) { this.R = source.R; this.G = source.G; @@ -283,178 +248,114 @@ namespace SixLabors.ImageSharp.PixelFormats this.A = source.A; } - /// - /// Converts the value of this instance to a hexadecimal string. - /// - /// A hexadecimal string representation of the value. - public string ToHex() - { - uint hexOrder = (uint)(this.A << 0 | this.B << 8 | this.G << 16 | this.R << 24); - return hexOrder.ToString("X8"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - dest = Unsafe.As(ref this); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) { - dest = this; + this.Bgr = source; + this.A = byte.MaxValue; } - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToArgb32(ref Argb32 dest) + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = this.A; + this.R = source.R; + this.G = source.G; + this.B = source.B; + this.A = source.A; } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgr24(ref Bgr24 dest) + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; + this.R = source.PackedValue; + this.G = source.PackedValue; + this.B = source.PackedValue; + this.A = byte.MaxValue; } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32(ref Bgra32 dest) + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = this.A; + byte rgb = ImageMaths.DownScaleFrom16BitTo8Bit(source.PackedValue); + this.R = rgb; + this.G = rgb; + this.B = rgb; + this.A = byte.MaxValue; } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) { - this.PackFromVector4(vector); + this.Rgb = source; + this.A = byte.MaxValue; } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this = source; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) { - return this.ToVector4(); + dest = this; } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) { - this.Pack(ref vector); + this.R = ImageMaths.DownScaleFrom16BitTo8Bit(source.R); + this.G = ImageMaths.DownScaleFrom16BitTo8Bit(source.G); + this.B = ImageMaths.DownScaleFrom16BitTo8Bit(source.B); + this.A = byte.MaxValue; } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) { - return new Vector4(this.R, this.G, this.B, this.A) / MaxBytes; + this.R = ImageMaths.DownScaleFrom16BitTo8Bit(source.R); + this.G = ImageMaths.DownScaleFrom16BitTo8Bit(source.G); + this.B = ImageMaths.DownScaleFrom16BitTo8Bit(source.B); + this.A = ImageMaths.DownScaleFrom16BitTo8Bit(source.A); } /// - /// Gets the value of this struct as . - /// Useful for changing the component order. - /// - /// A value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Bgra32 ToBgra32() => new Bgra32(this.R, this.G, this.B, this.A); - - /// - /// Gets the value of this struct as . - /// Useful for changing the component order. - /// - /// A value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Argb32 ToArgb32() => new Argb32(this.R, this.G, this.B, this.A); - - /// - /// Converts the pixel to format. + /// Converts the value of this instance to a hexadecimal string. /// - /// The RGBA value - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgba32 ToRgba32() => this; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgb48(Rgb48 source) - { - this.R = (byte)(((source.R * 255) + 32895) >> 16); - this.G = (byte)(((source.G * 255) + 32895) >> 16); - this.B = (byte)(((source.B * 255) + 32895) >> 16); - this.A = byte.MaxValue; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) + /// A hexadecimal string representation of the value. + public string ToHex() { - // Taken from libpng pngtran.c line: 2419 - this.R = (byte)(((source.R * 255) + 32895) >> 16); - this.G = (byte)(((source.G * 255) + 32895) >> 16); - this.B = (byte)(((source.B * 255) + 32895) >> 16); - this.A = (byte)(((source.A * 255) + 32895) >> 16); + uint hexOrder = (uint)(this.A << 0 | this.B << 8 | this.G << 16 | this.R << 24); + return hexOrder.ToString("X8"); } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); + public override bool Equals(object obj) => obj is Rgba32 rgba32 && this.Equals(rgba32); /// - public override bool Equals(object obj) - { - return obj is Rgba32 rgba32 && this.Equals(rgba32); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rgba32 other) - { - return this.Rgba == other.Rgba; - } + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Rgba32 other) => this.Rgba.Equals(other.Rgba); /// - public override string ToString() - { - return $"({this.R},{this.G},{this.B},{this.A})"; - } + public override string ToString() => $"Rgba32({this.R}, {this.G}, {this.B}, {this.A})"; /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public override int GetHashCode() => this.Rgba.GetHashCode(); - /// - /// Gets the representation without normalizing to [0, 1] - /// - /// A of values in [0, 255] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Vector4 ToByteScaledVector4() - { - return new Vector4(this.R, this.G, this.B, this.A); - } - /// /// Packs a into a color returning a new instance as a result. /// /// The vector containing the values to pack. /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private static Rgba32 PackNew(ref Vector4 vector) { vector *= MaxBytes; @@ -471,7 +372,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// The y-component /// The z-component /// The w-component - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private void Pack(float x, float y, float z, float w) { var value = new Vector4(x, y, z, w); @@ -482,10 +383,10 @@ namespace SixLabors.ImageSharp.PixelFormats /// Packs a into a uint. /// /// The vector containing the values to pack. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private void Pack(ref Vector3 vector) { - var value = new Vector4(vector, 1); + var value = new Vector4(vector, 1F); this.Pack(ref value); } @@ -493,7 +394,7 @@ namespace SixLabors.ImageSharp.PixelFormats /// Packs a into a color. /// /// The vector containing the values to pack. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] private void Pack(ref Vector4 vector) { vector *= MaxBytes; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs new file mode 100644 index 0000000000..5ae5492e23 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba64.cs @@ -0,0 +1,234 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing four 16-bit unsigned normalized values ranging from 0 to 635535. + /// + /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. + /// + /// + [StructLayout(LayoutKind.Sequential)] + public partial struct Rgba64 : IPixel, IPackedVector + { + private const float Max = ushort.MaxValue; + + /// + /// Gets or sets the red component. + /// + public ushort R; + + /// + /// Gets or sets the green component. + /// + public ushort G; + + /// + /// Gets or sets the blue component. + /// + public ushort B; + + /// + /// Gets or sets the alpha component. + /// + public ushort A; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component. + /// The green component. + /// The blue component. + /// The alpha component. + public Rgba64(ushort r, ushort g, ushort b, ushort a) + { + this.R = r; + this.G = g; + this.B = b; + this.A = a; + } + + /// + /// Gets or sets the RGB components of this struct as + /// + public Rgb48 Rgb + { + [MethodImpl(InliningOptions.ShortMethod)] + get => Unsafe.As(ref this); + + [MethodImpl(InliningOptions.ShortMethod)] + set => Unsafe.As(ref this) = value; + } + + /// + public ulong PackedValue + { + [MethodImpl(InliningOptions.ShortMethod)] + get => Unsafe.As(ref this); + + [MethodImpl(InliningOptions.ShortMethod)] + set => Unsafe.As(ref this) = value; + } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Rgba64 left, Rgba64 right) => left.PackedValue == right.PackedValue; + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Rgba64 left, Rgba64 right) => left.PackedValue != right.PackedValue; + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) + { + vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max; + this.R = (ushort)MathF.Round(vector.X); + this.G = (ushort)MathF.Round(vector.Y); + this.B = (ushort)MathF.Round(vector.Z); + this.A = (ushort)MathF.Round(vector.W); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A) / Max; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + this.A = ImageMaths.UpscaleFrom8BitTo16Bit(source.A); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + this.A = ushort.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + this.A = ImageMaths.UpscaleFrom8BitTo16Bit(source.A); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) + { + ushort rgb = ImageMaths.UpscaleFrom8BitTo16Bit(source.PackedValue); + this.R = rgb; + this.G = rgb; + this.B = rgb; + this.A = ushort.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) + { + this.R = source.PackedValue; + this.G = source.PackedValue; + this.B = source.PackedValue; + this.A = ushort.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + this.A = ushort.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) + { + this.R = ImageMaths.UpscaleFrom8BitTo16Bit(source.R); + this.G = ImageMaths.UpscaleFrom8BitTo16Bit(source.G); + this.B = ImageMaths.UpscaleFrom8BitTo16Bit(source.B); + this.A = ImageMaths.UpscaleFrom8BitTo16Bit(source.A); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.R = ImageMaths.DownScaleFrom16BitTo8Bit(this.R); + dest.G = ImageMaths.DownScaleFrom16BitTo8Bit(this.G); + dest.B = ImageMaths.DownScaleFrom16BitTo8Bit(this.B); + dest.A = ImageMaths.DownScaleFrom16BitTo8Bit(this.A); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) + { + this.Rgb = source; + this.A = ushort.MaxValue; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this = source; + + /// + public override bool Equals(object obj) => obj is Rgba64 rgba64 && this.Equals(rgba64); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Rgba64 other) => this.PackedValue.Equals(other.PackedValue); + + /// + public override string ToString() => $"Rgba64({this.R}, {this.G}, {this.B}, {this.A})"; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs new file mode 100644 index 0000000000..bffaf57ddd --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.PixelOperations.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Provides optimized overrides for bulk operations. + /// + public partial struct RgbaVector + { + /// + /// implementation optimized for . + /// + internal class PixelOperations : PixelOperations + { + /// + internal override void FromScaledVector4( + Configuration configuration, + ReadOnlySpan sourceVectors, + Span destinationColors) + { + Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationColors, nameof(destinationColors)); + + MemoryMarshal.Cast(sourceVectors).CopyTo(destinationColors); + } + + /// + internal override void ToScaledVector4( + Configuration configuration, + ReadOnlySpan sourceColors, + Span destinationVectors) + => this.ToVector4(configuration, sourceColors, destinationVectors); + + /// + internal override void ToVector4( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destVectors) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors)); + + MemoryMarshal.Cast(sourcePixels).CopyTo(destVectors); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs new file mode 100644 index 0000000000..ff4c69d701 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/RgbaVector.cs @@ -0,0 +1,207 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Unpacked pixel type containing four 16-bit floating-point values typically ranging from 0 to 1. + /// The color components are stored in red, green, blue, and alpha order. + /// + /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. + /// + /// + /// + /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, + /// as it avoids the need to create new values for modification operations. + /// + [StructLayout(LayoutKind.Sequential)] + public partial struct RgbaVector : IPixel + { + /// + /// Gets or sets the red component. + /// + public float R; + + /// + /// Gets or sets the green component. + /// + public float G; + + /// + /// Gets or sets the blue component. + /// + public float B; + + /// + /// Gets or sets the alpha component. + /// + public float A; + + private const float MaxBytes = byte.MaxValue; + private static readonly Vector4 Max = new Vector4(MaxBytes); + 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. + /// The alpha component. + [MethodImpl(InliningOptions.ShortMethod)] + public RgbaVector(float r, float g, float b, float a = 1) + { + this.R = r; + this.G = g; + this.B = b; + this.A = a; + } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(RgbaVector left, RgbaVector right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(RgbaVector left, RgbaVector right) => !left.Equals(right); + + /// + /// Creates a new instance of the struct. + /// + /// + /// The hexadecimal representation of the combined color components arranged + /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. + /// + /// + /// The . + /// + public static RgbaVector FromHex(string hex) => ColorBuilder.FromHex(hex); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) => this.FromVector4(vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() => this.ToVector4(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) + { + vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One); + this.R = vector.X; + this.G = vector.Y; + this.B = vector.Z; + this.A = vector.W; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() => new Vector4(this.R, this.G, this.B, this.A); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.FromScaledVector4(this.ToScaledVector4()); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + /// Converts the value of this instance to a hexadecimal string. + /// + /// A hexadecimal string representation of the value. + public string ToHex() + { + // Hex is RRGGBBAA + Vector4 vector = this.ToVector4() * Max; + vector += Half; + uint hexOrder = (uint)((byte)vector.W | (byte)vector.Z << 8 | (byte)vector.Y << 16 | (byte)vector.X << 24); + return hexOrder.ToString("X8"); + } + + /// + public override bool Equals(object obj) => obj is RgbaVector other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(RgbaVector other) => + this.R.Equals(other.R) + && this.G.Equals(other.G) + && this.B.Equals(other.B) + && this.A.Equals(other.A); + + /// + public override string ToString() + { + var vector = this.ToVector4(); + return FormattableString.Invariant($"RgbaVector({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##}, {this.A:#0.##})"); + } + + /// + public override int GetHashCode() + { + int hash = HashHelpers.Combine(this.R.GetHashCode(), this.G.GetHashCode()); + hash = HashHelpers.Combine(hash, this.B.GetHashCode()); + return HashHelpers.Combine(hash, this.A.GetHashCode()); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs new file mode 100644 index 0000000000..96fe15ed61 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short2.cs @@ -0,0 +1,181 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing two 16-bit signed integer values. + /// + /// Ranges from [-32767, -32767, 0, 1] to [32767, 32767, 0, 1] in vector form. + /// + /// + public struct Short2 : IPixel, IPackedVector + { + // Largest two byte positive number 0xFFFF >> 1; + private const float MaxPos = 0x7FFF; + + // Two's complement + private const float MinNeg = ~(int)MaxPos; + + private static readonly Vector2 Max = new Vector2(MaxPos); + private static readonly Vector2 Min = new Vector2(MinNeg); + + /// + /// Initializes a new instance of the struct. + /// + /// The x-component. + /// The y-component. + public Short2(float x, float y) + : this(new Vector2(x, y)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector containing the component values. + public Short2(Vector2 vector) => this.PackedValue = Pack(vector); + + /// + public uint PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Short2 left, Short2 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Short2 left, Short2 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) + { + Vector2 scaled = new Vector2(vector.X, vector.Y) * 65534F; + scaled -= new Vector2(32767F); + this.PackedValue = Pack(scaled); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() + { + var scaled = this.ToVector2(); + scaled += new Vector2(32767F); + scaled /= 65534F; + return new Vector4(scaled, 0F, 1F); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) + { + var vector2 = new Vector2(vector.X, vector.Y); + this.PackedValue = Pack(vector2); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() => new Vector4((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10), 0, 1); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.FromScaledVector4(this.ToScaledVector4()); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + /// Expands the packed representation into a . + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + [MethodImpl(InliningOptions.ShortMethod)] + public Vector2 ToVector2() => new Vector2((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10)); + + /// + public override bool Equals(object obj) => obj is Short2 other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Short2 other) => this.PackedValue.Equals(other.PackedValue); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override string ToString() + { + var vector = this.ToVector2(); + return FormattableString.Invariant($"Short2({vector.X:#0.##}, {vector.Y:#0.##})"); + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static uint Pack(Vector2 vector) + { + vector = Vector2.Clamp(vector, Min, Max); + uint word2 = (uint)Math.Round(vector.X) & 0xFFFF; + uint word1 = ((uint)Math.Round(vector.Y) & 0xFFFF) << 0x10; + + return word2 | word1; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs new file mode 100644 index 0000000000..d224f8eb4e --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Short4.cs @@ -0,0 +1,185 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + /// + /// Packed pixel type containing four 16-bit signed integer values. + /// + /// Ranges from [-37267, -37267, -37267, -37267] to [37267, 37267, 37267, 37267] in vector form. + /// + /// + public struct Short4 : IPixel, IPackedVector + { + // Largest two byte positive number 0xFFFF >> 1; + private const float MaxPos = 0x7FFF; + + // Two's complement + private const float MinNeg = ~(int)MaxPos; + + private static readonly Vector4 Max = new Vector4(MaxPos); + private static readonly Vector4 Min = new Vector4(MinNeg); + + /// + /// Initializes a new instance of the struct. + /// + /// The x-component. + /// The y-component. + /// The z-component. + /// The w-component. + public Short4(float x, float y, float z, float w) + : this(new Vector4(x, y, z, w)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// A vector containing the initial values for the components. + public Short4(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + public ulong PackedValue { get; set; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Short4 left, Short4 right) => left.Equals(right); + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the parameter is not equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Short4 left, Short4 right) => !left.Equals(right); + + /// + public PixelOperations CreatePixelOperations() => new PixelOperations(); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromScaledVector4(Vector4 vector) + { + vector *= 65534F; + vector -= new Vector4(32767F); + this.FromVector4(vector); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToScaledVector4() + { + var scaled = this.ToVector4(); + scaled += new Vector4(32767F); + scaled /= 65534F; + return scaled; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromVector4(Vector4 vector) => this.PackedValue = Pack(ref vector); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 ToVector4() + { + return new Vector4( + (short)(this.PackedValue & 0xFFFF), + (short)((this.PackedValue >> 0x10) & 0xFFFF), + (short)((this.PackedValue >> 0x20) & 0xFFFF), + (short)((this.PackedValue >> 0x30) & 0xFFFF)); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromArgb32(Argb32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgr24(Bgr24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromBgra32(Bgra32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray8(Gray8 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromGray16(Gray16 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb24(Rgb24 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba32(Rgba32 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ToRgba32(ref Rgba32 dest) + { + dest.FromScaledVector4(this.ToScaledVector4()); + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgb48(Rgb48 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void FromRgba64(Rgba64 source) => this.FromScaledVector4(source.ToScaledVector4()); + + /// + public override bool Equals(object obj) => obj is Short4 other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Short4 other) => this.PackedValue.Equals(other); + + /// + /// Gets the hash code for the current instance. + /// + /// Hash code for the instance. + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => this.PackedValue.GetHashCode(); + + /// + public override string ToString() + { + var vector = this.ToVector4(); + return FormattableString.Invariant($"Short4({vector.X:#0.##}, {vector.Y:#0.##}, {vector.Z:#0.##}, {vector.W:#0.##})"); + } + + [MethodImpl(InliningOptions.ShortMethod)] + private static ulong Pack(ref Vector4 vector) + { + vector = Vector4.Clamp(vector, Min, Max); + + // Clamp the value between min and max values + ulong word4 = ((ulong)Math.Round(vector.X) & 0xFFFF) << 0x00; + ulong word3 = ((ulong)Math.Round(vector.Y) & 0xFFFF) << 0x10; + ulong word2 = ((ulong)Math.Round(vector.Z) & 0xFFFF) << 0x20; + ulong word1 = ((ulong)Math.Round(vector.W) & 0xFFFF) << 0x30; + + return word4 | word3 | word2 | word1; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs new file mode 100644 index 0000000000..207a8767d6 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.cs @@ -0,0 +1,661 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. +// + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + public partial class PixelOperations + { + /// + /// Converts all pixels in 'source` span of into a span of -s. + /// + /// A to configure internal operations + /// The source of data. + /// The to the destination pixels. + internal virtual void FromArgb32(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + ref Argb32 sourceBaseRef = ref MemoryMarshal.GetReference(source); + ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < source.Length; i++) + { + ref Argb32 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromArgb32(sp); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void FromArgb32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) + { + this.FromArgb32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destPixels); + } + + /// + /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// + /// A to configure internal operations + /// The span of source pixels + /// The destination span of data. + internal virtual void ToArgb32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Argb32 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Argb32 dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromScaledVector4(sp.ToScaledVector4()); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source pixels. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ToArgb32Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + { + this.ToArgb32(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + } + + /// + /// Converts all pixels in 'source` span of into a span of -s. + /// + /// A to configure internal operations + /// The source of data. + /// The to the destination pixels. + internal virtual void FromBgr24(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + ref Bgr24 sourceBaseRef = ref MemoryMarshal.GetReference(source); + ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < source.Length; i++) + { + ref Bgr24 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromBgr24(sp); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void FromBgr24Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) + { + this.FromBgr24(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destPixels); + } + + /// + /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// + /// A to configure internal operations + /// The span of source pixels + /// The destination span of data. + internal virtual void ToBgr24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgr24 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Bgr24 dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromScaledVector4(sp.ToScaledVector4()); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source pixels. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ToBgr24Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + { + this.ToBgr24(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + } + + /// + /// Converts all pixels in 'source` span of into a span of -s. + /// + /// A to configure internal operations + /// The source of data. + /// The to the destination pixels. + internal virtual void FromBgra32(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + ref Bgra32 sourceBaseRef = ref MemoryMarshal.GetReference(source); + ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < source.Length; i++) + { + ref Bgra32 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromBgra32(sp); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void FromBgra32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) + { + this.FromBgra32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destPixels); + } + + /// + /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// + /// A to configure internal operations + /// The span of source pixels + /// The destination span of data. + internal virtual void ToBgra32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Bgra32 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Bgra32 dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromScaledVector4(sp.ToScaledVector4()); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source pixels. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ToBgra32Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + { + this.ToBgra32(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + } + + /// + /// Converts all pixels in 'source` span of into a span of -s. + /// + /// A to configure internal operations + /// The source of data. + /// The to the destination pixels. + internal virtual void FromGray8(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + ref Gray8 sourceBaseRef = ref MemoryMarshal.GetReference(source); + ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < source.Length; i++) + { + ref Gray8 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromGray8(sp); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void FromGray8Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) + { + this.FromGray8(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destPixels); + } + + /// + /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// + /// A to configure internal operations + /// The span of source pixels + /// The destination span of data. + internal virtual void ToGray8(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray8 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Gray8 dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromScaledVector4(sp.ToScaledVector4()); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source pixels. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ToGray8Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + { + this.ToGray8(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + } + + /// + /// Converts all pixels in 'source` span of into a span of -s. + /// + /// A to configure internal operations + /// The source of data. + /// The to the destination pixels. + internal virtual void FromGray16(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + ref Gray16 sourceBaseRef = ref MemoryMarshal.GetReference(source); + ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < source.Length; i++) + { + ref Gray16 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromGray16(sp); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void FromGray16Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) + { + this.FromGray16(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destPixels); + } + + /// + /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// + /// A to configure internal operations + /// The span of source pixels + /// The destination span of data. + internal virtual void ToGray16(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Gray16 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Gray16 dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromScaledVector4(sp.ToScaledVector4()); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source pixels. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ToGray16Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + { + this.ToGray16(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + } + + /// + /// Converts all pixels in 'source` span of into a span of -s. + /// + /// A to configure internal operations + /// The source of data. + /// The to the destination pixels. + internal virtual void FromRgb24(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + ref Rgb24 sourceBaseRef = ref MemoryMarshal.GetReference(source); + ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < source.Length; i++) + { + ref Rgb24 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromRgb24(sp); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void FromRgb24Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) + { + this.FromRgb24(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destPixels); + } + + /// + /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// + /// A to configure internal operations + /// The span of source pixels + /// The destination span of data. + internal virtual void ToRgb24(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb24 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Rgb24 dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromScaledVector4(sp.ToScaledVector4()); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source pixels. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ToRgb24Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + { + this.ToRgb24(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + } + + /// + /// Converts all pixels in 'source` span of into a span of -s. + /// + /// A to configure internal operations + /// The source of data. + /// The to the destination pixels. + internal virtual void FromRgba32(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + ref Rgba32 sourceBaseRef = ref MemoryMarshal.GetReference(source); + ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < source.Length; i++) + { + ref Rgba32 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromRgba32(sp); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void FromRgba32Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) + { + this.FromRgba32(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destPixels); + } + + /// + /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// + /// A to configure internal operations + /// The span of source pixels + /// The destination span of data. + internal virtual void ToRgba32(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba32 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Rgba32 dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromScaledVector4(sp.ToScaledVector4()); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source pixels. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ToRgba32Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + { + this.ToRgba32(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + } + + /// + /// Converts all pixels in 'source` span of into a span of -s. + /// + /// A to configure internal operations + /// The source of data. + /// The to the destination pixels. + internal virtual void FromRgb48(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + ref Rgb48 sourceBaseRef = ref MemoryMarshal.GetReference(source); + ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < source.Length; i++) + { + ref Rgb48 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromRgb48(sp); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void FromRgb48Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) + { + this.FromRgb48(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destPixels); + } + + /// + /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// + /// A to configure internal operations + /// The span of source pixels + /// The destination span of data. + internal virtual void ToRgb48(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgb48 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Rgb48 dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromScaledVector4(sp.ToScaledVector4()); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source pixels. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ToRgb48Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + { + this.ToRgb48(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + } + + /// + /// Converts all pixels in 'source` span of into a span of -s. + /// + /// A to configure internal operations + /// The source of data. + /// The to the destination pixels. + internal virtual void FromRgba64(Configuration configuration, ReadOnlySpan source, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + ref Rgba64 sourceBaseRef = ref MemoryMarshal.GetReference(source); + ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < source.Length; i++) + { + ref Rgba64 sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromRgba64(sp); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void FromRgba64Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) + { + this.FromRgba64(configuration, MemoryMarshal.Cast(sourceBytes).Slice(0, count), destPixels); + } + + /// + /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// + /// A to configure internal operations + /// The span of source pixels + /// The destination span of data. + internal virtual void ToRgba64(Configuration configuration, ReadOnlySpan sourcePixels, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Rgba64 destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref Rgba64 dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromScaledVector4(sp.ToScaledVector4()); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source pixels. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ToRgba64Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + { + this.ToRgba64(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast(destBytes)); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt new file mode 100644 index 0000000000..8579423b34 --- /dev/null +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.Generated.tt @@ -0,0 +1,140 @@ +<# +// Copyright (c) Six Labors 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" #> +<#@ import namespace="System.Runtime.InteropServices" #> +<#@ output extension=".cs" #> +<# + + void GenerateFromMethods(string pixelType) + { + #> + + /// + /// Converts all pixels in 'source` span of into a span of -s. + /// + /// A to configure internal operations + /// The source of data. + /// The to the destination pixels. + internal virtual void From<#=pixelType#>(Configuration configuration, ReadOnlySpan<<#=pixelType#>> source, Span destPixels) + { + Guard.DestinationShouldNotBeTooShort(source, destPixels, nameof(destPixels)); + + ref <#=pixelType#> sourceBaseRef = ref MemoryMarshal.GetReference(source); + ref TPixel destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < source.Length; i++) + { + ref <#=pixelType#> sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref TPixel dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.From<#=pixelType#>(sp); + } + } + + /// + /// A helper for that expects a byte span. + /// The layout of the data in 'sourceBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source bytes. + /// The to the destination pixels. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void From<#=pixelType#>Bytes(Configuration configuration, ReadOnlySpan sourceBytes, Span destPixels, int count) + { + this.From<#=pixelType#>(configuration, MemoryMarshal.Cast>(sourceBytes).Slice(0, count), destPixels); + } + +<# + } + + void GenerateToDestFormatMethods(string pixelType) + { + #> + /// + /// Converts all pixels of the 'sourcePixels` span to a span of -s. + /// + /// A to configure internal operations + /// The span of source pixels + /// The destination span of data. + internal virtual void To<#=pixelType#>(Configuration configuration, ReadOnlySpan sourcePixels, Span<<#=pixelType#>> destPixels) + { + Guard.DestinationShouldNotBeTooShort(sourcePixels, destPixels, nameof(destPixels)); + + ref TPixel sourceBaseRef = ref MemoryMarshal.GetReference(sourcePixels); + ref <#=pixelType#> destBaseRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceBaseRef, i); + ref <#=pixelType#> dp = ref Unsafe.Add(ref destBaseRef, i); + + dp.FromScaledVector4(sp.ToScaledVector4()); + } + } + + /// + /// A helper for that expects a byte span as destination. + /// The layout of the data in 'destBytes' must be compatible with layout. + /// + /// A to configure internal operations + /// The to the source pixels. + /// The to the destination bytes. + /// The number of pixels to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void To<#=pixelType#>Bytes(Configuration configuration, ReadOnlySpan sourcePixels, Span destBytes, int count) + { + this.To<#=pixelType#>(configuration, sourcePixels.Slice(0, count), MemoryMarshal.Cast>(destBytes)); + } +<# + } + +#> +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. +// + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.PixelFormats +{ + public partial class PixelOperations + {<# + + GenerateFromMethods("Argb32"); + GenerateToDestFormatMethods("Argb32"); + + GenerateFromMethods("Bgr24"); + GenerateToDestFormatMethods("Bgr24"); + + GenerateFromMethods("Bgra32"); + GenerateToDestFormatMethods("Bgra32"); + + GenerateFromMethods("Gray8"); + GenerateToDestFormatMethods("Gray8"); + + GenerateFromMethods("Gray16"); + GenerateToDestFormatMethods("Gray16"); + + GenerateFromMethods("Rgb24"); + GenerateToDestFormatMethods("Rgb24"); + + GenerateFromMethods("Rgba32"); + GenerateToDestFormatMethods("Rgba32"); + + GenerateFromMethods("Rgb48"); + GenerateToDestFormatMethods("Rgb48"); + + GenerateFromMethods("Rgba64"); + GenerateToDestFormatMethods("Rgba64"); + +#> } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs index b12a2bfa58..115dd7a43d 100644 --- a/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs +++ b/src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -22,109 +23,130 @@ namespace SixLabors.ImageSharp.PixelFormats public static PixelOperations Instance { get; } = default(TPixel).CreatePixelOperations(); /// - /// Bulk version of + /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. /// + /// A to configure internal operations /// The to the source vectors. - /// The to the destination colors. - /// The number of pixels to convert. - internal virtual void PackFromVector4(ReadOnlySpan sourceVectors, Span destinationColors, int count) + /// The to the destination colors. + internal virtual void FromVector4( + Configuration configuration, + ReadOnlySpan sourceVectors, + Span destPixels) { - GuardSpans(sourceVectors, nameof(sourceVectors), destinationColors, nameof(destinationColors), count); - - ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceVectors); - ref TPixel destRef = ref MemoryMarshal.GetReference(destinationColors); + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels)); - for (int i = 0; i < count; i++) - { - ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i); - ref TPixel dp = ref Unsafe.Add(ref destRef, i); - dp.PackFromVector4(sp); - } + Utils.Vector4Converters.Default.DangerousFromVector4(sourceVectors, destPixels); } /// - /// Bulk version of . + /// Bulk version of converting 'sourceColors.Length' pixels into 'destinationVectors'. /// - /// The to the source colors. - /// The to the destination vectors. - /// The number of pixels to convert. - internal virtual void ToVector4(ReadOnlySpan sourceColors, Span destinationVectors, int count) + /// A to configure internal operations + /// The to the source colors. + /// The to the destination vectors. + internal virtual void ToVector4( + Configuration configuration, + ReadOnlySpan sourcePixels, + Span destVectors) { - GuardSpans(sourceColors, nameof(sourceColors), destinationVectors, nameof(destinationVectors), count); + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors)); - ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourceColors); - ref Vector4 destRef = ref MemoryMarshal.GetReference(destinationVectors); - - for (int i = 0; i < count; i++) - { - ref TPixel sp = ref Unsafe.Add(ref sourceRef, i); - ref Vector4 dp = ref Unsafe.Add(ref destRef, i); - dp = sp.ToVector4(); - } + Utils.Vector4Converters.Default.DangerousToVector4(sourcePixels, destVectors); } /// - /// Bulk version of + /// Bulk version of converting 'sourceVectors.Length' pixels into 'destinationColors'. /// + /// A to configure internal operations /// The to the source vectors. /// The to the destination colors. - /// The number of pixels to convert. - internal virtual void PackFromScaledVector4(ReadOnlySpan sourceVectors, Span destinationColors, int count) + internal virtual void FromScaledVector4( + Configuration configuration, + ReadOnlySpan sourceVectors, + Span destinationColors) { - GuardSpans(sourceVectors, nameof(sourceVectors), destinationColors, nameof(destinationColors), count); + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourceVectors, destinationColors, nameof(destinationColors)); - ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceVectors); - ref TPixel destRef = ref MemoryMarshal.GetReference(destinationColors); - - for (int i = 0; i < count; i++) - { - ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i); - ref TPixel dp = ref Unsafe.Add(ref destRef, i); - dp.PackFromScaledVector4(sp); - } + Utils.Vector4Converters.Default.DangerousFromScaledVector4(sourceVectors, destinationColors); } /// - /// Bulk version of . + /// Bulk version of converting 'sourceColors.Length' pixels into 'destinationVectors'. /// + /// A to configure internal operations /// The to the source colors. /// The to the destination vectors. - /// The number of pixels to convert. - internal virtual void ToScaledVector4(ReadOnlySpan sourceColors, Span destinationVectors, int count) + internal virtual void ToScaledVector4( + Configuration configuration, + ReadOnlySpan sourceColors, + Span destinationVectors) + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourceColors, destinationVectors, nameof(destinationVectors)); + + Utils.Vector4Converters.Default.DangerousToScaledVector4(sourceColors, destinationVectors); + } + + /// + /// Converts 'sourceColors.Length' pixels from 'sourceColors' into 'destinationColors'. + /// + /// The destination pixel type. + /// A to configure internal operations + /// The to the source colors. + /// The to the destination colors. + internal virtual void To( + Configuration configuration, + ReadOnlySpan sourceColors, + Span destinationColors) + where TDestinationPixel : struct, IPixel { - GuardSpans(sourceColors, nameof(sourceColors), destinationVectors, nameof(destinationVectors), count); + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourceColors, destinationColors, nameof(destinationColors)); + int count = sourceColors.Length; ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourceColors); - ref Vector4 destRef = ref MemoryMarshal.GetReference(destinationVectors); + // Gray8 and Gray16 are special implementations of IPixel in that they do not conform to the + // standard RGBA colorspace format and must be converted from RGBA using the special ITU BT709 algorithm. + // One of the requirements of FromScaledVector4/ToScaledVector4 is that it unaware of this and + // packs/unpacks the pixel without and conversion so we employ custom methods do do this. + if (typeof(TDestinationPixel) == typeof(Gray16)) + { + ref Gray16 gray16Ref = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(destinationColors)); + for (int i = 0; i < count; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray16 dp = ref Unsafe.Add(ref gray16Ref, i); + dp.ConvertFromRgbaScaledVector4(sp.ToScaledVector4()); + } + + return; + } + + if (typeof(TDestinationPixel) == typeof(Gray8)) + { + ref Gray8 gray8Ref = ref MemoryMarshal.GetReference(MemoryMarshal.Cast(destinationColors)); + for (int i = 0; i < count; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceRef, i); + ref Gray8 dp = ref Unsafe.Add(ref gray8Ref, i); + dp.ConvertFromRgbaScaledVector4(sp.ToScaledVector4()); + } + + return; + } + + // Normal conversion + ref TDestinationPixel destRef = ref MemoryMarshal.GetReference(destinationColors); for (int i = 0; i < count; i++) { ref TPixel sp = ref Unsafe.Add(ref sourceRef, i); - ref Vector4 dp = ref Unsafe.Add(ref destRef, i); - dp = sp.ToScaledVector4(); + ref TDestinationPixel dp = ref Unsafe.Add(ref destRef, i); + dp.FromScaledVector4(sp.ToScaledVector4()); } } - - /// - /// Verifies that the given 'source' and 'destination' spans are at least of 'minLength' size. - /// Throwing an if the condition is not met. - /// - /// 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( - ReadOnlySpan source, - string sourceParamName, - Span destination, - string destinationParamName, - int minLength) - { - Guard.MustBeSizedAtLeast(source, minLength, sourceParamName); - Guard.MustBeSizedAtLeast(destination, minLength, destinationParamName); - } } } \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/README.md b/src/ImageSharp/PixelFormats/README.md index c332bc92c1..cbebaf23ad 100644 --- a/src/ImageSharp/PixelFormats/README.md +++ b/src/ImageSharp/PixelFormats/README.md @@ -2,9 +2,5 @@ https://github.com/MonoGame/MonoGame -Rgba32 is our default format. As such it positioned within the ImageSharp root namespace to ensure visibility of the format. - -All other pixel formats should be positioned within ImageSharp.PixelFormats to reduce intellisense burden. - The naming convention of each pixel format is to order the color components from least significant to most significant, reading from left to right. For example in the Rgba32 pixel format the R component is the least significant byte, and the A component is the most significant. diff --git a/src/ImageSharp/PixelFormats/Rg32.cs b/src/ImageSharp/PixelFormats/Rg32.cs deleted file mode 100644 index e5ceeacec2..0000000000 --- a/src/ImageSharp/PixelFormats/Rg32.cs +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed pixel type containing two 16-bit unsigned normalized values ranging from 0 to 1. - /// - /// Ranges from [0, 0, 0, 1] to [1, 1, 0, 1] in vector form. - /// - /// - public struct Rg32 : IPixel, IPackedVector - { - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - public Rg32(float x, float y) - { - this.PackedValue = Pack(x, y); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public Rg32(Vector2 vector) - { - this.PackedValue = Pack(vector.X, vector.Y); - } - - /// - public uint PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rg32 left, Rg32 right) - { - return left.PackedValue == right.PackedValue; - } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rg32 left, Rg32 right) - { - return left.PackedValue != right.PackedValue; - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - /// Expands the packed representation into a . - /// The vector components are typically expanded in least to greatest significance order. - /// - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector2 ToVector2() - { - return new Vector2( - (this.PackedValue & 0xFFFF) / 65535F, - ((this.PackedValue >> 16) & 0xFFFF) / 65535F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - return this.ToVector4(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4(this.ToVector2(), 0F, 1F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = (byte)vector.Z; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToArgb32(ref Argb32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToBgr24(ref Bgr24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - dest.R = (byte)vector.X; - dest.G = (byte)vector.Y; - dest.B = (byte)vector.Z; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32(ref Bgra32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - public override bool Equals(object obj) - { - return obj is Rg32 other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rg32 other) - { - return this.PackedValue == other.PackedValue; - } - - /// - public override string ToString() - { - return this.ToVector2().ToString(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - { - return this.PackedValue.GetHashCode(); - } - - /// - /// Packs the components into a . - /// - /// The x-component - /// The y-component - /// The containing the packed values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(float x, float y) - { - return (uint)( - ((int)Math.Round(x.Clamp(0, 1) * 65535F) & 0xFFFF) | - (((int)Math.Round(y.Clamp(0, 1) * 65535F) & 0xFFFF) << 16)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Vector4 ToByteScaledVector4() => this.ToVector4() * 255F; - } -} diff --git a/src/ImageSharp/PixelFormats/Rgb24.cs b/src/ImageSharp/PixelFormats/Rgb24.cs deleted file mode 100644 index d7e1c47ec0..0000000000 --- a/src/ImageSharp/PixelFormats/Rgb24.cs +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Pixel type containing three 8-bit unsigned normalized values ranging from 0 to 255. - /// The color components are stored in red, green, blue order (least significant to most significant byte). - /// - /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. - /// - /// - [StructLayout(LayoutKind.Explicit)] - public struct Rgb24 : IPixel - { - /// - /// The red component. - /// - [FieldOffset(0)] - public byte R; - - /// - /// The green component. - /// - [FieldOffset(1)] - public byte G; - - /// - /// The blue component. - /// - [FieldOffset(2)] - 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 is Rgb24 other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - { - int hash = HashHelpers.Combine(this.R.GetHashCode(), this.G.GetHashCode()); - return HashHelpers.Combine(hash, this.B.GetHashCode()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - this = Unsafe.As(ref source); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - this.R = source.R; - this.G = source.G; - this.B = source.B; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - return this.ToVector4(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - Rgba32 rgba = default; - rgba.PackFromVector4(vector); - this.PackFromRgba32(rgba); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Rgba32(this.R, this.G, this.B, byte.MaxValue).ToVector4(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) => dest = this; - - /// - public void ToRgba32(ref Rgba32 dest) - { - dest.Rgb = this; - dest.A = byte.MaxValue; - } - - /// - public void ToArgb32(ref Argb32 dest) - { - dest.R = this.R; - dest.G = this.G; - dest.B = this.B; - dest.A = byte.MaxValue; - } - - /// - 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 = byte.MaxValue; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgb48(Rgb48 source) - { - this.R = (byte)(((source.R * 255) + 32895) >> 16); - this.G = (byte)(((source.G * 255) + 32895) >> 16); - this.B = (byte)(((source.B * 255) + 32895) >> 16); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) - { - this.R = (byte)(((source.R * 255) + 32895) >> 16); - this.G = (byte)(((source.G * 255) + 32895) >> 16); - this.B = (byte)(((source.B * 255) + 32895) >> 16); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - public override string ToString() - { - return $"({this.R},{this.G},{this.B})"; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Rgb48.cs b/src/ImageSharp/PixelFormats/Rgb48.cs deleted file mode 100644 index 2d92b0e4e3..0000000000 --- a/src/ImageSharp/PixelFormats/Rgb48.cs +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed pixel type containing three 16-bit unsigned normalized values ranging from 0 to 635535. - /// - /// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form. - /// - /// - [StructLayout(LayoutKind.Sequential)] - public struct Rgb48 : IPixel - { - private const float Max = 65535F; - - /// - /// Gets or sets the red component. - /// - public ushort R; - - /// - /// Gets or sets the green component. - /// - public ushort G; - - /// - /// Gets or sets the blue component. - /// - public ushort B; - - /// - /// Initializes a new instance of the struct. - /// - /// The red component. - /// The green component. - /// The blue component. - public Rgb48(ushort r, ushort g, ushort b) - : this() - { - this.R = r; - this.G = g; - this.B = b; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The red component. - /// The green component. - /// The blue component. - public Rgb48(float r, float g, float b) - : this() - { - this.R = (ushort)MathF.Round(r.Clamp(0, 1) * Max); - this.G = (ushort)MathF.Round(g.Clamp(0, 1) * Max); - this.B = (ushort)MathF.Round(b.Clamp(0, 1) * Max); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the components values. - public Rgb48(Vector3 vector) - : this(vector.X, vector.Y, vector.Z) - { - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rgb48 left, Rgb48 right) - { - return left.R == right.R - && left.G == right.G - && left.B == right.B; - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rgb48 left, Rgb48 right) - { - return left.R != right.R - || left.G != right.G - || left.B != right.B; - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - return this.ToVector4(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4(this.R / Max, this.G / Max, this.B / Max, 1); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max; - this.R = (ushort)MathF.Round(vector.X); - this.G = (ushort)MathF.Round(vector.Y); - this.B = (ushort)MathF.Round(vector.Z); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this = source.Rgb; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - dest.R = (byte)(((this.R * 255) + 32895) >> 16); - dest.G = (byte)(((this.G * 255) + 32895) >> 16); - dest.B = (byte)(((this.B * 255) + 32895) >> 16); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - dest.R = (byte)(((this.R * 255) + 32895) >> 16); - dest.G = (byte)(((this.G * 255) + 32895) >> 16); - dest.B = (byte)(((this.B * 255) + 32895) >> 16); - dest.A = 255; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToArgb32(ref Argb32 dest) - { - dest.R = (byte)(((this.R * 255) + 32895) >> 16); - dest.G = (byte)(((this.G * 255) + 32895) >> 16); - dest.B = (byte)(((this.B * 255) + 32895) >> 16); - dest.A = 255; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgr24(ref Bgr24 dest) - { - dest.R = (byte)(((this.R * 255) + 32895) >> 16); - dest.G = (byte)(((this.G * 255) + 32895) >> 16); - dest.B = (byte)(((this.B * 255) + 32895) >> 16); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32(ref Bgra32 dest) - { - dest.R = (byte)(((this.R * 255) + 32895) >> 16); - dest.G = (byte)(((this.G * 255) + 32895) >> 16); - dest.B = (byte)(((this.B * 255) + 32895) >> 16); - dest.A = 255; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgb48(Rgb48 source) => this = source; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest = this; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) - { - dest.Rgb = this; - dest.A = ushort.MaxValue; - } - - /// - public override bool Equals(object obj) - { - return obj is Rgb48 rgb48 && this.Equals(rgb48); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rgb48 other) - { - return this.R == other.R - && this.G == other.G - && this.B == other.B; - } - - /// - public override string ToString() => this.ToVector4().ToString(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - { - return HashHelpers.Combine( - this.R.GetHashCode(), - HashHelpers.Combine(this.G.GetHashCode(), this.B.GetHashCode())); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Rgba1010102.cs b/src/ImageSharp/PixelFormats/Rgba1010102.cs deleted file mode 100644 index 94fb7a41e6..0000000000 --- a/src/ImageSharp/PixelFormats/Rgba1010102.cs +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed vector type containing unsigned normalized values ranging from 0 to 1. - /// The x, y and z components use 10 bits, and the w component uses 2 bits. - /// - /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. - /// - /// - public struct Rgba1010102 : IPixel, IPackedVector - { - /// - /// Initializes a new instance of the struct. - /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - public Rgba1010102(float x, float y, float z, float w) - { - this.PackedValue = Pack(x, y, z, w); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public Rgba1010102(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); - } - - /// - public uint PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rgba1010102 left, Rgba1010102 right) - { - return left.PackedValue == right.PackedValue; - } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rgba1010102 left, Rgba1010102 right) - { - return left.PackedValue != right.PackedValue; - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - return this.ToVector4(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4( - ((this.PackedValue >> 0) & 0x03FF) / 1023F, - ((this.PackedValue >> 10) & 0x03FF) / 1023F, - ((this.PackedValue >> 20) & 0x03FF) / 1023F, - ((this.PackedValue >> 30) & 0x03) / 3F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - Vector4 vector = this.ToVector4() * 255F; - 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 ToRgba32(ref Rgba32 dest) - { - Vector4 vector = this.ToVector4() * 255F; - 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 ToArgb32(ref Argb32 dest) - { - Vector4 vector = this.ToVector4() * 255F; - 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 ToBgr24(ref Bgr24 dest) - { - Vector4 vector = this.ToVector4() * 255F; - 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 ToBgra32(ref Bgra32 dest) - { - Vector4 vector = this.ToVector4() * 255F; - 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 PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - public override bool Equals(object obj) - { - return obj is Rgba1010102 other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rgba1010102 other) - { - return this.PackedValue == other.PackedValue; - } - - /// - public override string ToString() - { - return this.ToVector4().ToString(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - { - return this.PackedValue.GetHashCode(); - } - - /// - /// Packs the components into a . - /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - /// The containing the packed values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(float x, float y, float z, float w) - { - return (uint)( - (((int)Math.Round(x.Clamp(0, 1) * 1023F) & 0x03FF) << 0) | - (((int)Math.Round(y.Clamp(0, 1) * 1023F) & 0x03FF) << 10) | - (((int)Math.Round(z.Clamp(0, 1) * 1023F) & 0x03FF) << 20) | - (((int)Math.Round(w.Clamp(0, 1) * 3F) & 0x03) << 30)); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs b/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs deleted file mode 100644 index 2629ce3f79..0000000000 --- a/src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using SixLabors.Memory; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Provides optimized overrides for bulk operations. - /// - public partial struct Rgba32 - { - /// - /// implementation optimized for . - /// - internal partial class PixelOperations : PixelOperations - { - /// - /// SIMD optimized bulk implementation of - /// that works only with `count` divisible by . - /// - /// The to the source colors. - /// The to the dstination vectors. - /// The number of pixels to convert. - /// - /// Implementation adapted from: - /// - /// http://stackoverflow.com/a/5362789 - /// - /// TODO: We can replace this implementation in the future using new Vector API-s: - /// - /// https://github.com/dotnet/corefx/issues/15957 - /// - /// - internal static void ToVector4SimdAligned(ReadOnlySpan sourceColors, Span destVectors, int count) - { - if (!Vector.IsHardwareAccelerated) - { - throw new InvalidOperationException( - "Rgba32.PixelOperations.ToVector4SimdAligned() should not be called when Vector.IsHardwareAccelerated == false!"); - } - - DebugGuard.IsTrue( - count % Vector.Count == 0, - nameof(count), - "Argument 'count' should divisible by Vector.Count!"); - - var bVec = new Vector(256.0f / 255.0f); - var magicFloat = new Vector(32768.0f); - var magicInt = new Vector(1191182336); // reinterpreded value of 32768.0f - var mask = new Vector(255); - - int unpackedRawCount = count * 4; - - ref uint sourceBase = ref Unsafe.As(ref MemoryMarshal.GetReference(sourceColors)); - ref UnpackedRGBA destBaseAsUnpacked = ref Unsafe.As(ref MemoryMarshal.GetReference(destVectors)); - ref Vector destBaseAsUInt = ref Unsafe.As>(ref destBaseAsUnpacked); - ref Vector destBaseAsFloat = ref Unsafe.As>(ref destBaseAsUnpacked); - - for (int i = 0; i < count; i++) - { - uint sVal = Unsafe.Add(ref sourceBase, i); - ref UnpackedRGBA dst = ref Unsafe.Add(ref destBaseAsUnpacked, i); - - // This call is the bottleneck now: - dst.Load(sVal); - } - - int numOfVectors = unpackedRawCount / Vector.Count; - - for (int i = 0; i < numOfVectors; i++) - { - Vector vi = Unsafe.Add(ref destBaseAsUInt, i); - - vi &= mask; - vi |= magicInt; - - var vf = Vector.AsVectorSingle(vi); - vf = (vf - magicFloat) * bVec; - - Unsafe.Add(ref destBaseAsFloat, i) = vf; - } - } - - /// - internal override void ToVector4(ReadOnlySpan sourceColors, Span destinationVectors, int count) - { - Guard.MustBeSizedAtLeast(sourceColors, count, nameof(sourceColors)); - Guard.MustBeSizedAtLeast(destinationVectors, count, nameof(destinationVectors)); - - if (count < 256 || !Vector.IsHardwareAccelerated) - { - // Doesn't worth to bother with SIMD: - base.ToVector4(sourceColors, destinationVectors, count); - return; - } - - int remainder = count % Vector.Count; - int alignedCount = count - remainder; - - if (alignedCount > 0) - { - ToVector4SimdAligned(sourceColors, destinationVectors, alignedCount); - } - - if (remainder > 0) - { - sourceColors = sourceColors.Slice(alignedCount); - destinationVectors = destinationVectors.Slice(alignedCount); - base.ToVector4(sourceColors, destinationVectors, remainder); - } - } - - /// - internal override void PackFromVector4(ReadOnlySpan sourceVectors, Span destinationColors, int count) - { - GuardSpans(sourceVectors, nameof(sourceVectors), destinationColors, nameof(destinationColors), count); - - if (!SimdUtils.IsAvx2CompatibleArchitecture) - { - base.PackFromVector4(sourceVectors, destinationColors, count); - return; - } - - int remainder = count % 2; - int alignedCount = count - remainder; - - if (alignedCount > 0) - { - ReadOnlySpan flatSrc = MemoryMarshal.Cast(sourceVectors.Slice(0, alignedCount)); - Span flatDest = MemoryMarshal.Cast(destinationColors); - - SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows(flatSrc, flatDest); - } - - if (remainder > 0) - { - // actually: remainder == 1 - int lastIdx = count - 1; - destinationColors[lastIdx].PackFromVector4(sourceVectors[lastIdx]); - } - } - - /// - internal override void ToScaledVector4(ReadOnlySpan sourceColors, Span destinationVectors, int count) - { - this.ToVector4(sourceColors, destinationVectors, count); - } - - /// - internal override void PackFromScaledVector4(ReadOnlySpan sourceVectors, Span destinationColors, int count) - { - this.PackFromVector4(sourceVectors, destinationColors, count); - } - - /// - internal override void PackFromRgba32(ReadOnlySpan source, Span destPixels, int count) - { - GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count); - - source.Slice(0, count).CopyTo(destPixels); - } - - /// - internal override void ToRgba32(ReadOnlySpan sourcePixels, Span dest, int count) - { - GuardSpans(sourcePixels, nameof(sourcePixels), dest, nameof(dest), count); - - sourcePixels.Slice(0, count).CopyTo(dest); - } - - /// - /// Value type to store -s unpacked into multiple -s. - /// - [StructLayout(LayoutKind.Sequential)] - private struct UnpackedRGBA - { - private uint r; - - private uint g; - - private uint b; - - private uint a; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Load(uint p) - { - this.r = p; - this.g = p >> GreenShift; - this.b = p >> BlueShift; - this.a = p >> AlphaShift; - } - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Rgba64.cs b/src/ImageSharp/PixelFormats/Rgba64.cs deleted file mode 100644 index 8e6be1e8c4..0000000000 --- a/src/ImageSharp/PixelFormats/Rgba64.cs +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed pixel type containing four 16-bit unsigned normalized values ranging from 0 to 635535. - /// - /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. - /// - /// - [StructLayout(LayoutKind.Sequential)] - public struct Rgba64 : IPixel, IPackedVector - { - private const float Max = 65535F; - - /// - /// Gets or sets the red component. - /// - public ushort R; - - /// - /// Gets or sets the green component. - /// - public ushort G; - - /// - /// Gets or sets the blue component. - /// - public ushort B; - - /// - /// Gets or sets the alpha component. - /// - public ushort A; - - /// - /// Initializes a new instance of the struct. - /// - /// The red component. - /// The green component. - /// The blue component. - /// The alpha component. - public Rgba64(ushort r, ushort g, ushort b, ushort a) - : this() - { - this.R = r; - this.G = g; - this.B = b; - this.A = a; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The red component. - /// The green component. - /// The blue component. - /// The alpha component. - public Rgba64(float r, float g, float b, float a) - : this() - { - this.R = (ushort)MathF.Round(r.Clamp(0, 1) * Max); - this.G = (ushort)MathF.Round(g.Clamp(0, 1) * Max); - this.B = (ushort)MathF.Round(b.Clamp(0, 1) * Max); - this.A = (ushort)MathF.Round(a.Clamp(0, 1) * Max); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the components values. - public Rgba64(Vector4 vector) - : this(vector.X, vector.Y, vector.Z, vector.W) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The packed value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Rgba64(ulong packed) - : this() - { - this.PackedValue = packed; - } - - /// - /// Gets or sets the RGB components of this struct as - /// - public Rgb48 Rgb - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Unsafe.As(ref this); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set => Unsafe.As(ref this) = value; - } - - /// - public ulong PackedValue - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Unsafe.As(ref this); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set => Unsafe.As(ref this) = value; - } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Rgba64 left, Rgba64 right) - { - return left.PackedValue == right.PackedValue; - } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Rgba64 left, Rgba64 right) - { - return left.PackedValue != right.PackedValue; - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - return this.ToVector4(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4(this.R, this.G, this.B, this.A) / Max; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - vector = Vector4.Clamp(vector, Vector4.Zero, Vector4.One) * Max; - this.R = (ushort)MathF.Round(vector.X); - this.G = (ushort)MathF.Round(vector.Y); - this.B = (ushort)MathF.Round(vector.Z); - this.A = (ushort)MathF.Round(vector.W); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - this.PackFromVector4(source.ToVector4()); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this = source; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - dest.R = (byte)(((this.R * 255) + 32895) >> 16); - dest.G = (byte)(((this.G * 255) + 32895) >> 16); - dest.B = (byte)(((this.B * 255) + 32895) >> 16); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - // Taken from libpng pngtran.c line: 2419 - dest.R = (byte)(((this.R * 255) + 32895) >> 16); - dest.G = (byte)(((this.G * 255) + 32895) >> 16); - dest.B = (byte)(((this.B * 255) + 32895) >> 16); - dest.A = (byte)(((this.A * 255) + 32895) >> 16); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgb48(Rgb48 source) - { - this.Rgb = source; - this.A = ushort.MaxValue; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest = this.Rgb; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest = this; - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToArgb32(ref Argb32 dest) - { - dest.R = (byte)(((this.R * 255) + 32895) >> 16); - dest.G = (byte)(((this.G * 255) + 32895) >> 16); - dest.B = (byte)(((this.B * 255) + 32895) >> 16); - dest.A = (byte)(((this.A * 255) + 32895) >> 16); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgr24(ref Bgr24 dest) - { - dest.R = (byte)(((this.R * 255) + 32895) >> 16); - dest.G = (byte)(((this.G * 255) + 32895) >> 16); - dest.B = (byte)(((this.B * 255) + 32895) >> 16); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32(ref Bgra32 dest) - { - dest.R = (byte)(((this.R * 255) + 32895) >> 16); - dest.G = (byte)(((this.G * 255) + 32895) >> 16); - dest.B = (byte)(((this.B * 255) + 32895) >> 16); - dest.A = (byte)(((this.A * 255) + 32895) >> 16); - } - - /// - public override bool Equals(object obj) - { - return obj is Rgba64 rgba64 && this.Equals(rgba64); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rgba64 other) - { - return this.PackedValue == other.PackedValue; - } - - /// - public override string ToString() - { - return $"({this.R},{this.G},{this.B},{this.A})"; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/RgbaVector.Definitions.cs b/src/ImageSharp/PixelFormats/RgbaVector.Definitions.cs deleted file mode 100644 index 2ef37c43ae..0000000000 --- a/src/ImageSharp/PixelFormats/RgbaVector.Definitions.cs +++ /dev/null @@ -1,721 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Provides operators and composition algorithms. - /// - public partial struct RgbaVector - { - /// - /// Represents a matching the W3C definition that has an hex value of #F0F8FF. - /// - public static readonly RgbaVector AliceBlue = NamedColors.AliceBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #FAEBD7. - /// - public static readonly RgbaVector AntiqueWhite = NamedColors.AntiqueWhite; - - /// - /// Represents a matching the W3C definition that has an hex value of #00FFFF. - /// - public static readonly RgbaVector Aqua = NamedColors.Aqua; - - /// - /// Represents a matching the W3C definition that has an hex value of #7FFFD4. - /// - public static readonly RgbaVector Aquamarine = NamedColors.Aquamarine; - - /// - /// Represents a matching the W3C definition that has an hex value of #F0FFFF. - /// - public static readonly RgbaVector Azure = NamedColors.Azure; - - /// - /// Represents a matching the W3C definition that has an hex value of #F5F5DC. - /// - public static readonly RgbaVector Beige = NamedColors.Beige; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFE4C4. - /// - public static readonly RgbaVector Bisque = NamedColors.Bisque; - - /// - /// Represents a matching the W3C definition that has an hex value of #000000. - /// - public static readonly RgbaVector Black = NamedColors.Black; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFEBCD. - /// - public static readonly RgbaVector BlanchedAlmond = NamedColors.BlanchedAlmond; - - /// - /// Represents a matching the W3C definition that has an hex value of #0000FF. - /// - public static readonly RgbaVector Blue = NamedColors.Blue; - - /// - /// Represents a matching the W3C definition that has an hex value of #8A2BE2. - /// - public static readonly RgbaVector BlueViolet = NamedColors.BlueViolet; - - /// - /// Represents a matching the W3C definition that has an hex value of #A52A2A. - /// - public static readonly RgbaVector Brown = NamedColors.Brown; - - /// - /// Represents a matching the W3C definition that has an hex value of #DEB887. - /// - public static readonly RgbaVector BurlyWood = NamedColors.BurlyWood; - - /// - /// Represents a matching the W3C definition that has an hex value of #5F9EA0. - /// - public static readonly RgbaVector CadetBlue = NamedColors.CadetBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #7FFF00. - /// - public static readonly RgbaVector Chartreuse = NamedColors.Chartreuse; - - /// - /// Represents a matching the W3C definition that has an hex value of #D2691E. - /// - public static readonly RgbaVector Chocolate = NamedColors.Chocolate; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF7F50. - /// - public static readonly RgbaVector Coral = NamedColors.Coral; - - /// - /// Represents a matching the W3C definition that has an hex value of #6495ED. - /// - public static readonly RgbaVector CornflowerBlue = NamedColors.CornflowerBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFF8DC. - /// - public static readonly RgbaVector Cornsilk = NamedColors.Cornsilk; - - /// - /// Represents a matching the W3C definition that has an hex value of #DC143C. - /// - public static readonly RgbaVector Crimson = NamedColors.Crimson; - - /// - /// Represents a matching the W3C definition that has an hex value of #00FFFF. - /// - public static readonly RgbaVector Cyan = NamedColors.Cyan; - - /// - /// Represents a matching the W3C definition that has an hex value of #00008B. - /// - public static readonly RgbaVector DarkBlue = NamedColors.DarkBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #008B8B. - /// - public static readonly RgbaVector DarkCyan = NamedColors.DarkCyan; - - /// - /// Represents a matching the W3C definition that has an hex value of #B8860B. - /// - public static readonly RgbaVector DarkGoldenrod = NamedColors.DarkGoldenrod; - - /// - /// Represents a matching the W3C definition that has an hex value of #A9A9A9. - /// - public static readonly RgbaVector DarkGray = NamedColors.DarkGray; - - /// - /// Represents a matching the W3C definition that has an hex value of #006400. - /// - public static readonly RgbaVector DarkGreen = NamedColors.DarkGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #BDB76B. - /// - public static readonly RgbaVector DarkKhaki = NamedColors.DarkKhaki; - - /// - /// Represents a matching the W3C definition that has an hex value of #8B008B. - /// - public static readonly RgbaVector DarkMagenta = NamedColors.DarkMagenta; - - /// - /// Represents a matching the W3C definition that has an hex value of #556B2F. - /// - public static readonly RgbaVector DarkOliveGreen = NamedColors.DarkOliveGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF8C00. - /// - public static readonly RgbaVector DarkOrange = NamedColors.DarkOrange; - - /// - /// Represents a matching the W3C definition that has an hex value of #9932CC. - /// - public static readonly RgbaVector DarkOrchid = NamedColors.DarkOrchid; - - /// - /// Represents a matching the W3C definition that has an hex value of #8B0000. - /// - public static readonly RgbaVector DarkRed = NamedColors.DarkRed; - - /// - /// Represents a matching the W3C definition that has an hex value of #E9967A. - /// - public static readonly RgbaVector DarkSalmon = NamedColors.DarkSalmon; - - /// - /// Represents a matching the W3C definition that has an hex value of #8FBC8B. - /// - public static readonly RgbaVector DarkSeaGreen = NamedColors.DarkSeaGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #483D8B. - /// - public static readonly RgbaVector DarkSlateBlue = NamedColors.DarkSlateBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #2F4F4F. - /// - public static readonly RgbaVector DarkSlateGray = NamedColors.DarkSlateGray; - - /// - /// Represents a matching the W3C definition that has an hex value of #00CED1. - /// - public static readonly RgbaVector DarkTurquoise = NamedColors.DarkTurquoise; - - /// - /// Represents a matching the W3C definition that has an hex value of #9400D3. - /// - public static readonly RgbaVector DarkViolet = NamedColors.DarkViolet; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF1493. - /// - public static readonly RgbaVector DeepPink = NamedColors.DeepPink; - - /// - /// Represents a matching the W3C definition that has an hex value of #00BFFF. - /// - public static readonly RgbaVector DeepSkyBlue = NamedColors.DeepSkyBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #696969. - /// - public static readonly RgbaVector DimGray = NamedColors.DimGray; - - /// - /// Represents a matching the W3C definition that has an hex value of #1E90FF. - /// - public static readonly RgbaVector DodgerBlue = NamedColors.DodgerBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #B22222. - /// - public static readonly RgbaVector Firebrick = NamedColors.Firebrick; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFAF0. - /// - public static readonly RgbaVector FloralWhite = NamedColors.FloralWhite; - - /// - /// Represents a matching the W3C definition that has an hex value of #228B22. - /// - public static readonly RgbaVector ForestGreen = NamedColors.ForestGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF00FF. - /// - public static readonly RgbaVector Fuchsia = NamedColors.Fuchsia; - - /// - /// Represents a matching the W3C definition that has an hex value of #DCDCDC. - /// - public static readonly RgbaVector Gainsboro = NamedColors.Gainsboro; - - /// - /// Represents a matching the W3C definition that has an hex value of #F8F8FF. - /// - public static readonly RgbaVector GhostWhite = NamedColors.GhostWhite; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFD700. - /// - public static readonly RgbaVector Gold = NamedColors.Gold; - - /// - /// Represents a matching the W3C definition that has an hex value of #DAA520. - /// - public static readonly RgbaVector Goldenrod = NamedColors.Goldenrod; - - /// - /// Represents a matching the W3C definition that has an hex value of #808080. - /// - public static readonly RgbaVector Gray = NamedColors.Gray; - - /// - /// Represents a matching the W3C definition that has an hex value of #008000. - /// - public static readonly RgbaVector Green = NamedColors.Green; - - /// - /// Represents a matching the W3C definition that has an hex value of #ADFF2F. - /// - public static readonly RgbaVector GreenYellow = NamedColors.GreenYellow; - - /// - /// Represents a matching the W3C definition that has an hex value of #F0FFF0. - /// - public static readonly RgbaVector Honeydew = NamedColors.Honeydew; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF69B4. - /// - public static readonly RgbaVector HotPink = NamedColors.HotPink; - - /// - /// Represents a matching the W3C definition that has an hex value of #CD5C5C. - /// - public static readonly RgbaVector IndianRed = NamedColors.IndianRed; - - /// - /// Represents a matching the W3C definition that has an hex value of #4B0082. - /// - public static readonly RgbaVector Indigo = NamedColors.Indigo; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFFF0. - /// - public static readonly RgbaVector Ivory = NamedColors.Ivory; - - /// - /// Represents a matching the W3C definition that has an hex value of #F0E68C. - /// - public static readonly RgbaVector Khaki = NamedColors.Khaki; - - /// - /// Represents a matching the W3C definition that has an hex value of #E6E6FA. - /// - public static readonly RgbaVector Lavender = NamedColors.Lavender; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFF0F5. - /// - public static readonly RgbaVector LavenderBlush = NamedColors.LavenderBlush; - - /// - /// Represents a matching the W3C definition that has an hex value of #7CFC00. - /// - public static readonly RgbaVector LawnGreen = NamedColors.LawnGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFACD. - /// - public static readonly RgbaVector LemonChiffon = NamedColors.LemonChiffon; - - /// - /// Represents a matching the W3C definition that has an hex value of #ADD8E6. - /// - public static readonly RgbaVector LightBlue = NamedColors.LightBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #F08080. - /// - public static readonly RgbaVector LightCoral = NamedColors.LightCoral; - - /// - /// Represents a matching the W3C definition that has an hex value of #E0FFFF. - /// - public static readonly RgbaVector LightCyan = NamedColors.LightCyan; - - /// - /// Represents a matching the W3C definition that has an hex value of #FAFAD2. - /// - public static readonly RgbaVector LightGoldenrodYellow = NamedColors.LightGoldenrodYellow; - - /// - /// Represents a matching the W3C definition that has an hex value of #D3D3D3. - /// - public static readonly RgbaVector LightGray = NamedColors.LightGray; - - /// - /// Represents a matching the W3C definition that has an hex value of #90EE90. - /// - public static readonly RgbaVector LightGreen = NamedColors.LightGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFB6C1. - /// - public static readonly RgbaVector LightPink = NamedColors.LightPink; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFA07A. - /// - public static readonly RgbaVector LightSalmon = NamedColors.LightSalmon; - - /// - /// Represents a matching the W3C definition that has an hex value of #20B2AA. - /// - public static readonly RgbaVector LightSeaGreen = NamedColors.LightSeaGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #87CEFA. - /// - public static readonly RgbaVector LightSkyBlue = NamedColors.LightSkyBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #778899. - /// - public static readonly RgbaVector LightSlateGray = NamedColors.LightSlateGray; - - /// - /// Represents a matching the W3C definition that has an hex value of #B0C4DE. - /// - public static readonly RgbaVector LightSteelBlue = NamedColors.LightSteelBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFFE0. - /// - public static readonly RgbaVector LightYellow = NamedColors.LightYellow; - - /// - /// Represents a matching the W3C definition that has an hex value of #00FF00. - /// - public static readonly RgbaVector Lime = NamedColors.Lime; - - /// - /// Represents a matching the W3C definition that has an hex value of #32CD32. - /// - public static readonly RgbaVector LimeGreen = NamedColors.LimeGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #FAF0E6. - /// - public static readonly RgbaVector Linen = NamedColors.Linen; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF00FF. - /// - public static readonly RgbaVector Magenta = NamedColors.Magenta; - - /// - /// Represents a matching the W3C definition that has an hex value of #800000. - /// - public static readonly RgbaVector Maroon = NamedColors.Maroon; - - /// - /// Represents a matching the W3C definition that has an hex value of #66CDAA. - /// - public static readonly RgbaVector MediumAquamarine = NamedColors.MediumAquamarine; - - /// - /// Represents a matching the W3C definition that has an hex value of #0000CD. - /// - public static readonly RgbaVector MediumBlue = NamedColors.MediumBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #BA55D3. - /// - public static readonly RgbaVector MediumOrchid = NamedColors.MediumOrchid; - - /// - /// Represents a matching the W3C definition that has an hex value of #9370DB. - /// - public static readonly RgbaVector MediumPurple = NamedColors.MediumPurple; - - /// - /// Represents a matching the W3C definition that has an hex value of #3CB371. - /// - public static readonly RgbaVector MediumSeaGreen = NamedColors.MediumSeaGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #7B68EE. - /// - public static readonly RgbaVector MediumSlateBlue = NamedColors.MediumSlateBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #00FA9A. - /// - public static readonly RgbaVector MediumSpringGreen = NamedColors.MediumSpringGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #48D1CC. - /// - public static readonly RgbaVector MediumTurquoise = NamedColors.MediumTurquoise; - - /// - /// Represents a matching the W3C definition that has an hex value of #C71585. - /// - public static readonly RgbaVector MediumVioletRed = NamedColors.MediumVioletRed; - - /// - /// Represents a matching the W3C definition that has an hex value of #191970. - /// - public static readonly RgbaVector MidnightBlue = NamedColors.MidnightBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #F5FFFA. - /// - public static readonly RgbaVector MintCream = NamedColors.MintCream; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFE4E1. - /// - public static readonly RgbaVector MistyRose = NamedColors.MistyRose; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFE4B5. - /// - public static readonly RgbaVector Moccasin = NamedColors.Moccasin; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFDEAD. - /// - public static readonly RgbaVector NavajoWhite = NamedColors.NavajoWhite; - - /// - /// Represents a matching the W3C definition that has an hex value of #000080. - /// - public static readonly RgbaVector Navy = NamedColors.Navy; - - /// - /// Represents a matching the W3C definition that has an hex value of #FDF5E6. - /// - public static readonly RgbaVector OldLace = NamedColors.OldLace; - - /// - /// Represents a matching the W3C definition that has an hex value of #808000. - /// - public static readonly RgbaVector Olive = NamedColors.Olive; - - /// - /// Represents a matching the W3C definition that has an hex value of #6B8E23. - /// - public static readonly RgbaVector OliveDrab = NamedColors.OliveDrab; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFA500. - /// - public static readonly RgbaVector Orange = NamedColors.Orange; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF4500. - /// - public static readonly RgbaVector OrangeRed = NamedColors.OrangeRed; - - /// - /// Represents a matching the W3C definition that has an hex value of #DA70D6. - /// - public static readonly RgbaVector Orchid = NamedColors.Orchid; - - /// - /// Represents a matching the W3C definition that has an hex value of #EEE8AA. - /// - public static readonly RgbaVector PaleGoldenrod = NamedColors.PaleGoldenrod; - - /// - /// Represents a matching the W3C definition that has an hex value of #98FB98. - /// - public static readonly RgbaVector PaleGreen = NamedColors.PaleGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #AFEEEE. - /// - public static readonly RgbaVector PaleTurquoise = NamedColors.PaleTurquoise; - - /// - /// Represents a matching the W3C definition that has an hex value of #DB7093. - /// - public static readonly RgbaVector PaleVioletRed = NamedColors.PaleVioletRed; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFEFD5. - /// - public static readonly RgbaVector PapayaWhip = NamedColors.PapayaWhip; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFDAB9. - /// - public static readonly RgbaVector PeachPuff = NamedColors.PeachPuff; - - /// - /// Represents a matching the W3C definition that has an hex value of #CD853F. - /// - public static readonly RgbaVector Peru = NamedColors.Peru; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFC0CB. - /// - public static readonly RgbaVector Pink = NamedColors.Pink; - - /// - /// Represents a matching the W3C definition that has an hex value of #DDA0DD. - /// - public static readonly RgbaVector Plum = NamedColors.Plum; - - /// - /// Represents a matching the W3C definition that has an hex value of #B0E0E6. - /// - public static readonly RgbaVector PowderBlue = NamedColors.PowderBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #800080. - /// - public static readonly RgbaVector Purple = NamedColors.Purple; - - /// - /// Represents a matching the W3C definition that has an hex value of #663399. - /// - public static readonly RgbaVector RebeccaPurple = NamedColors.RebeccaPurple; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF0000. - /// - public static readonly RgbaVector Red = NamedColors.Red; - - /// - /// Represents a matching the W3C definition that has an hex value of #BC8F8F. - /// - public static readonly RgbaVector RosyBrown = NamedColors.RosyBrown; - - /// - /// Represents a matching the W3C definition that has an hex value of #4169E1. - /// - public static readonly RgbaVector RoyalBlue = NamedColors.RoyalBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #8B4513. - /// - public static readonly RgbaVector SaddleBrown = NamedColors.SaddleBrown; - - /// - /// Represents a matching the W3C definition that has an hex value of #FA8072. - /// - public static readonly RgbaVector Salmon = NamedColors.Salmon; - - /// - /// Represents a matching the W3C definition that has an hex value of #F4A460. - /// - public static readonly RgbaVector SandyBrown = NamedColors.SandyBrown; - - /// - /// Represents a matching the W3C definition that has an hex value of #2E8B57. - /// - public static readonly RgbaVector SeaGreen = NamedColors.SeaGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFF5EE. - /// - public static readonly RgbaVector SeaShell = NamedColors.SeaShell; - - /// - /// Represents a matching the W3C definition that has an hex value of #A0522D. - /// - public static readonly RgbaVector Sienna = NamedColors.Sienna; - - /// - /// Represents a matching the W3C definition that has an hex value of #C0C0C0. - /// - public static readonly RgbaVector Silver = NamedColors.Silver; - - /// - /// Represents a matching the W3C definition that has an hex value of #87CEEB. - /// - public static readonly RgbaVector SkyBlue = NamedColors.SkyBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #6A5ACD. - /// - public static readonly RgbaVector SlateBlue = NamedColors.SlateBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #708090. - /// - public static readonly RgbaVector SlateGray = NamedColors.SlateGray; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFAFA. - /// - public static readonly RgbaVector Snow = NamedColors.Snow; - - /// - /// Represents a matching the W3C definition that has an hex value of #00FF7F. - /// - public static readonly RgbaVector SpringGreen = NamedColors.SpringGreen; - - /// - /// Represents a matching the W3C definition that has an hex value of #4682B4. - /// - public static readonly RgbaVector SteelBlue = NamedColors.SteelBlue; - - /// - /// Represents a matching the W3C definition that has an hex value of #D2B48C. - /// - public static readonly RgbaVector Tan = NamedColors.Tan; - - /// - /// Represents a matching the W3C definition that has an hex value of #008080. - /// - public static readonly RgbaVector Teal = NamedColors.Teal; - - /// - /// Represents a matching the W3C definition that has an hex value of #D8BFD8. - /// - public static readonly RgbaVector Thistle = NamedColors.Thistle; - - /// - /// Represents a matching the W3C definition that has an hex value of #FF6347. - /// - public static readonly RgbaVector Tomato = NamedColors.Tomato; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFFFF. - /// - public static readonly RgbaVector Transparent = NamedColors.Transparent; - - /// - /// Represents a matching the W3C definition that has an hex value of #40E0D0. - /// - public static readonly RgbaVector Turquoise = NamedColors.Turquoise; - - /// - /// Represents a matching the W3C definition that has an hex value of #EE82EE. - /// - public static readonly RgbaVector Violet = NamedColors.Violet; - - /// - /// Represents a matching the W3C definition that has an hex value of #F5DEB3. - /// - public static readonly RgbaVector Wheat = NamedColors.Wheat; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFFFF. - /// - public static readonly RgbaVector White = NamedColors.White; - - /// - /// Represents a matching the W3C definition that has an hex value of #F5F5F5. - /// - public static readonly RgbaVector WhiteSmoke = NamedColors.WhiteSmoke; - - /// - /// Represents a matching the W3C definition that has an hex value of #FFFF00. - /// - public static readonly RgbaVector Yellow = NamedColors.Yellow; - - /// - /// Represents a matching the W3C definition that has an hex value of #9ACD32. - /// - public static readonly RgbaVector YellowGreen = NamedColors.YellowGreen; - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs b/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs deleted file mode 100644 index ce40665cd4..0000000000 --- a/src/ImageSharp/PixelFormats/RgbaVector.PixelOperations.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Provides optimized overrides for bulk operations. - /// - public partial struct RgbaVector - { - /// - /// implementation optimized for . - /// - internal class PixelOperations : PixelOperations - { - /// - internal override unsafe void ToVector4(ReadOnlySpan sourceColors, Span destVectors, int count) - { - GuardSpans(sourceColors, nameof(sourceColors), destVectors, nameof(destVectors), count); - - MemoryMarshal.Cast(sourceColors).Slice(0, count).CopyTo(destVectors); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/RgbaVector.cs b/src/ImageSharp/PixelFormats/RgbaVector.cs deleted file mode 100644 index dd5f77b80f..0000000000 --- a/src/ImageSharp/PixelFormats/RgbaVector.cs +++ /dev/null @@ -1,376 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Unpacked pixel type containing four 16-bit floating-point values typically ranging from 0 to 1. - /// The color components are stored in red, green, blue, and alpha order. - /// - /// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form. - /// - /// - /// - /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, - /// as it avoids the need to create new values for modification operations. - /// - public partial struct RgbaVector : IPixel - { - /// - /// The maximum byte value. - /// - private static readonly Vector4 MaxBytes = new Vector4(255); - - /// - /// The half vector value. - /// - private static readonly Vector4 Half = new Vector4(0.5F); - - /// - /// The backing vector for SIMD support. - /// - private Vector4 backingVector; - - /// - /// Initializes a new instance of the struct. - /// - /// The red component. - /// The green component. - /// The blue component. - /// The alpha component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public RgbaVector(byte r, byte g, byte b, byte a = 255) - : this() - { - this.backingVector = new Vector4(r, g, b, a) / MaxBytes; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The red component. - /// The green component. - /// The blue component. - /// The alpha component. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public RgbaVector(float r, float g, float b, float a = 1) - : this() - { - this.backingVector = new Vector4(r, g, b, a); - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The vector containing the components for the packed vector. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public RgbaVector(Vector3 vector) - : this() - { - this.backingVector = new Vector4(vector, 1); - } - - /// - /// Initializes a new instance of the struct. - /// - /// - /// The vector containing the components for the packed vector. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public RgbaVector(Vector4 vector) - : this() - { - this.backingVector = vector; - } - - /// - /// Gets or sets the red component. - /// - public float R - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return this.backingVector.X; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.backingVector.X = value; - } - } - - /// - /// Gets or sets the green component. - /// - public float G - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return this.backingVector.Y; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.backingVector.Y = value; - } - } - - /// - /// Gets or sets the blue component. - /// - public float B - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return this.backingVector.Z; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.backingVector.Z = value; - } - } - - /// - /// Gets or sets the alpha component. - /// - public float A - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return this.backingVector.W; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - this.backingVector.W = value; - } - } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(RgbaVector left, RgbaVector right) - { - return left.backingVector == right.backingVector; - } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(RgbaVector left, RgbaVector right) - { - return left.backingVector != right.backingVector; - } - - /// - /// Creates a new instance of the struct. - /// - /// - /// The hexadecimal representation of the combined color components arranged - /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax. - /// - /// - /// The . - /// - public static RgbaVector FromHex(string hex) - { - return ColorBuilder.FromHex(hex); - } - - /// - public PixelOperations CreatePixelOperations() => new RgbaVector.PixelOperations(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - this.backingVector = source.ToVector4(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - this.backingVector = source.ToVector4(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - this.backingVector = source.ToVector4(); - } - - /// - /// Converts the value of this instance to a hexadecimal string. - /// - /// A hexadecimal string representation of the value. - public string ToHex() - { - // Hex is RRGGBBAA - Vector4 vector = this.backingVector * MaxBytes; - vector += Half; - uint hexOrder = (uint)((byte)vector.W | (byte)vector.Z << 8 | (byte)vector.Y << 16 | (byte)vector.X << 24); - return hexOrder.ToString("X8"); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToRgba32(ref Rgba32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToArgb32(ref Argb32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToBgr24(ref Bgr24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToBgra32(ref Bgra32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - return this.ToVector4(); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.backingVector = vector; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return this.backingVector; - } - - /// - public override bool Equals(object obj) - { - return obj is RgbaVector other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(RgbaVector other) - { - return this.backingVector == other.backingVector; - } - - /// - /// Gets a string representation of the packed vector. - /// - /// A string representation of the packed vector. - public override string ToString() - { - return this.ToVector4().ToString(); - } - - /// - public override int GetHashCode() - { - return this.backingVector.GetHashCode(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Vector4 ToByteScaledVector4() => 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 deleted file mode 100644 index 9fc7618b91..0000000000 --- a/src/ImageSharp/PixelFormats/Short2.cs +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed pixel type containing two 16-bit signed integer values. - /// - /// Ranges from [-32767, -32767, 0, 1] to [32767, 32767, 0, 1] in vector form. - /// - /// - public struct Short2 : IPixel, IPackedVector - { - /// - /// The maximum byte value. - /// - private static readonly Vector2 MaxBytes = new Vector2(255); - - /// - /// The half the maximum byte value. - /// - private static readonly Vector2 Half = new Vector2(127); - - /// - /// The vector value used for rounding. - /// - private static readonly Vector2 Round = new Vector2(.5F); - - /// - /// Initializes a new instance of the struct. - /// - /// The vector containing the component values. - public Short2(Vector2 vector) - { - this.PackedValue = Pack(vector.X, vector.Y); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The x-component. - /// The y-component. - public Short2(float x, float y) - { - this.PackedValue = Pack(x, y); - } - - /// - public uint PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Short2 left, Short2 right) - { - return left.PackedValue == right.PackedValue; - } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Short2 left, Short2 right) - { - return left.PackedValue != right.PackedValue; - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - Vector2 scaled = new Vector2(vector.X, vector.Y) * 65534F; - scaled -= new Vector2(32767F); - this.PackedValue = Pack(scaled.X, scaled.Y); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - var scaled = this.ToVector2(); - scaled += new Vector2(32767F); - scaled /= 65534F; - return new Vector4(scaled, 0F, 1F); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10), 0, 1); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - Vector2 vector = new Vector2(source.R, source.G) / 255; - vector *= 65534; - vector -= new Vector2(32767); - this.PackedValue = Pack(vector.X, vector.Y); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - Vector2 vector = new Vector2(source.R, source.G) / 255; - vector *= 65534; - vector -= new Vector2(32767); - this.PackedValue = Pack(vector.X, vector.Y); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - Vector2 vector = new Vector2(source.R, source.G) / 255; - vector *= 65534; - vector -= new Vector2(32767); - this.PackedValue = Pack(vector.X, vector.Y); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - Vector2 vector = this.ToByteScaledVector2(); - dest.R = (byte)MathF.Round(vector.X); - dest.G = (byte)MathF.Round(vector.Y); - dest.B = 0; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba32(ref Rgba32 dest) - { - Vector2 vector = this.ToByteScaledVector2(); - 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 ToArgb32(ref Argb32 dest) - { - Vector2 vector = this.ToByteScaledVector2(); - 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 ToBgr24(ref Bgr24 dest) - { - Vector2 vector = this.ToByteScaledVector2(); - dest.R = (byte)MathF.Round(vector.X); - dest.G = (byte)MathF.Round(vector.Y); - dest.B = 0; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToBgra32(ref Bgra32 dest) - { - Vector2 vector = this.ToByteScaledVector2(); - 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 PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - /// Expands the packed representation into a . - /// The vector components are typically expanded in least to greatest significance order. - /// - /// The . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector2 ToVector2() - { - return new Vector2((short)(this.PackedValue & 0xFFFF), (short)(this.PackedValue >> 0x10)); - } - - /// - public override bool Equals(object obj) - { - return obj is Short2 other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Short2 other) - { - return this == other; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - /// - public override string ToString() - { - return this.PackedValue.ToString("x8"); - } - - /// - /// Packs the components into a . - /// - /// The x-component - /// The y-component - /// The containing the packed values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static uint Pack(float x, float y) - { - // Largest two byte positive number 0xFFFF >> 1; - const float MaxPos = 0x7FFF; - const float MinNeg = ~(int)MaxPos; - - // Clamp the value between min and max values - uint word2 = (uint)Math.Round(x.Clamp(MinNeg, MaxPos)) & 0xFFFF; - uint word1 = ((uint)Math.Round(y.Clamp(MinNeg, MaxPos)) & 0xFFFF) << 0x10; - - return word2 | word1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Vector2 ToByteScaledVector2() - { - var 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 deleted file mode 100644 index 641f154f94..0000000000 --- a/src/ImageSharp/PixelFormats/Short4.cs +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.PixelFormats -{ - /// - /// Packed pixel type containing four 16-bit signed integer values. - /// - /// Ranges from [-37267, -37267, -37267, -37267] to [37267, 37267, 37267, 37267] in vector form. - /// - /// - public struct Short4 : IPixel, IPackedVector - { - /// - /// The maximum byte value. - /// - private static readonly Vector4 MaxBytes = new Vector4(255); - - /// - /// The half the maximum byte value. - /// - private static readonly Vector4 Half = new Vector4(127); - - /// - /// The vector value used for rounding. - /// - private static readonly Vector4 Round = new Vector4(.5F); - - /// - /// Initializes a new instance of the struct. - /// - /// A vector containing the initial values for the components. - public Short4(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The x-component. - /// The y-component. - /// The z-component. - /// The w-component. - public Short4(float x, float y, float z, float w) - { - this.PackedValue = Pack(x, y, z, w); - } - - /// - public ulong PackedValue { get; set; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(Short4 left, Short4 right) - { - return left.PackedValue == right.PackedValue; - } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the parameter is not equal to the parameter; otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator !=(Short4 left, Short4 right) - { - return left.PackedValue != right.PackedValue; - } - - /// - public PixelOperations CreatePixelOperations() => new PixelOperations(); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromScaledVector4(Vector4 vector) - { - vector *= 65534F; - vector -= new Vector4(32767F); - this.PackFromVector4(vector); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() - { - var scaled = this.ToVector4(); - scaled += new Vector4(32767F); - scaled /= 65534F; - return scaled; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromVector4(Vector4 vector) - { - this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToVector4() - { - return new Vector4( - (short)(this.PackedValue & 0xFFFF), - (short)((this.PackedValue >> 0x10) & 0xFFFF), - (short)((this.PackedValue >> 0x20) & 0xFFFF), - (short)((this.PackedValue >> 0x30) & 0xFFFF)); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba32(Rgba32 source) - { - var vector = source.ToVector4(); - vector *= 65534; - vector -= new Vector4(32767); - this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromArgb32(Argb32 source) - { - var vector = source.ToVector4(); - vector *= 65534; - vector -= new Vector4(32767); - this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromBgra32(Bgra32 source) - { - var vector = source.ToVector4(); - vector *= 65534; - vector -= new Vector4(32767); - this.PackedValue = Pack(vector.X, vector.Y, vector.Z, vector.W); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb24(ref Rgb24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToRgba32(ref Rgba32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToArgb32(ref Argb32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToBgr24(ref Bgr24 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 ToBgra32(ref Bgra32 dest) - { - Vector4 vector = this.ToByteScaledVector4(); - 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 PackFromRgb48(Rgb48 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgb48(ref Rgb48 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PackFromRgba64(Rgba64 source) => this.PackFromScaledVector4(source.ToScaledVector4()); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ToRgba64(ref Rgba64 dest) => dest.PackFromScaledVector4(this.ToScaledVector4()); - - /// - public override bool Equals(object obj) - { - return obj is Short4 other && this.Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Short4 other) - { - return this == other; - } - - /// - /// Gets the hash code for the current instance. - /// - /// Hash code for the instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => this.PackedValue.GetHashCode(); - - /// - /// Returns a string representation of the current instance. - /// - /// String that represents the object. - public override string ToString() - { - return this.PackedValue.ToString("x16"); - } - - /// - /// Packs the components into a . - /// - /// The x-component - /// The y-component - /// The z-component - /// The w-component - /// The containing the packed values. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong Pack(float x, float y, float z, float w) - { - // Largest two byte positive number 0xFFFF >> 1; - const float MaxPos = 0x7FFF; - - // Two's complement - const float MinNeg = ~(int)MaxPos; - - // Clamp the value between min and max values - ulong word4 = ((ulong)Math.Round(x.Clamp(MinNeg, MaxPos)) & 0xFFFF) << 0x00; - ulong word3 = ((ulong)Math.Round(y.Clamp(MinNeg, MaxPos)) & 0xFFFF) << 0x10; - ulong word2 = ((ulong)Math.Round(z.Clamp(MinNeg, MaxPos)) & 0xFFFF) << 0x20; - ulong word1 = ((ulong)Math.Round(w.Clamp(MinNeg, MaxPos)) & 0xFFFF) << 0x30; - - return word4 | word3 | word2 | word1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Vector4 ToByteScaledVector4() - { - var vector = this.ToVector4(); - vector /= 65534; - vector *= 255; - vector += Half; - vector += Round; - return Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs b/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs new file mode 100644 index 0000000000..12ec389b06 --- /dev/null +++ b/src/ImageSharp/PixelFormats/Utils/PixelConverter.cs @@ -0,0 +1,108 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Buffers.Binary; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.PixelFormats.Utils +{ + /// + /// Contains optimized implementations for conversion between pixel formats. + /// + /// + /// Implementations are based on ideas in: + /// https://github.com/dotnet/coreclr/blob/master/src/System.Private.CoreLib/shared/System/Buffers/Binary/Reader.cs#L84 + /// The JIT can detect and optimize rotation idioms ROTL (Rotate Left) + /// and ROTR (Rotate Right) emitting efficient CPU instructions: + /// https://github.com/dotnet/coreclr/pull/1830 + /// + internal static class PixelConverter + { + public static class FromRgba32 + { + /// + /// Converts a packed to . + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static uint ToArgb32(uint packedRgba) + { + // packedRgba = [aa bb gg rr] + // ROTL(8, packedRgba) = [bb gg rr aa] + return (packedRgba << 8) | (packedRgba >> 24); + } + + /// + /// Converts a packed to . + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static uint ToBgra32(uint packedRgba) + { + // packedRgba = [aa bb gg rr] + // tmp1 = [aa 00 gg 00] + // tmp2 = [00 bb 00 rr] + // tmp3=ROTL(16, tmp2) = [00 rr 00 bb] + // tmp1 + tmp3 = [aa rr gg bb] + uint tmp1 = packedRgba & 0xFF00FF00; + uint tmp2 = packedRgba & 0x00FF00FF; + uint tmp3 = (tmp2 << 16) | (tmp2 >> 16); + return tmp1 + tmp3; + } + } + + public static class FromArgb32 + { + /// + /// Converts a packed to . + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static uint ToRgba32(uint packedArgb) + { + // packedArgb = [bb gg rr aa] + // ROTR(8, packedArgb) = [aa bb gg rr] + return (packedArgb >> 8) | (packedArgb << 24); + } + + /// + /// Converts a packed to . + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static uint ToBgra32(uint packedArgb) + { + // packedArgb = [bb gg rr aa] + // REVERSE(packedArgb) = [aa rr gg bb] + return BinaryPrimitives.ReverseEndianness(packedArgb); + } + } + + public static class FromBgra32 + { + /// + /// Converts a packed to . + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static uint ToArgb32(uint packedBgra) + { + // packedBgra = [aa rr gg bb] + // REVERSE(packedBgra) = [bb gg rr aa] + return BinaryPrimitives.ReverseEndianness(packedBgra); + } + + /// + /// Converts a packed to . + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static uint ToRgba32(uint packedBgra) + { + // packedRgba = [aa rr gg bb] + // tmp1 = [aa 00 gg 00] + // tmp2 = [00 rr 00 bb] + // tmp3=ROTL(16, tmp2) = [00 bb 00 rr] + // tmp1 + tmp3 = [aa bb gg rr] + uint tmp1 = packedBgra & 0xFF00FF00; + uint tmp2 = packedBgra & 0x00FF00FF; + uint tmp3 = (tmp2 << 16) | (tmp2 >> 16); + return tmp1 + tmp3; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs new file mode 100644 index 0000000000..139dbfa10f --- /dev/null +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.Default.cs @@ -0,0 +1,89 @@ +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.PixelFormats.Utils +{ + /// + /// Helper class for (bulk) conversion of buffers to/from other buffer types. + /// + internal static partial class Vector4Converters + { + /// + /// Provides default implementations for batched to/from conversion. + /// WARNING: The methods are operating without bounds checking and input validation! + /// Input validation is the responsibility of the caller! + /// + public static class Default + { + [MethodImpl(InliningOptions.ShortMethod)] + internal static void DangerousFromVector4( + ReadOnlySpan sourceVectors, + Span destPixels) + where TPixel : struct, IPixel + { + ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceVectors); + ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels); + + for (int i = 0; i < sourceVectors.Length; i++) + { + ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i); + ref TPixel dp = ref Unsafe.Add(ref destRef, i); + dp.FromVector4(sp); + } + } + + [MethodImpl(InliningOptions.ShortMethod)] + internal static void DangerousToVector4( + ReadOnlySpan sourcePixels, + Span destVectors) + where TPixel : struct, IPixel + { + ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourcePixels); + ref Vector4 destRef = ref MemoryMarshal.GetReference(destVectors); + + for (int i = 0; i < sourcePixels.Length; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceRef, i); + ref Vector4 dp = ref Unsafe.Add(ref destRef, i); + dp = sp.ToVector4(); + } + } + + [MethodImpl(InliningOptions.ShortMethod)] + internal static void DangerousFromScaledVector4( + ReadOnlySpan sourceVectors, + Span destinationColors) + where TPixel : struct, IPixel + { + ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceVectors); + ref TPixel destRef = ref MemoryMarshal.GetReference(destinationColors); + + for (int i = 0; i < sourceVectors.Length; i++) + { + ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i); + ref TPixel dp = ref Unsafe.Add(ref destRef, i); + dp.FromScaledVector4(sp); + } + } + + [MethodImpl(InliningOptions.ShortMethod)] + internal static void DangerousToScaledVector4( + ReadOnlySpan sourceColors, + Span destinationVectors) + where TPixel : struct, IPixel + { + ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourceColors); + ref Vector4 destRef = ref MemoryMarshal.GetReference(destinationVectors); + + for (int i = 0; i < sourceColors.Length; i++) + { + ref TPixel sp = ref Unsafe.Add(ref sourceRef, i); + ref Vector4 dp = ref Unsafe.Add(ref destRef, i); + dp = sp.ToScaledVector4(); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs new file mode 100644 index 0000000000..5609e606d8 --- /dev/null +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.RgbaCompatible.cs @@ -0,0 +1,154 @@ +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.PixelFormats.Utils +{ + /// + /// Contains + /// + internal static partial class Vector4Converters + { + /// + /// Provides efficient implementations for batched to/from conversion. + /// which is applicable for -compatible pixel types where + /// returns the same scaled result as . + /// The method is works by internally converting to a therefore it's not applicable for that type! + /// + public static class RgbaCompatible + { + /// + /// It's not worth to bother the transitive pixel conversion method below this limit. + /// The value depends on the actual gain brought by the SIMD characteristics of the executing CPU and JIT. + /// + private static readonly int Vector4ConversionThreshold = CalculateVector4ConversionThreshold(); + + /// + /// Provides an efficient default implementation for + /// and + /// which is applicable for -compatible pixel types where + /// returns the same scaled result as . + /// The method is works by internally converting to a therefore it's not applicable for that type! + /// + [MethodImpl(InliningOptions.ShortMethod)] + internal static void ToVector4( + Configuration configuration, + PixelOperations pixelOperations, + ReadOnlySpan sourcePixels, + Span destVectors, + bool scaled) + where TPixel : struct, IPixel + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourcePixels, destVectors, nameof(destVectors)); + + int count = sourcePixels.Length; + + // Not worth for small buffers: + if (count < Vector4ConversionThreshold) + { + ToVector4Fallback(sourcePixels, destVectors, scaled); + + return; + } + + // Using the last quarter of 'destVectors' as a temporary buffer to avoid allocation: + int countWithoutLastItem = count - 1; + ReadOnlySpan reducedSource = sourcePixels.Slice(0, countWithoutLastItem); + Span lastQuarterOfDestBuffer = MemoryMarshal.Cast(destVectors).Slice((3 * count) + 1, countWithoutLastItem); + pixelOperations.ToRgba32(configuration, reducedSource, lastQuarterOfDestBuffer); + + // 'destVectors' and 'lastQuarterOfDestBuffer' are overlapping buffers, + // but we are always reading/writing at different positions: + SimdUtils.BulkConvertByteToNormalizedFloat( + MemoryMarshal.Cast(lastQuarterOfDestBuffer), + MemoryMarshal.Cast(destVectors.Slice(0, countWithoutLastItem))); + + destVectors[countWithoutLastItem] = sourcePixels[countWithoutLastItem].ToVector4(); + } + + /// + /// Provides an efficient default implementation for + /// and + /// which is applicable for -compatible pixel types where + /// returns the same scaled result as . + /// The method is works by internally converting to a therefore it's not applicable for that type! + /// + [MethodImpl(InliningOptions.ShortMethod)] + internal static void FromVector4( + Configuration configuration, + PixelOperations pixelOperations, + ReadOnlySpan sourceVectors, + Span destPixels, + bool scaled) + where TPixel : struct, IPixel + { + Guard.NotNull(configuration, nameof(configuration)); + Guard.DestinationShouldNotBeTooShort(sourceVectors, destPixels, nameof(destPixels)); + + int count = sourceVectors.Length; + + // Not worth for small buffers: + if (count < Vector4ConversionThreshold) + { + FromVector4Fallback(sourceVectors, destPixels, scaled); + + return; + } + + // For the opposite direction it's not easy to implement the trick used in RunRgba32CompatibleToVector4Conversion, + // so let's allocate a temporary buffer as usually: + using (IMemoryOwner tempBuffer = configuration.MemoryAllocator.Allocate(count)) + { + Span tempSpan = tempBuffer.Memory.Span; + + SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows( + MemoryMarshal.Cast(sourceVectors), + MemoryMarshal.Cast(tempSpan)); + + pixelOperations.FromRgba32(configuration, tempSpan, destPixels); + } + } + + [MethodImpl(InliningOptions.ColdPath)] + private static void ToVector4Fallback(ReadOnlySpan sourcePixels, Span destVectors, bool scaled) + where TPixel : struct, IPixel + { + if (scaled) + { + Default.DangerousToScaledVector4(sourcePixels, destVectors); + } + else + { + Default.DangerousToVector4(sourcePixels, destVectors); + } + } + + [MethodImpl(InliningOptions.ColdPath)] + private static void FromVector4Fallback(ReadOnlySpan sourceVectors, Span destPixels, bool scaled) + where TPixel : struct, IPixel + { + if (scaled) + { + Default.DangerousFromScaledVector4(sourceVectors, destPixels); + } + else + { + Default.DangerousFromVector4(sourceVectors, destPixels); + } + } + + private static int CalculateVector4ConversionThreshold() + { + if (!Vector.IsHardwareAccelerated) + { + return int.MaxValue; + } + + return SimdUtils.ExtendedIntrinsics.IsAvailable && SimdUtils.IsAvx2CompatibleArchitecture ? 256 : 128; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Primitives/DenseMatrix{T}.cs b/src/ImageSharp/Primitives/DenseMatrix{T}.cs index ef1abc8971..7cfa98ec1b 100644 --- a/src/ImageSharp/Primitives/DenseMatrix{T}.cs +++ b/src/ImageSharp/Primitives/DenseMatrix{T}.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using System.Runtime.CompilerServices; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Primitives { @@ -31,6 +32,11 @@ namespace SixLabors.ImageSharp.Primitives /// public readonly int Rows; + /// + /// Gets the size of the dense matrix. + /// + public readonly Size Size; + /// /// Gets the number of items in the array. /// @@ -57,6 +63,7 @@ namespace SixLabors.ImageSharp.Primitives this.Rows = rows; this.Columns = columns; + this.Size = new Size(columns, rows); this.Count = columns * rows; this.Data = new T[this.Columns * this.Rows]; } @@ -76,6 +83,7 @@ namespace SixLabors.ImageSharp.Primitives this.Rows = rows; this.Columns = columns; + this.Size = new Size(columns, rows); this.Count = this.Columns * this.Rows; this.Data = new T[this.Columns * this.Rows]; @@ -182,13 +190,13 @@ namespace SixLabors.ImageSharp.Primitives } /// - public bool Equals(DenseMatrix other) => - this.Columns == other.Columns && - this.Rows == other.Rows && - this.Span.SequenceEqual(other.Span); + public override bool Equals(object obj) => obj is DenseMatrix other && this.Equals(other); /// - public override bool Equals(object obj) => obj is DenseMatrix other && this.Equals(other); + public bool Equals(DenseMatrix other) => + this.Columns == other.Columns + && this.Rows == other.Rows + && this.Span.SequenceEqual(other.Span); /// public override int GetHashCode() => this.Data.GetHashCode(); diff --git a/src/ImageSharp/Processing/AffineTransformBuilder.cs b/src/ImageSharp/Processing/AffineTransformBuilder.cs new file mode 100644 index 0000000000..c3d01241c9 --- /dev/null +++ b/src/ImageSharp/Processing/AffineTransformBuilder.cs @@ -0,0 +1,303 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Numerics; +using SixLabors.ImageSharp.Processing.Processors.Transforms; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// A helper class for constructing instances for use in affine transforms. + /// + public class AffineTransformBuilder + { + private readonly List> matrixFactories = new List>(); + + /// + /// Prepends a rotation matrix using the given rotation angle in degrees + /// and the image center point as rotation center. + /// + /// The amount of rotation, in degrees. + /// The . + public AffineTransformBuilder PrependRotationDegrees(float degrees) + => this.PrependRotationRadians(GeometryUtilities.DegreeToRadian(degrees)); + + /// + /// Prepends a rotation matrix using the given rotation angle in radians + /// and the image center point as rotation center. + /// + /// The amount of rotation, in radians. + /// The . + public AffineTransformBuilder PrependRotationRadians(float radians) + => this.Prepend(size => TransformUtils.CreateRotationMatrixRadians(radians, size)); + + /// + /// Prepends a rotation matrix using the given rotation in degrees at the given origin. + /// + /// The amount of rotation, in degrees. + /// The rotation origin point. + /// The . + public AffineTransformBuilder PrependRotationDegrees(float degrees, Vector2 origin) + => this.PrependRotationRadians(GeometryUtilities.DegreeToRadian(degrees), origin); + + /// + /// Prepends a rotation matrix using the given rotation in radians at the given origin. + /// + /// The amount of rotation, in radians. + /// The rotation origin point. + /// The . + public AffineTransformBuilder PrependRotationRadians(float radians, Vector2 origin) + => this.PrependMatrix(Matrix3x2.CreateRotation(radians, origin)); + + /// + /// Appends a rotation matrix using the given rotation angle in degrees + /// and the image center point as rotation center. + /// + /// The amount of rotation, in degrees. + /// The . + public AffineTransformBuilder AppendRotationDegrees(float degrees) + => this.AppendRotationRadians(GeometryUtilities.DegreeToRadian(degrees)); + + /// + /// Appends a rotation matrix using the given rotation angle in radians + /// and the image center point as rotation center. + /// + /// The amount of rotation, in radians. + /// The . + public AffineTransformBuilder AppendRotationRadians(float radians) + => this.Append(size => TransformUtils.CreateRotationMatrixRadians(radians, size)); + + /// + /// Appends a rotation matrix using the given rotation in degrees at the given origin. + /// + /// The amount of rotation, in degrees. + /// The rotation origin point. + /// The . + public AffineTransformBuilder AppendRotationDegrees(float degrees, Vector2 origin) + => this.AppendRotationRadians(GeometryUtilities.DegreeToRadian(degrees), origin); + + /// + /// Appends a rotation matrix using the given rotation in radians at the given origin. + /// + /// The amount of rotation, in radians. + /// The rotation origin point. + /// The . + public AffineTransformBuilder AppendRotationRadians(float radians, Vector2 origin) + => this.AppendMatrix(Matrix3x2.CreateRotation(radians, origin)); + + /// + /// Prepends a scale matrix from the given uniform scale. + /// + /// The uniform scale. + /// The . + public AffineTransformBuilder PrependScale(float scale) + => this.PrependMatrix(Matrix3x2.CreateScale(scale)); + + /// + /// Prepends a scale matrix from the given vector scale. + /// + /// The horizontal and vertical scale. + /// The . + public AffineTransformBuilder PrependScale(SizeF scale) + => this.PrependScale((Vector2)scale); + + /// + /// Prepends a scale matrix from the given vector scale. + /// + /// The horizontal and vertical scale. + /// The . + public AffineTransformBuilder PrependScale(Vector2 scales) + => this.PrependMatrix(Matrix3x2.CreateScale(scales)); + + /// + /// Appends a scale matrix from the given uniform scale. + /// + /// The uniform scale. + /// The . + public AffineTransformBuilder AppendScale(float scale) + => this.AppendMatrix(Matrix3x2.CreateScale(scale)); + + /// + /// Appends a scale matrix from the given vector scale. + /// + /// The horizontal and vertical scale. + /// The . + public AffineTransformBuilder AppendScale(SizeF scales) + => this.AppendScale((Vector2)scales); + + /// + /// Appends a scale matrix from the given vector scale. + /// + /// The horizontal and vertical scale. + /// The . + public AffineTransformBuilder AppendScale(Vector2 scales) + => this.AppendMatrix(Matrix3x2.CreateScale(scales)); + + /// + /// Prepends a centered skew matrix from the give angles in degrees. + /// + /// The X angle, in degrees. + /// The Y angle, in degrees. + /// The . + public AffineTransformBuilder PrependSkewDegrees(float degreesX, float degreesY) + => this.Prepend(size => TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, size)); + + /// + /// Prepends a centered skew matrix from the give angles in radians. + /// + /// The X angle, in radians. + /// The Y angle, in radians. + /// The . + public AffineTransformBuilder PrependSkewRadians(float radiansX, float radiansY) + => this.Prepend(size => TransformUtils.CreateSkewMatrixRadians(radiansX, radiansY, size)); + + /// + /// Prepends a skew matrix using the given angles in degrees at the given origin. + /// + /// The X angle, in degrees. + /// The Y angle, in degrees. + /// The skew origin point. + /// The . + public AffineTransformBuilder PrependSkewDegrees(float degreesX, float degreesY, Vector2 origin) + => this.PrependSkewRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), origin); + + /// + /// Prepends a skew matrix using the given angles in radians at the given origin. + /// + /// The X angle, in radians. + /// The Y angle, in radians. + /// The skew origin point. + /// The . + public AffineTransformBuilder PrependSkewRadians(float radiansX, float radiansY, Vector2 origin) + => this.PrependMatrix(Matrix3x2.CreateSkew(radiansX, radiansY, origin)); + + /// + /// Appends a centered skew matrix from the give angles in degrees. + /// + /// The X angle, in degrees. + /// The Y angle, in degrees. + /// The . + public AffineTransformBuilder AppendSkewDegrees(float degreesX, float degreesY) + => this.Append(size => TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, size)); + + /// + /// Appends a centered skew matrix from the give angles in radians. + /// + /// The X angle, in radians. + /// The Y angle, in radians. + /// The . + public AffineTransformBuilder AppendSkewRadians(float radiansX, float radiansY) + => this.Append(size => TransformUtils.CreateSkewMatrixRadians(radiansX, radiansY, size)); + + /// + /// Appends a skew matrix using the given angles in degrees at the given origin. + /// + /// The X angle, in degrees. + /// The Y angle, in degrees. + /// The skew origin point. + /// The . + public AffineTransformBuilder AppendSkewDegrees(float degreesX, float degreesY, Vector2 origin) + => this.AppendSkewRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), origin); + + /// + /// Appends a skew matrix using the given angles in radians at the given origin. + /// + /// The X angle, in radians. + /// The Y angle, in radians. + /// The skew origin point. + /// The . + public AffineTransformBuilder AppendSkewRadians(float radiansX, float radiansY, Vector2 origin) + => this.AppendMatrix(Matrix3x2.CreateSkew(radiansX, radiansY, origin)); + + /// + /// Prepends a translation matrix from the given vector. + /// + /// The translation position. + /// The . + public AffineTransformBuilder PrependTranslation(PointF position) + => this.PrependTranslation((Vector2)position); + + /// + /// Prepends a translation matrix from the given vector. + /// + /// The translation position. + /// The . + public AffineTransformBuilder PrependTranslation(Vector2 position) + => this.PrependMatrix(Matrix3x2.CreateTranslation(position)); + + /// + /// Appends a translation matrix from the given vector. + /// + /// The translation position. + /// The . + public AffineTransformBuilder AppendTranslation(PointF position) + => this.AppendTranslation((Vector2)position); + + /// + /// Appends a translation matrix from the given vector. + /// + /// The translation position. + /// The . + public AffineTransformBuilder AppendTranslation(Vector2 position) + => this.AppendMatrix(Matrix3x2.CreateTranslation(position)); + + /// + /// Prepends a raw matrix. + /// + /// The matrix to prepend. + /// The . + public AffineTransformBuilder PrependMatrix(Matrix3x2 matrix) => this.Prepend(_ => matrix); + + /// + /// Appends a raw matrix. + /// + /// The matrix to append. + /// The . + public AffineTransformBuilder AppendMatrix(Matrix3x2 matrix) => this.Append(_ => matrix); + + /// + /// Returns the combined matrix for a given source size. + /// + /// The source image size. + /// The . + public Matrix3x2 BuildMatrix(Size sourceSize) => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize)); + + /// + /// Returns the combined matrix for a given source rectangle. + /// + /// The rectangle in the source image. + /// The . + public Matrix3x2 BuildMatrix(Rectangle sourceRectangle) + { + Guard.MustBeGreaterThan(sourceRectangle.Width, 0, nameof(sourceRectangle)); + Guard.MustBeGreaterThan(sourceRectangle.Height, 0, nameof(sourceRectangle)); + + // Translate the origin matrix to cater for source rectangle offsets. + var matrix = Matrix3x2.CreateTranslation(-sourceRectangle.Location); + + Size size = sourceRectangle.Size; + + foreach (Func factory in this.matrixFactories) + { + matrix *= factory(size); + } + + return matrix; + } + + private AffineTransformBuilder Prepend(Func factory) + { + this.matrixFactories.Insert(0, factory); + return this; + } + + private AffineTransformBuilder Append(Func factory) + { + this.matrixFactories.Add(factory); + return this; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/CropExtensions.cs b/src/ImageSharp/Processing/CropExtensions.cs index 34c754a08e..1c0d80afc9 100644 --- a/src/ImageSharp/Processing/CropExtensions.cs +++ b/src/ImageSharp/Processing/CropExtensions.cs @@ -35,6 +35,6 @@ namespace SixLabors.ImageSharp.Processing /// The public static IImageProcessingContext Crop(this IImageProcessingContext source, Rectangle cropRectangle) where TPixel : struct, IPixel - => source.ApplyProcessor(new CropProcessor(cropRectangle)); + => source.ApplyProcessor(new CropProcessor(cropRectangle, source.GetCurrentSize())); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/KnownFilterMatrices.cs b/src/ImageSharp/Processing/KnownFilterMatrices.cs index 4f5e3c8697..cf0d19ff85 100644 --- a/src/ImageSharp/Processing/KnownFilterMatrices.cs +++ b/src/ImageSharp/Processing/KnownFilterMatrices.cs @@ -322,7 +322,7 @@ namespace SixLabors.ImageSharp.Processing degrees += 360; } - float radian = MathFExtensions.DegreeToRadian(degrees); + float radian = GeometryUtilities.DegreeToRadian(degrees); float cosRadian = MathF.Cos(radian); float sinRadian = MathF.Sin(radian); diff --git a/src/ImageSharp/Processing/KnownQuantizers.cs b/src/ImageSharp/Processing/KnownQuantizers.cs index fe98063104..e4a7a75d5f 100644 --- a/src/ImageSharp/Processing/KnownQuantizers.cs +++ b/src/ImageSharp/Processing/KnownQuantizers.cs @@ -12,20 +12,23 @@ namespace SixLabors.ImageSharp.Processing { /// /// Gets the adaptive Octree quantizer. Fast with good quality. - /// The quantizer only supports a single alpha value. /// public static IQuantizer Octree { get; } = new OctreeQuantizer(); /// /// Gets the Xiaolin Wu's Color Quantizer which generates high quality output. - /// The quantizer supports multiple alpha values. /// public static IQuantizer Wu { get; } = new WuQuantizer(); /// - /// Gets the palette based, Using the collection of web-safe colors. - /// The quantizer supports multiple alpha values. + /// Gets the palette based quantizer consisting of web safe colors as defined in the CSS Color Module Level 4. /// - public static IQuantizer Palette { get; } = new PaletteQuantizer(); + public static IQuantizer WebSafe { get; } = new WebSafePaletteQuantizer(); + + /// + /// Gets the palette based quantizer consisting of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821. + /// The hex codes were collected and defined by Nicholas Rougeux + /// + public static IQuantizer Werner { get; } = new WernerPaletteQuantizer(); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs index 5041dcf5ac..32cc2f434b 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryErrorDiffusionProcessor.cs @@ -76,8 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - float threshold = this.Threshold * 255F; - Rgba32 rgba = default; + byte threshold = (byte)MathF.Round(this.Threshold * 255F); bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); @@ -89,10 +88,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization // Collect the values before looping so we can reduce our calculation count for identical sibling pixels TPixel sourcePixel = source[startX, startY]; TPixel previousPixel = sourcePixel; + Rgba32 rgba = default; sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required - float luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); + byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); for (int y = startY; y < endY; y++) { @@ -107,7 +107,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization if (!previousPixel.Equals(sourcePixel)) { sourcePixel.ToRgba32(ref rgba); - luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); + luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); // Setup the previous pointer previousPixel = sourcePixel; diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs index 048af82619..cfdaf107c3 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryOrderedDitherProcessor.cs @@ -56,7 +56,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - Rgba32 rgba = default; bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); @@ -68,10 +67,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization // Collect the values before looping so we can reduce our calculation count for identical sibling pixels TPixel sourcePixel = source[startX, startY]; TPixel previousPixel = sourcePixel; + Rgba32 rgba = default; sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required - float luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); + byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); for (int y = startY; y < endY; y++) { @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization if (!previousPixel.Equals(sourcePixel)) { sourcePixel.ToRgba32(ref rgba); - luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); + luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); // Setup the previous pointer previousPixel = sourcePixel; diff --git a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs index c4f4266d98..67dcfc7f1b 100644 --- a/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Binarization/BinaryThresholdProcessor.cs @@ -2,10 +2,10 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Threading.Tasks; + using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Binarization @@ -56,9 +56,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization public TPixel LowerColor { get; set; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) { - float threshold = this.Threshold * 255F; + byte threshold = (byte)MathF.Round(this.Threshold * 255F); TPixel upper = this.UpperColor; TPixel lower = this.LowerColor; @@ -70,25 +73,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Binarization bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); - ParallelFor.WithConfiguration( - startY, - endY, + var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); + + ParallelHelper.IterateRows( + workingRect, configuration, - y => + rows => { - Span row = source.GetPixelRowSpan(y); Rgba32 rgba = default; - - for (int x = startX; x < endX; x++) + for (int y = rows.Min; y < rows.Max; y++) { - ref TPixel color = ref row[x]; - color.ToRgba32(ref rgba); + Span row = source.GetPixelRowSpan(y); + + for (int x = startX; x < endX; x++) + { + ref TPixel color = ref row[x]; + color.ToRgba32(ref rgba); - // Convert to grayscale using ITU-R Recommendation BT.709 if required - float luminance = isAlphaOnly - ? rgba.A - : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); - color = luminance >= threshold ? upper : lower; + // Convert to grayscale using ITU-R Recommendation BT.709 if required + byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); + color = luminance >= threshold ? upper : lower; + } } }); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs index 0ec62ac3d4..38dc638b90 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -66,36 +66,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution ? new DenseMatrix(size, 1) : new DenseMatrix(1, size); - float sum = 0F; - for (int i = 0; i < size; i++) - { - float x = 1; - sum += x; - if (horizontal) - { - kernel[0, i] = x; - } - else - { - kernel[i, 0] = x; - } - } - - // Normalize kernel so that the sum of all weights equals 1 - if (horizontal) - { - for (int i = 0; i < size; i++) - { - kernel[0, i] = kernel[0, i] / sum; - } - } - else - { - for (int i = 0; i < size; i++) - { - kernel[i, 0] = kernel[i, 0] / sum; - } - } + kernel.Fill(1.0F / size); return kernel; } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs index b5a2725437..bd1419e4bb 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs @@ -3,12 +3,10 @@ using System; using System.Numerics; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; -using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution @@ -27,6 +25,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// The vertical gradient operator. public Convolution2DProcessor(DenseMatrix kernelX, DenseMatrix kernelY) { + Guard.IsTrue(kernelX.Size.Equals(kernelY.Size), $"{nameof(kernelX)} {nameof(kernelY)}", "Kernel sizes must be the same."); this.KernelX = kernelX; this.KernelY = kernelY; } @@ -42,19 +41,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public DenseMatrix KernelY { get; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) { - int kernelYHeight = this.KernelY.Rows; - int kernelYWidth = this.KernelY.Columns; - int kernelXHeight = this.KernelX.Rows; - int kernelXWidth = this.KernelX.Columns; - int radiusY = kernelYHeight >> 1; - int radiusX = kernelXWidth >> 1; - - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; + DenseMatrix matrixY = this.KernelY; + DenseMatrix matrixX = this.KernelX; + + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; int maxY = endY - 1; int maxX = endX - 1; @@ -62,67 +61,30 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { source.CopyTo(targetPixels); - ParallelFor.WithConfiguration( - startY, - endY, - configuration, - y => - { - Span sourceRow = source.GetPixelRowSpan(y); - Span targetRow = targetPixels.GetRowSpan(y); + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + int width = workingRectangle.Width; - for (int x = startX; x < endX; x++) + ParallelHelper.IterateRowsWithTempBuffer( + workingRectangle, + configuration, + (rows, vectorBuffer) => { - float rX = 0; - float gX = 0; - float bX = 0; - float rY = 0; - float gY = 0; - float bY = 0; + Span vectorSpan = vectorBuffer.Span; + int length = vectorSpan.Length; - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelYHeight; fy++) + for (int y = rows.Min; y < rows.Max; y++) { - int fyr = fy - radiusY; - int offsetY = y + fyr; + Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan); - offsetY = offsetY.Clamp(0, maxY); - Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); - - for (int fx = 0; fx < kernelXWidth; fx++) + for (int x = 0; x < width; x++) { - int fxr = fx - radiusX; - int offsetX = x + fxr; - - offsetX = offsetX.Clamp(0, maxX); - Vector4 currentColor = sourceOffsetRow[offsetX].ToVector4().Premultiply(); - - if (fy < kernelXHeight) - { - Vector4 kx = this.KernelX[fy, fx] * currentColor; - rX += kx.X; - gX += kx.Y; - bX += kx.Z; - } - - if (fx < kernelYWidth) - { - Vector4 ky = this.KernelY[fy, fx] * currentColor; - rY += ky.X; - gY += ky.Y; - bY += ky.Z; - } + DenseMatrixUtils.Convolve2D(in matrixY, in matrixX, source.PixelBuffer, vectorSpan, y, x, maxY, maxX, startX); } - } - float red = MathF.Sqrt((rX * rX) + (rY * rY)); - float green = MathF.Sqrt((gX * gX) + (gY * gY)); - float blue = MathF.Sqrt((bX * bX) + (bY * bY)); - - ref TPixel pixel = ref targetRow[x]; - pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply()); - } - }); + PixelOperations.Instance.FromVector4(configuration, vectorSpan.Slice(0, length), targetRowSpan); + } + }); Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs index 0808c07d03..05007c3706 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs @@ -3,13 +3,11 @@ using System; using System.Numerics; -using System.Threading.Tasks; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; -using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution @@ -47,8 +45,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { using (Buffer2D firstPassPixels = configuration.MemoryAllocator.Allocate2D(source.Size())) { - this.ApplyConvolution(firstPassPixels, source.PixelBuffer, source.Bounds(), this.KernelX, configuration); - this.ApplyConvolution(source.PixelBuffer, firstPassPixels, sourceRectangle, this.KernelY, configuration); + source.CopyTo(firstPassPixels); + + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + this.ApplyConvolution(firstPassPixels, source.PixelBuffer, interest, this.KernelX, configuration); + this.ApplyConvolution(source.PixelBuffer, firstPassPixels, interest, this.KernelY, configuration); } } @@ -67,14 +68,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution Buffer2D targetPixels, Buffer2D sourcePixels, Rectangle sourceRectangle, - DenseMatrix kernel, // TODO: Can't use 'in' as pass by ref to lambda expression. + in DenseMatrix kernel, Configuration configuration) { - int kernelHeight = kernel.Rows; - int kernelWidth = kernel.Columns; - int radiusY = kernelHeight >> 1; - int radiusX = kernelWidth >> 1; - + DenseMatrix matrix = kernel; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; @@ -82,43 +79,30 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution int maxY = endY - 1; int maxX = endX - 1; - ParallelFor.WithConfiguration( - startY, - endY, - configuration, - y => - { - Span targetRow = targetPixels.GetRowSpan(y); + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + int width = workingRectangle.Width; - for (int x = startX; x < endX; x++) + ParallelHelper.IterateRowsWithTempBuffer( + workingRectangle, + configuration, + (rows, vectorBuffer) => { - Vector4 destination = default; + Span vectorSpan = vectorBuffer.Span; + int length = vectorSpan.Length; - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelHeight; fy++) + for (int y = rows.Min; y < rows.Max; y++) { - int fyr = fy - radiusY; - int offsetY = y + fyr; - - offsetY = offsetY.Clamp(0, maxY); - Span row = sourcePixels.GetRowSpan(offsetY); + Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan); - for (int fx = 0; fx < kernelWidth; fx++) + for (int x = 0; x < width; x++) { - int fxr = fx - radiusX; - int offsetX = x + fxr; - - offsetX = offsetX.Clamp(0, maxX); - - Vector4 currentColor = row[offsetX].ToVector4().Premultiply(); - destination += kernel[fy, fx] * currentColor; + DenseMatrixUtils.Convolve(in matrix, sourcePixels, vectorSpan, y, x, maxY, maxX, startX); } - } - ref TPixel pixel = ref targetRow[x]; - pixel.PackFromVector4(destination.UnPremultiply()); - } - }); + PixelOperations.Instance.FromVector4(configuration, vectorSpan.Slice(0, length), targetRowSpan); + } + }); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs index 31e638a0ad..8ef64bdacc 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs @@ -3,13 +3,10 @@ using System; using System.Numerics; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; -using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution @@ -25,10 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Initializes a new instance of the class. /// /// The 2d gradient operator. - public ConvolutionProcessor(DenseMatrix kernelXY) - { - this.KernelXY = kernelXY; - } + public ConvolutionProcessor(DenseMatrix kernelXY) => this.KernelXY = kernelXY; /// /// Gets the 2d gradient operator. @@ -38,13 +32,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - int kernelLength = this.KernelXY.Rows; - int radius = kernelLength >> 1; - - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; + DenseMatrix matrix = this.KernelXY; + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startY = interest.Y; + int endY = interest.Bottom; + int startX = interest.X; + int endX = interest.Right; int maxY = endY - 1; int maxX = endX - 1; @@ -52,50 +45,30 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { source.CopyTo(targetPixels); - ParallelFor.WithConfiguration( - startY, - endY, - configuration, - y => - { - Span sourceRow = source.GetPixelRowSpan(y); - Span targetRow = targetPixels.GetRowSpan(y); - - for (int x = startX; x < endX; x++) - { - float red = 0; - float green = 0; - float blue = 0; - - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelLength; fy++) - { - int fyr = fy - radius; - int offsetY = y + fyr; - - offsetY = offsetY.Clamp(0, maxY); - Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); - - for (int fx = 0; fx < kernelLength; fx++) - { - int fxr = fx - radius; - int offsetX = x + fxr; - - offsetX = offsetX.Clamp(0, maxX); - - Vector4 currentColor = sourceOffsetRow[offsetX].ToVector4().Premultiply(); - currentColor *= this.KernelXY[fy, fx]; - - red += currentColor.X; - green += currentColor.Y; - blue += currentColor.Z; - } - } - - ref TPixel pixel = ref targetRow[x]; - pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W).UnPremultiply()); - } - }); + var workingRectangle = Rectangle.FromLTRB(startX, startY, endX, endY); + int width = workingRectangle.Width; + + ParallelHelper.IterateRowsWithTempBuffer( + workingRectangle, + configuration, + (rows, vectorBuffer) => + { + Span vectorSpan = vectorBuffer.Span; + int length = vectorSpan.Length; + + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan.Slice(0, length), vectorSpan); + + for (int x = 0; x < width; x++) + { + DenseMatrixUtils.Convolve(in matrix, source.PixelBuffer, vectorSpan, y, x, maxY, maxX, startX); + } + + PixelOperations.Instance.FromVector4(configuration, vectorSpan.Slice(0, length), targetRowSpan); + } + }); Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs index dd43d3e159..8927716492 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs @@ -23,6 +23,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Whether to convert the image to grayscale before performing edge detection. protected EdgeDetector2DProcessor(DenseMatrix kernelX, DenseMatrix kernelY, bool grayscale) { + Guard.IsTrue(kernelX.Size.Equals(kernelY.Size), $"{nameof(kernelX)} {nameof(kernelY)}", "Kernel sizes must be the same."); this.KernelX = kernelX; this.KernelY = kernelY; this.Grayscale = grayscale; @@ -42,10 +43,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public bool Grayscale { get; set; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - new Convolution2DProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); - } + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) => new Convolution2DProcessor(this.KernelX, this.KernelY).Apply(source, sourceRectangle, configuration); /// protected override void BeforeFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs index 316de422f5..4165cf024e 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Threading.Tasks; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing.Processors.Filters; @@ -124,6 +125,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution shiftY = 0; } + var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + // Additional runs. // ReSharper disable once ForCanBeConvertedToForeach for (int i = 1; i < kernels.Length; i++) @@ -135,30 +138,35 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution Buffer2D passPixels = pass.PixelBuffer; Buffer2D targetPixels = source.PixelBuffer; - ParallelFor.WithConfiguration( - minY, - maxY, + ParallelHelper.IterateRows( + workingRect, configuration, - y => + rows => { - int offsetY = y - shiftY; + for (int y = rows.Min; y < rows.Max; y++) + { + int offsetY = y - shiftY; - ref TPixel passPixelsBase = ref MemoryMarshal.GetReference(passPixels.GetRowSpan(offsetY)); - ref TPixel targetPixelsBase = ref MemoryMarshal.GetReference(targetPixels.GetRowSpan(offsetY)); + ref TPixel passPixelsBase = + ref MemoryMarshal.GetReference(passPixels.GetRowSpan(offsetY)); + ref TPixel targetPixelsBase = + ref MemoryMarshal.GetReference(targetPixels.GetRowSpan(offsetY)); - for (int x = minX; x < maxX; x++) - { - int offsetX = x - shiftX; + for (int x = minX; x < maxX; x++) + { + int offsetX = x - shiftX; - // Grab the max components of the two pixels - ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, offsetX); - ref TPixel currentTargetPixel = ref Unsafe.Add(ref targetPixelsBase, offsetX); + // Grab the max components of the two pixels + ref TPixel currentPassPixel = ref Unsafe.Add(ref passPixelsBase, offsetX); + ref TPixel currentTargetPixel = + ref Unsafe.Add(ref targetPixelsBase, offsetX); - var pixelValue = Vector4.Max( - currentPassPixel.ToVector4(), - currentTargetPixel.ToVector4()); + var pixelValue = Vector4.Max( + currentPassPixel.ToVector4(), + currentTargetPixel.ToVector4()); - currentTargetPixel.PackFromVector4(pixelValue); + currentTargetPixel.FromVector4(pixelValue); + } } }); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/KirshKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/KirschKernels.cs similarity index 96% rename from src/ImageSharp/Processing/Processors/Convolution/KirshKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/KirschKernels.cs index d315875089..86232e306a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KirshKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KirschKernels.cs @@ -6,9 +6,9 @@ using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Contains the eight matrices used for Kirsh edge detection + /// Contains the eight matrices used for Kirsch edge detection /// - internal static class KirshKernels + internal static class KirschKernels { /// /// Gets the North gradient operator diff --git a/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs index 46cf00c226..c3188676f3 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs @@ -23,27 +23,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - public override DenseMatrix North => KirshKernels.KirschNorth; + public override DenseMatrix North => KirschKernels.KirschNorth; /// - public override DenseMatrix NorthWest => KirshKernels.KirschNorthWest; + public override DenseMatrix NorthWest => KirschKernels.KirschNorthWest; /// - public override DenseMatrix West => KirshKernels.KirschWest; + public override DenseMatrix West => KirschKernels.KirschWest; /// - public override DenseMatrix SouthWest => KirshKernels.KirschSouthWest; + public override DenseMatrix SouthWest => KirschKernels.KirschSouthWest; /// - public override DenseMatrix South => KirshKernels.KirschSouth; + public override DenseMatrix South => KirschKernels.KirschSouth; /// - public override DenseMatrix SouthEast => KirshKernels.KirschSouthEast; + public override DenseMatrix SouthEast => KirschKernels.KirschSouthEast; /// - public override DenseMatrix East => KirshKernels.KirschEast; + public override DenseMatrix East => KirschKernels.KirschEast; /// - public override DenseMatrix NorthEast => KirshKernels.KirschNorthEast; + public override DenseMatrix NorthEast => KirschKernels.KirschNorthEast; } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs index b407841f20..642da2f001 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs @@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering var offsetColor = pixel.ToVector4(); Vector4 result = ((error * coefficient) / this.divisorVector) + offsetColor; - pixel.PackFromVector4(result); + pixel.FromVector4(result); } } } diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs index 8e2b2a5a82..911d3e8fdc 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs @@ -4,7 +4,6 @@ using System; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering @@ -64,8 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - float threshold = this.Threshold * 255F; - Rgba32 rgba = default; + byte threshold = (byte)MathF.Round(this.Threshold * 255F); bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); @@ -78,10 +76,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering TPixel sourcePixel = source[startX, startY]; TPixel previousPixel = sourcePixel; PixelPair pair = this.GetClosestPixelPair(ref sourcePixel); + Rgba32 rgba = default; sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required - float luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); + byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); for (int y = startY; y < endY; y++) { @@ -104,7 +103,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering } sourcePixel.ToRgba32(ref rgba); - luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); + luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); // Setup the previous pointer previousPixel = sourcePixel; diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs index b5e2eebc2b..1b4910a147 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs @@ -4,7 +4,6 @@ using System; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering @@ -32,10 +31,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// The ordered ditherer. /// The palette to select substitute colors from. public OrderedDitherPaletteProcessor(IOrderedDither dither, TPixel[] palette) - : base(palette) - { - this.Dither = dither ?? throw new ArgumentNullException(nameof(dither)); - } + : base(palette) => this.Dither = dither ?? throw new ArgumentNullException(nameof(dither)); /// /// Gets the ditherer. @@ -45,7 +41,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - Rgba32 rgba = default; bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8); var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); @@ -58,10 +53,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering TPixel sourcePixel = source[startX, startY]; TPixel previousPixel = sourcePixel; PixelPair pair = this.GetClosestPixelPair(ref sourcePixel); + Rgba32 rgba = default; sourcePixel.ToRgba32(ref rgba); // Convert to grayscale using ITU-R Recommendation BT.709 if required - float luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); + byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); for (int y = startY; y < endY; y++) { @@ -84,7 +80,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering } sourcePixel.ToRgba32(ref rgba); - luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B); + luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B); // Setup the previous pointer previousPixel = sourcePixel; diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs index a1bbe72733..f01865ec05 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Dithering { @@ -21,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// /// The vector representation of the image palette. /// - private readonly Vector4[] paletteVector; + private Vector4[] paletteVector; /// /// Initializes a new instance of the class. @@ -30,8 +31,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering protected PaletteDitherProcessorBase(TPixel[] palette) { this.Palette = palette ?? throw new ArgumentNullException(nameof(palette)); - this.paletteVector = new Vector4[this.Palette.Length]; - PixelOperations.Instance.ToScaledVector4(this.Palette, this.paletteVector, this.Palette.Length); } /// @@ -40,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering public TPixel[] Palette { get; } /// - /// Returns the two closest colors from the palette calcluated via Euclidean distance in the Rgba space. + /// Returns the two closest colors from the palette calculated via Euclidean distance in the Rgba space. /// /// The source color to match. /// The . @@ -90,5 +89,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering return pair; } + + protected override void BeforeFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + base.BeforeFrameApply(source, sourceRectangle, configuration); + + // Lazy init paletteVector: + if (this.paletteVector == null) + { + this.paletteVector = new Vector4[this.Palette.Length]; + PixelOperations.Instance.ToScaledVector4(configuration, this.Palette, this.paletteVector); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs index 59898e9fc1..1b17c470ed 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs @@ -3,11 +3,11 @@ using System; using System.Numerics; -using System.Threading.Tasks; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Effects @@ -49,7 +49,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects public int BrushSize { get; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) { if (this.BrushSize <= 0 || this.BrushSize > source.Height || this.BrushSize > source.Width) { @@ -70,69 +73,74 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects { source.CopyTo(targetPixels); - ParallelFor.WithConfiguration( - startY, - maxY, + var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); + ParallelHelper.IterateRows( + workingRect, configuration, - y => - { - Span sourceRow = source.GetPixelRowSpan(y); - Span targetRow = targetPixels.GetRowSpan(y); - - for (int x = startX; x < endX; x++) + rows => { - int maxIntensity = 0; - int maxIndex = 0; + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = source.GetPixelRowSpan(y); + Span targetRow = targetPixels.GetRowSpan(y); - int[] intensityBin = new int[levels]; - float[] redBin = new float[levels]; - float[] blueBin = new float[levels]; - float[] greenBin = new float[levels]; + for (int x = startX; x < endX; x++) + { + int maxIntensity = 0; + int maxIndex = 0; - for (int fy = 0; fy <= radius; fy++) - { - int fyr = fy - radius; - int offsetY = y + fyr; + int[] intensityBin = new int[levels]; + float[] redBin = new float[levels]; + float[] blueBin = new float[levels]; + float[] greenBin = new float[levels]; - offsetY = offsetY.Clamp(0, maxY); + for (int fy = 0; fy <= radius; fy++) + { + int fyr = fy - radius; + int offsetY = y + fyr; - Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); + offsetY = offsetY.Clamp(0, maxY); - for (int fx = 0; fx <= radius; fx++) - { - int fxr = fx - radius; - int offsetX = x + fxr; - offsetX = offsetX.Clamp(0, maxX); + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); - var vector = sourceOffsetRow[offsetX].ToVector4(); + for (int fx = 0; fx <= radius; fx++) + { + int fxr = fx - radius; + int offsetX = x + fxr; + offsetX = offsetX.Clamp(0, maxX); - float sourceRed = vector.X; - float sourceBlue = vector.Z; - float sourceGreen = vector.Y; + var vector = sourceOffsetRow[offsetX].ToVector4(); - int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); + float sourceRed = vector.X; + float sourceBlue = vector.Z; + float sourceGreen = vector.Y; - intensityBin[currentIntensity]++; - blueBin[currentIntensity] += sourceBlue; - greenBin[currentIntensity] += sourceGreen; - redBin[currentIntensity] += sourceRed; + int currentIntensity = (int)MathF.Round( + (sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); - if (intensityBin[currentIntensity] > maxIntensity) - { - maxIntensity = intensityBin[currentIntensity]; - maxIndex = currentIntensity; - } - } + intensityBin[currentIntensity]++; + blueBin[currentIntensity] += sourceBlue; + greenBin[currentIntensity] += sourceGreen; + redBin[currentIntensity] += sourceRed; - float red = MathF.Abs(redBin[maxIndex] / maxIntensity); - float green = MathF.Abs(greenBin[maxIndex] / maxIntensity); - float blue = MathF.Abs(blueBin[maxIndex] / maxIntensity); + if (intensityBin[currentIntensity] > maxIntensity) + { + maxIntensity = intensityBin[currentIntensity]; + maxIndex = currentIntensity; + } + } - ref TPixel pixel = ref targetRow[x]; - pixel.PackFromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); + float red = MathF.Abs(redBin[maxIndex] / maxIntensity); + float green = MathF.Abs(greenBin[maxIndex] / maxIntensity); + float blue = MathF.Abs(blueBin[maxIndex] / maxIntensity); + + ref TPixel pixel = ref targetRow[x]; + pixel.FromVector4( + new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); + } + } } - } - }); + }); Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); } diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs index 6244d8bf76..d3a44c066e 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs @@ -5,6 +5,7 @@ using System; using System.Numerics; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -35,25 +36,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - int startY = interest.Y; - int endY = interest.Bottom; - int startX = interest.X; - int endX = interest.Right; + Matrix4x4 matrix = this.Matrix; - ParallelFor.WithConfiguration( - startY, - endY, + ParallelHelper.IterateRows( + interest, configuration, - y => + rows => { - Span row = source.GetPixelRowSpan(y); - - for (int x = startX; x < endX; x++) + for (int y = rows.Min; y < rows.Max; y++) { - ref TPixel pixel = ref row[x]; - var vector = Vector4.Transform(pixel.ToVector4(), matrix); - pixel.PackFromVector4(vector); + Span row = source.GetPixelRowSpan(y); + + for (int x = interest.X; x < interest.Right; x++) + { + ref TPixel pixel = ref row[x]; + var vector = Vector4.Transform(pixel.ToVector4(), matrix); + pixel.FromVector4(vector); + } } }); } diff --git a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs index ecbeebeb06..25787ff922 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/BackgroundColorProcessor.cs @@ -6,6 +6,7 @@ using System.Buffers; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; @@ -67,6 +68,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays int width = maxX - minX; + var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + using (IMemoryOwner colors = source.MemoryAllocator.Allocate(width)) using (IMemoryOwner amount = source.MemoryAllocator.Allocate(width)) { @@ -74,25 +77,30 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays Span colorSpan = colors.GetSpan(); Span amountSpan = amount.GetSpan(); - // TODO: Use Span.Fill? - for (int i = 0; i < width; i++) - { - colorSpan[i] = this.Color; - amountSpan[i] = this.GraphicsOptions.BlendPercentage; - } + colorSpan.Fill(this.Color); + amountSpan.Fill(this.GraphicsOptions.BlendPercentage); PixelBlender blender = PixelOperations.Instance.GetPixelBlender(this.GraphicsOptions); - ParallelFor.WithConfiguration( - minY, - maxY, + + ParallelHelper.IterateRows( + workingRect, configuration, - y => - { - Span destination = source.GetPixelRowSpan(y - startY).Slice(minX - startX, width); + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span destination = + source.GetPixelRowSpan(y - startY).Slice(minX - startX, width); - // This switched color & destination in the 2nd and 3rd places because we are applying the target color under the current one - blender.Blend(source.MemoryAllocator, destination, colors.GetSpan(), destination, amount.GetSpan()); - }); + // This switched color & destination in the 2nd and 3rd places because we are applying the target color under the current one + blender.Blend( + source.Configuration, + destination, + colors.GetSpan(), + destination, + amount.GetSpan()); + } + }); } } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index eb91fec043..21f6be69f8 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -7,6 +7,7 @@ using System.Numerics; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.Memory; @@ -84,6 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { + // TODO: can we simplify the rectangle calculation? int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; @@ -113,36 +115,43 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays } int width = maxX - minX; + int offsetX = minX - startX; + + var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + using (IMemoryOwner rowColors = source.MemoryAllocator.Allocate(width)) { - // Be careful! Do not capture rowColorsSpan in the lambda below! - Span rowColorsSpan = rowColors.GetSpan(); - - for (int i = 0; i < width; i++) - { - rowColorsSpan[i] = glowColor; - } + rowColors.GetSpan().Fill(glowColor); - ParallelFor.WithTemporaryBuffer( - minY, - maxY, + ParallelHelper.IterateRowsWithTempBuffer( + workingRect, configuration, - width, - (y, amounts) => - { - Span amountsSpan = amounts.GetSpan(); - int offsetY = y - startY; - int offsetX = minX - startX; - for (int i = 0; i < width; i++) + (rows, amounts) => { - float distance = Vector2.Distance(center, new Vector2(i + offsetX, offsetY)); - amountsSpan[i] = (this.GraphicsOptions.BlendPercentage * (1 - (.95F * (distance / maxDistance)))).Clamp(0, 1); - } - - Span destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); - - this.blender.Blend(source.MemoryAllocator, destination, destination, rowColors.GetSpan(), amountsSpan); - }); + Span amountsSpan = amounts.Span; + + for (int y = rows.Min; y < rows.Max; y++) + { + int offsetY = y - startY; + + for (int i = 0; i < width; i++) + { + float distance = Vector2.Distance(center, new Vector2(i + offsetX, offsetY)); + amountsSpan[i] = + (this.GraphicsOptions.BlendPercentage * (1 - (.95F * (distance / maxDistance)))) + .Clamp(0, 1); + } + + Span destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); + + this.blender.Blend( + source.Configuration, + destination, + destination, + rowColors.GetSpan(), + amountsSpan); + } + }); } } } diff --git a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs index 63780df476..a8fa1d65c1 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/VignetteProcessor.cs @@ -7,6 +7,7 @@ using System.Numerics; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.Memory; @@ -115,43 +116,43 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays } int width = maxX - minX; + int offsetX = minX - startX; + + var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + using (IMemoryOwner rowColors = source.MemoryAllocator.Allocate(width)) { - // Be careful! Do not capture rowColorsSpan in the lambda below! - Span rowColorsSpan = rowColors.GetSpan(); - - for (int i = 0; i < width; i++) - { - rowColorsSpan[i] = vignetteColor; - } + rowColors.GetSpan().Fill(vignetteColor); - ParallelFor.WithTemporaryBuffer( - minY, - maxY, + ParallelHelper.IterateRowsWithTempBuffer( + workingRect, configuration, - width, - (y, amounts) => + (rows, amounts) => { - Span amountsSpan = amounts.GetSpan(); - int offsetY = y - startY; - int offsetX = minX - startX; - for (int i = 0; i < width; i++) + Span amountsSpan = amounts.Span; + + for (int y = rows.Min; y < rows.Max; y++) { - float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY)); - amountsSpan[i] = - (this.GraphicsOptions.BlendPercentage * (.9F * (distance / maxDistance))).Clamp( - 0, - 1); + int offsetY = y - startY; + + for (int i = 0; i < width; i++) + { + float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY)); + amountsSpan[i] = + (this.GraphicsOptions.BlendPercentage * (.9F * (distance / maxDistance))).Clamp( + 0, + 1); + } + + Span destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); + + this.blender.Blend( + source.Configuration, + destination, + destination, + rowColors.GetSpan(), + amountsSpan); } - - Span destination = source.GetPixelRowSpan(offsetY).Slice(offsetX, width); - - this.blender.Blend( - source.MemoryAllocator, - destination, - destination, - rowColors.GetSpan(), - amountsSpan); }); } } diff --git a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs index 6e594f223e..3b9b046a00 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization // Collect the palette. Required before the second pass runs. TPixel[] palette = this.GetPalette(); this.paletteVector = new Vector4[palette.Length]; - PixelOperations.Instance.ToScaledVector4(palette, this.paletteVector, palette.Length); + PixelOperations.Instance.ToScaledVector4(image.Configuration, palette, this.paletteVector); var quantizedFrame = new QuantizedFrame(image.MemoryAllocator, width, height, palette); if (this.Dither) @@ -139,8 +139,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization protected byte GetTransparentIndex() { // Transparent pixels are much more likely to be found at the end of a palette. - int index = this.paletteVector.Length - 1; - for (int i = this.paletteVector.Length - 1; i >= 0; i--) + int paletteVectorLengthMinus1 = this.paletteVector.Length - 1; + + int index = paletteVectorLengthMinus1; + for (int i = paletteVectorLengthMinus1; i >= 0; i--) { ref Vector4 candidate = ref this.paletteVector[i]; if (candidate.Equals(default)) diff --git a/src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs index 3da09cde09..f1490a6d2b 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/IQuantizer.cs @@ -19,18 +19,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Creates the generic frame quantizer /// + /// The to configure internal operations. /// The pixel format. /// The - IFrameQuantizer CreateFrameQuantizer() + IFrameQuantizer CreateFrameQuantizer(Configuration configuration) where TPixel : struct, IPixel; /// /// Creates the generic frame quantizer /// /// The pixel format. + /// The to configure internal operations. /// The maximum number of colors to hold in the color palette. /// The - IFrameQuantizer CreateFrameQuantizer(int maxColors) + IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) where TPixel : struct, IPixel; } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs index 39546d63f7..dd56375f63 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs @@ -73,14 +73,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization ref TPixel scanBaseRef = ref MemoryMarshal.GetReference(row); // And loop through each column - Rgba32 rgba = default; for (int x = 0; x < width; x++) { ref TPixel pixel = ref Unsafe.Add(ref scanBaseRef, x); - pixel.ToRgba32(ref rgba); // Add the color to the Octree - this.octree.AddColor(ref pixel, ref rgba); + this.octree.AddColor(ref pixel); } } } @@ -97,9 +95,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization // pass of the algorithm by avoiding transforming rows of identical color. TPixel sourcePixel = source[0, 0]; TPixel previousPixel = sourcePixel; - Rgba32 rgba = default; this.transparentIndex = this.GetTransparentIndex(); - byte pixelValue = this.QuantizePixel(ref sourcePixel, ref rgba); + byte pixelValue = this.QuantizePixel(ref sourcePixel); TPixel transformedPixel = palette[pixelValue]; for (int y = 0; y < height; y++) @@ -117,7 +114,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization if (!previousPixel.Equals(sourcePixel)) { // Quantize the pixel - pixelValue = this.QuantizePixel(ref sourcePixel, ref rgba); + pixelValue = this.QuantizePixel(ref sourcePixel); // And setup the previous pointer previousPixel = sourcePixel; @@ -139,6 +136,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } } + internal TPixel[] AotGetPalette() => this.GetPalette(); + /// protected override TPixel[] GetPalette() => this.octree.Palletize(this.colors); @@ -146,10 +145,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// Process the pixel in the second pass of the algorithm. /// /// The pixel to quantize. - /// The color to compare against. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - private byte QuantizePixel(ref TPixel pixel, ref Rgba32 rgba) + private byte QuantizePixel(ref TPixel pixel) { if (this.Dither) { @@ -158,13 +156,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization return this.GetClosestPixel(ref pixel); } + Rgba32 rgba = default; pixel.ToRgba32(ref rgba); if (rgba.Equals(default)) { return this.transparentIndex; } - return (byte)this.octree.GetPaletteIndex(ref pixel, ref rgba); + return (byte)this.octree.GetPaletteIndex(ref pixel); } /// @@ -239,8 +238,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// Add a given color value to the Octree /// /// The pixel data. - /// The color. - public void AddColor(ref TPixel pixel, ref Rgba32 rgba) + public void AddColor(ref TPixel pixel) { // Check if this request is for the same color as the last if (this.previousColor.Equals(pixel)) @@ -250,18 +248,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization if (this.previousNode is null) { this.previousColor = pixel; - this.root.AddColor(ref pixel, this.maxColorBits, 0, this, ref rgba); + this.root.AddColor(ref pixel, this.maxColorBits, 0, this); } else { // Just update the previous node - this.previousNode.Increment(ref pixel, ref rgba); + this.previousNode.Increment(ref pixel); } } else { this.previousColor = pixel; - this.root.AddColor(ref pixel, this.maxColorBits, 0, this, ref rgba); + this.root.AddColor(ref pixel, this.maxColorBits, 0, this); } } @@ -294,12 +292,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// Get the palette index for the passed color /// /// The pixel data. - /// The color to map to. /// /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetPaletteIndex(ref TPixel pixel, ref Rgba32 rgba) => this.root.GetPaletteIndex(ref pixel, 0, ref rgba); + public int GetPaletteIndex(ref TPixel pixel) => this.root.GetPaletteIndex(ref pixel, 0); /// /// Keep track of the previous node that was quantized @@ -426,13 +423,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// The number of significant color bits /// The level in the tree /// The tree to which this node belongs - /// The color to map to. - public void AddColor(ref TPixel pixel, int colorBits, int level, Octree octree, ref Rgba32 rgba) + public void AddColor(ref TPixel pixel, int colorBits, int level, Octree octree) { // Update the color information if this is a leaf if (this.leaf) { - this.Increment(ref pixel, ref rgba); + this.Increment(ref pixel); // Setup the previous node octree.TrackPrevious(this); @@ -441,6 +437,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization { // Go to the next level down in the tree int shift = 7 - level; + Rgba32 rgba = default; pixel.ToRgba32(ref rgba); int index = ((rgba.B & Mask[level]) >> (shift - 2)) @@ -456,7 +453,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } // Add the color to the child node - child.AddColor(ref pixel, colorBits, level + 1, octree, ref rgba); + child.AddColor(ref pixel, colorBits, level + 1, octree); } } @@ -504,7 +501,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization // Set the color of the palette entry var vector = Vector3.Clamp(new Vector3(this.red, this.green, this.blue) / this.pixelCount, Vector3.Zero, new Vector3(255)); TPixel pixel = default; - pixel.PackFromRgba32(new Rgba32((byte)vector.X, (byte)vector.Y, (byte)vector.Z, byte.MaxValue)); + pixel.FromRgba32(new Rgba32((byte)vector.X, (byte)vector.Y, (byte)vector.Z, byte.MaxValue)); palette[index] = pixel; // Consume the next palette index @@ -525,18 +522,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// The pixel data. /// The level. - /// The color to map to. /// /// The representing the index of the pixel in the palette. /// [MethodImpl(MethodImplOptions.NoInlining)] - public int GetPaletteIndex(ref TPixel pixel, int level, ref Rgba32 rgba) + public int GetPaletteIndex(ref TPixel pixel, int level) { int index = this.paletteIndex; if (!this.leaf) { int shift = 7 - level; + Rgba32 rgba = default; pixel.ToRgba32(ref rgba); int pixelIndex = ((rgba.B & Mask[level]) >> (shift - 2)) @@ -546,7 +543,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization OctreeNode child = this.children[pixelIndex]; if (child != null) { - index = child.GetPaletteIndex(ref pixel, level + 1, ref rgba); + index = child.GetPaletteIndex(ref pixel, level + 1); } else { @@ -561,10 +558,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// Increment the pixel count and add to the color information /// /// The pixel to add. - /// The color to map to. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Increment(ref TPixel pixel, ref Rgba32 rgba) + public void Increment(ref TPixel pixel) { + Rgba32 rgba = default; pixel.ToRgba32(ref rgba); this.pixelCount++; this.red += rgba.R; diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs index 22bb5223f0..d49023886b 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer.cs @@ -15,11 +15,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// public class OctreeQuantizer : IQuantizer { - /// - /// The default maximum number of colors to use when quantizing the image. - /// - public const int DefaultMaxColors = 256; - /// /// Initializes a new instance of the class. /// @@ -42,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Whether to apply dithering to the output image public OctreeQuantizer(bool dither) - : this(GetDiffuser(dither), DefaultMaxColors) + : this(GetDiffuser(dither), QuantizerConstants.MaxColors) { } @@ -51,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// The error diffusion algorithm, if any, to apply to the output image public OctreeQuantizer(IErrorDiffuser diffuser) - : this(diffuser, DefaultMaxColors) + : this(diffuser, QuantizerConstants.MaxColors) { } @@ -63,7 +58,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization public OctreeQuantizer(IErrorDiffuser diffuser, int maxColors) { this.Diffuser = diffuser; - this.MaxColors = maxColors.Clamp(1, DefaultMaxColors); + this.MaxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors); } /// @@ -74,16 +69,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// public int MaxColors { get; } + /// /// - public IFrameQuantizer CreateFrameQuantizer() + public IFrameQuantizer CreateFrameQuantizer(Configuration configuration) where TPixel : struct, IPixel => new OctreeFrameQuantizer(this); /// - public IFrameQuantizer CreateFrameQuantizer(int maxColors) + public IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) where TPixel : struct, IPixel { - maxColors = maxColors.Clamp(1, DefaultMaxColors); + maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors); return new OctreeFrameQuantizer(this, maxColors); } diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs index cdf3514e2d..f8a19f8c40 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; @@ -23,26 +22,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// private readonly TPixel[] palette; - /// - /// The vector representation of the image palette. - /// - private readonly Vector4[] paletteVector; - /// /// Initializes a new instance of the class. /// /// The palette quantizer. /// An array of all colors in the palette. - public PaletteFrameQuantizer(PaletteQuantizer quantizer, TPixel[] colors) - : base(quantizer, true) - { - // TODO: Why is this value constrained? Gif has limitations but theoretically - // we might want to reduce the palette of an image to greater than that limitation. - Guard.MustBeBetweenOrEqualTo(colors.Length, 1, 256, nameof(colors)); - this.palette = colors; - this.paletteVector = new Vector4[this.palette.Length]; - PixelOperations.Instance.ToScaledVector4(this.palette, this.paletteVector, this.palette.Length); - } + public PaletteFrameQuantizer(IQuantizer quantizer, TPixel[] colors) + : base(quantizer, true) => this.palette = colors; /// protected override void SecondPass( diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs index 27ef05dfe9..6b2be3d038 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs @@ -8,18 +8,18 @@ using SixLabors.ImageSharp.Processing.Processors.Dithering; namespace SixLabors.ImageSharp.Processing.Processors.Quantization { /// - /// Allows the quantization of images pixels using web safe colors defined in the CSS Color Module Level 4. - /// Override this class to provide your own palette. + /// Allows the quantization of images pixels using color palettes. + /// Override this class to provide your own palette. /// - /// By default the quantizer uses dithering and the + /// By default the quantizer uses dithering. /// /// - public class PaletteQuantizer : IQuantizer + public abstract class PaletteQuantizer : IQuantizer { /// /// Initializes a new instance of the class. /// - public PaletteQuantizer() + protected PaletteQuantizer() : this(true) { } @@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// Initializes a new instance of the class. /// /// Whether to apply dithering to the output image - public PaletteQuantizer(bool dither) + protected PaletteQuantizer(bool dither) : this(GetDiffuser(dither)) { } @@ -37,41 +37,40 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// Initializes a new instance of the class. /// /// The error diffusion algorithm, if any, to apply to the output image - public PaletteQuantizer(IErrorDiffuser diffuser) => this.Diffuser = diffuser; + protected PaletteQuantizer(IErrorDiffuser diffuser) => this.Diffuser = diffuser; /// public IErrorDiffuser Diffuser { get; } /// - public virtual IFrameQuantizer CreateFrameQuantizer() - where TPixel : struct, IPixel - => this.CreateFrameQuantizer(() => NamedColors.WebSafePalette); + public abstract IFrameQuantizer CreateFrameQuantizer(Configuration configuration) + where TPixel : struct, IPixel; /// - public IFrameQuantizer CreateFrameQuantizer(int maxColors) + public abstract IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) + where TPixel : struct, IPixel; + + /// + /// Creates the generic frame quantizer. + /// + /// The pixel format. + /// The to configure internal operations. + /// The color palette. + /// The maximum number of colors to hold in the color palette. + /// The + protected IFrameQuantizer CreateFrameQuantizer(Configuration configuration, TPixel[] palette, int maxColors) where TPixel : struct, IPixel { - TPixel[] websafe = NamedColors.WebSafePalette; - int max = Math.Min(maxColors, websafe.Length); + int max = Math.Min(QuantizerConstants.MaxColors, Math.Min(maxColors, palette.Length)); - if (max != websafe.Length) + if (max != palette.Length) { - return this.CreateFrameQuantizer(() => NamedColors.WebSafePalette.AsSpan(0, max).ToArray()); + return new PaletteFrameQuantizer(this, palette.AsSpan(0, max).ToArray()); } - return this.CreateFrameQuantizer(() => websafe); + return new PaletteFrameQuantizer(this, palette); } - /// - /// Gets the palette to use to quantize the image. - /// - /// The pixel format. - /// The method to return the palette. - /// The - public IFrameQuantizer CreateFrameQuantizer(Func paletteFunction) - where TPixel : struct, IPixel - => new PaletteFrameQuantizer(this, paletteFunction.Invoke()); - private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null; } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs new file mode 100644 index 0000000000..a350adfc0c --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs @@ -0,0 +1,110 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Dithering; + +namespace SixLabors.ImageSharp.Processing.Processors.Quantization +{ + /// + /// A generic palette quantizer. + /// + /// The pixel format. + public class PaletteQuantizer : IQuantizer + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The color palette to use. + public PaletteQuantizer(TPixel[] palette) + : this(palette, true) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The color palette to use. + /// Whether to apply dithering to the output image + public PaletteQuantizer(TPixel[] palette, bool dither) + : this(palette, GetDiffuser(dither)) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The color palette to use. + /// The error diffusion algorithm, if any, to apply to the output image + public PaletteQuantizer(TPixel[] palette, IErrorDiffuser diffuser) + { + Guard.MustBeBetweenOrEqualTo(palette.Length, QuantizerConstants.MinColors, QuantizerConstants.MaxColors, nameof(palette)); + this.Palette = palette; + this.Diffuser = diffuser; + } + + /// + public IErrorDiffuser Diffuser { get; } + + /// + /// Gets the palette. + /// + public TPixel[] Palette { get; } + + /// + /// Creates the generic frame quantizer. + /// + /// The to configure internal operations. + /// The . + public IFrameQuantizer CreateFrameQuantizer(Configuration configuration) + => ((IQuantizer)this).CreateFrameQuantizer(configuration); + + /// + /// Creates the generic frame quantizer. + /// + /// The to configure internal operations. + /// The maximum number of colors to hold in the color palette. + /// The . + public IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) + => ((IQuantizer)this).CreateFrameQuantizer(configuration, maxColors); + + /// + IFrameQuantizer IQuantizer.CreateFrameQuantizer(Configuration configuration) + { + if (!typeof(TPixel).Equals(typeof(TPixel1))) + { + throw new InvalidOperationException("Generic method type must be the same as class type."); + } + + TPixel[] paletteRef = this.Palette; + return new PaletteFrameQuantizer(this, Unsafe.As(ref paletteRef)); + } + + /// + IFrameQuantizer IQuantizer.CreateFrameQuantizer(Configuration configuration, int maxColors) + { + if (!typeof(TPixel).Equals(typeof(TPixel1))) + { + throw new InvalidOperationException("Generic method type must be the same as class type."); + } + + TPixel[] paletteRef = this.Palette; + TPixel1[] castPalette = Unsafe.As(ref paletteRef); + + maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors); + int max = Math.Min(maxColors, castPalette.Length); + + if (max != castPalette.Length) + { + return new PaletteFrameQuantizer(this, castPalette.AsSpan(0, max).ToArray()); + } + + return new PaletteFrameQuantizer(this, castPalette); + } + + private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null; + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs index bd5a6e9ec7..8da89bf94a 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - IFrameQuantizer executor = this.Quantizer.CreateFrameQuantizer(); + IFrameQuantizer executor = this.Quantizer.CreateFrameQuantizer(configuration); using (QuantizedFrame quantized = executor.QuantizeFrame(source)) { int paletteCount = quantized.Palette.Length - 1; diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs index 2e3bb2c419..38862ef446 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizedFrame{TPixel}.cs @@ -3,7 +3,7 @@ using System; using System.Buffers; - +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; @@ -57,8 +57,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// Gets the pixels of this . /// /// The + [MethodImpl(InliningOptions.ShortMethod)] public Span GetPixelSpan() => this.pixels.GetSpan(); + /// + /// Gets the representation of the pixels as a of contiguous memory + /// at row beginning from the the first pixel on that row. + /// + /// The row. + /// The + [MethodImpl(InliningOptions.ShortMethod)] + public Span GetRowSpan(int rowIndex) => this.GetPixelSpan().Slice(rowIndex * this.Width, this.Width); + /// public void Dispose() { diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizerConstants.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizerConstants.cs new file mode 100644 index 0000000000..d79a91c301 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizerConstants.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Processing.Processors.Quantization +{ + /// + /// Contains color quantization specific constants. + /// + internal static class QuantizerConstants + { + /// + /// The minimum number of colors to use when quantizing an image. + /// + public const int MinColors = 1; + + /// + /// The maximum number of colors to use when quantizing an image. + /// + public const int MaxColors = 256; + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs new file mode 100644 index 0000000000..93630a9166 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Dithering; + +namespace SixLabors.ImageSharp.Processing.Processors.Quantization +{ + /// + /// A palette quantizer consisting of web safe colors as defined in the CSS Color Module Level 4. + /// + public class WebSafePaletteQuantizer : PaletteQuantizer + { + /// + /// Initializes a new instance of the class. + /// + public WebSafePaletteQuantizer() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Whether to apply dithering to the output image + public WebSafePaletteQuantizer(bool dither) + : base(dither) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The error diffusion algorithm, if any, to apply to the output image + public WebSafePaletteQuantizer(IErrorDiffuser diffuser) + : base(diffuser) + { + } + + /// + public override IFrameQuantizer CreateFrameQuantizer(Configuration configuration) + => this.CreateFrameQuantizer(configuration, NamedColors.WebSafePalette.Length); + + /// + public override IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) + => this.CreateFrameQuantizer(configuration, NamedColors.WebSafePalette, maxColors); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs new file mode 100644 index 0000000000..2ff9f5090c --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Dithering; + +namespace SixLabors.ImageSharp.Processing.Processors.Quantization +{ + /// + /// A palette quantizer consisting of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821. + /// The hex codes were collected and defined by Nicholas Rougeux + /// + public class WernerPaletteQuantizer : PaletteQuantizer + { + /// + /// Initializes a new instance of the class. + /// + public WernerPaletteQuantizer() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Whether to apply dithering to the output image + public WernerPaletteQuantizer(bool dither) + : base(dither) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The error diffusion algorithm, if any, to apply to the output image + public WernerPaletteQuantizer(IErrorDiffuser diffuser) + : base(diffuser) + { + } + + /// + public override IFrameQuantizer CreateFrameQuantizer(Configuration configuration) + => this.CreateFrameQuantizer(configuration, NamedColors.WernerPalette.Length); + + /// + public override IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) + => this.CreateFrameQuantizer(configuration, NamedColors.WernerPalette, maxColors); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs index 13bc057da8..44df226cfd 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs @@ -173,6 +173,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } } + internal TPixel[] AotGetPalette() => this.GetPalette(); + /// protected override TPixel[] GetPalette() { @@ -199,7 +201,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization float a = Volume(ref this.colorCube[k], vmaSpan); ref TPixel color = ref this.palette[k]; - color.PackFromScaledVector4(new Vector4(r, g, b, a) / weight / 255F); + color.FromScaledVector4(new Vector4(r, g, b, a) / weight / 255F); } } } @@ -442,33 +444,36 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization // Build up the 3-D color histogram // Loop through each row - for (int y = 0; y < height; y++) + using (IMemoryOwner rgbaBuffer = source.MemoryAllocator.Allocate(source.Width)) { - Span row = source.GetPixelRowSpan(y); - ref TPixel scanBaseRef = ref MemoryMarshal.GetReference(row); - - // And loop through each column - Rgba32 rgba = default; - for (int x = 0; x < width; x++) + for (int y = 0; y < height; y++) { - ref TPixel pixel = ref Unsafe.Add(ref scanBaseRef, x); - pixel.ToRgba32(ref rgba); + Span row = source.GetPixelRowSpan(y); + Span rgbaSpan = rgbaBuffer.GetSpan(); + PixelOperations.Instance.ToRgba32(source.Configuration, row, rgbaSpan); + ref Rgba32 scanBaseRef = ref MemoryMarshal.GetReference(rgbaSpan); + + // And loop through each column + for (int x = 0; x < width; x++) + { + ref Rgba32 rgba = ref Unsafe.Add(ref scanBaseRef, x); - int r = rgba.R >> (8 - IndexBits); - int g = rgba.G >> (8 - IndexBits); - int b = rgba.B >> (8 - IndexBits); - int a = rgba.A >> (8 - IndexAlphaBits); + int r = rgba.R >> (8 - IndexBits); + int g = rgba.G >> (8 - IndexBits); + int b = rgba.B >> (8 - IndexBits); + int a = rgba.A >> (8 - IndexAlphaBits); - int index = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1); + int index = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1); - vwtSpan[index]++; - vmrSpan[index] += rgba.R; - vmgSpan[index] += rgba.G; - vmbSpan[index] += rgba.B; - vmaSpan[index] += rgba.A; + vwtSpan[index]++; + vmrSpan[index] += rgba.R; + vmgSpan[index] += rgba.G; + vmbSpan[index] += rgba.B; + vmaSpan[index] += rgba.A; - var vector = new Vector4(rgba.R, rgba.G, rgba.B, rgba.A); - m2Span[index] += Vector4.Dot(vector, vector); + var vector = new Vector4(rgba.R, rgba.G, rgba.B, rgba.A); + m2Span[index] += Vector4.Dot(vector, vector); + } } } } diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs index 5123e737d3..eb8b0fec91 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuQuantizer.cs @@ -14,11 +14,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// public class WuQuantizer : IQuantizer { - /// - /// The default maximum number of colors to use when quantizing the image. - /// - public const int DefaultMaxColors = 256; - /// /// Initializes a new instance of the class. /// @@ -41,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Whether to apply dithering to the output image public WuQuantizer(bool dither) - : this(GetDiffuser(dither), DefaultMaxColors) + : this(GetDiffuser(dither), QuantizerConstants.MaxColors) { } @@ -50,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// The error diffusion algorithm, if any, to apply to the output image public WuQuantizer(IErrorDiffuser diffuser) - : this(diffuser, DefaultMaxColors) + : this(diffuser, QuantizerConstants.MaxColors) { } @@ -62,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization public WuQuantizer(IErrorDiffuser diffuser, int maxColors) { this.Diffuser = diffuser; - this.MaxColors = maxColors.Clamp(1, DefaultMaxColors); + this.MaxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors); } /// @@ -73,16 +68,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// public int MaxColors { get; } + /// /// - public IFrameQuantizer CreateFrameQuantizer() + public IFrameQuantizer CreateFrameQuantizer(Configuration configuration) where TPixel : struct, IPixel => new WuFrameQuantizer(this); /// - public IFrameQuantizer CreateFrameQuantizer(int maxColors) + public IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) where TPixel : struct, IPixel { - maxColors = maxColors.Clamp(1, DefaultMaxColors); + maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors); return new WuFrameQuantizer(this, maxColors); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs index a7e1589259..5a3b5943b7 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs @@ -5,13 +5,9 @@ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -20,29 +16,35 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// Provides the base methods to perform affine transforms on an image. /// /// The pixel format. - internal class AffineTransformProcessor : InterpolatedTransformProcessorBase + internal class AffineTransformProcessor : TransformProcessorBase where TPixel : struct, IPixel { /// /// Initializes a new instance of the class. /// - /// The transform matrix + /// The transform matrix. /// The sampler to perform the transform operation. - /// The target dimensions to constrain the transformed image to. + /// The target dimensions. public AffineTransformProcessor(Matrix3x2 matrix, IResampler sampler, Size targetDimensions) - : base(sampler) { + Guard.NotNull(sampler, nameof(sampler)); + this.Sampler = sampler; this.TransformMatrix = matrix; this.TargetDimensions = targetDimensions; } /// - /// Gets the matrix used to supply the affine transform + /// Gets the sampler to perform interpolation of the transform operation. + /// + public IResampler Sampler { get; } + + /// + /// Gets the matrix used to supply the affine transform. /// public Matrix3x2 TransformMatrix { get; } /// - /// Gets the target dimensions to constrain the transformed image to + /// Gets the target dimensions to constrain the transformed image to. /// public Size TargetDimensions { get; } @@ -64,34 +66,38 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Rectangle sourceRectangle, Configuration configuration) { - int height = this.TargetDimensions.Height; - int width = this.TargetDimensions.Width; - - Rectangle sourceBounds = source.Bounds(); - var targetBounds = new Rectangle(0, 0, width, height); + // Handle tranforms that result in output identical to the original. + if (this.TransformMatrix.Equals(default) || this.TransformMatrix.Equals(Matrix3x2.Identity)) + { + // The clone will be blank here copy all the pixel data over + source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); + return; + } - // Since could potentially be resizing the canvas we might need to re-calculate the matrix - Matrix3x2 matrix = this.GetProcessingMatrix(sourceBounds, targetBounds); + int width = this.TargetDimensions.Width; + var targetBounds = new Rectangle(Point.Empty, this.TargetDimensions); // Convert from screen to world space. - Matrix3x2.Invert(matrix, out matrix); + Matrix3x2.Invert(this.TransformMatrix, out Matrix3x2 matrix); if (this.Sampler is NearestNeighborResampler) { - ParallelFor.WithConfiguration( - 0, - height, + ParallelHelper.IterateRows( + targetBounds, configuration, - y => + rows => { - Span destRow = destination.GetPixelRowSpan(y); - - for (int x = 0; x < width; x++) + for (int y = rows.Min; y < rows.Max; y++) { - var point = Point.Transform(new Point(x, y), matrix); - if (sourceBounds.Contains(point.X, point.Y)) + Span destRow = destination.GetPixelRowSpan(y); + + for (int x = 0; x < width; x++) { - destRow[x] = source[point.X, point.Y]; + var point = Point.Transform(new Point(x, y), matrix); + if (sourceRectangle.Contains(point.X, point.Y)) + { + destRow[x] = source[point.X, point.Y]; + } } } }); @@ -99,117 +105,38 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return; } - int maxSourceX = source.Width - 1; - int maxSourceY = source.Height - 1; - (float radius, float scale, float ratio) xRadiusScale = this.GetSamplingRadius(source.Width, destination.Width); - (float radius, float scale, float ratio) yRadiusScale = this.GetSamplingRadius(source.Height, destination.Height); - float xScale = xRadiusScale.scale; - float yScale = yRadiusScale.scale; - var radius = new Vector2(xRadiusScale.radius, yRadiusScale.radius); - IResampler sampler = this.Sampler; - var maxSource = new Vector4(maxSourceX, maxSourceY, maxSourceX, maxSourceY); - int xLength = (int)MathF.Ceiling((radius.X * 2) + 2); - int yLength = (int)MathF.Ceiling((radius.Y * 2) + 2); - - MemoryAllocator memoryAllocator = configuration.MemoryAllocator; - - using (Buffer2D yBuffer = memoryAllocator.Allocate2D(yLength, height)) - using (Buffer2D xBuffer = memoryAllocator.Allocate2D(xLength, height)) + var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.Sampler); + try { - ParallelFor.WithConfiguration( - 0, - height, + ParallelHelper.IterateRowsWithTempBuffer( + targetBounds, configuration, - y => + (rows, vectorBuffer) => + { + Span vectorSpan = vectorBuffer.Span; + for (int y = rows.Min; y < rows.Max; y++) { - ref TPixel destRowRef = ref MemoryMarshal.GetReference(destination.GetPixelRowSpan(y)); - ref float ySpanRef = ref MemoryMarshal.GetReference(yBuffer.GetRowSpan(y)); - ref float xSpanRef = ref MemoryMarshal.GetReference(xBuffer.GetRowSpan(y)); + Span targetRowSpan = destination.GetPixelRowSpan(y); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); + ref float ySpanRef = ref kernel.GetYStartReference(y); + ref float xSpanRef = ref kernel.GetXStartReference(y); for (int x = 0; x < width; x++) { // Use the single precision position to calculate correct bounding pixels // otherwise we get rogue pixels outside of the bounds. var point = Vector2.Transform(new Vector2(x, y), matrix); - - // Clamp sampling pixel radial extents to the source image edges - Vector2 maxXY = point + radius; - Vector2 minXY = point - radius; - - // max, maxY, minX, minY - var extents = new Vector4( - MathF.Floor(maxXY.X + .5F), - MathF.Floor(maxXY.Y + .5F), - MathF.Ceiling(minXY.X - .5F), - MathF.Ceiling(minXY.Y - .5F)); - - int right = (int)extents.X; - int bottom = (int)extents.Y; - int left = (int)extents.Z; - int top = (int)extents.W; - - extents = Vector4.Clamp(extents, Vector4.Zero, maxSource); - - int maxX = (int)extents.X; - int maxY = (int)extents.Y; - int minX = (int)extents.Z; - int minY = (int)extents.W; - - if (minX == maxX || minY == maxY) - { - continue; - } - - // It appears these have to be calculated on-the-fly. - // Precalulating transformed weights would require prior knowledge of every transformed pixel location - // since they can be at sub-pixel positions on both axis. - // I've optimized where I can but am always open to suggestions. - if (yScale > 1 && xScale > 1) - { - CalculateWeightsDown(top, bottom, minY, maxY, point.Y, sampler, yScale, ref ySpanRef, yLength); - CalculateWeightsDown(left, right, minX, maxX, point.X, sampler, xScale, ref xSpanRef, xLength); - } - else - { - CalculateWeightsScaleUp(minY, maxY, point.Y, sampler, ref ySpanRef); - CalculateWeightsScaleUp(minX, maxX, point.X, sampler, ref xSpanRef); - } - - // Now multiply the results against the offsets - Vector4 sum = Vector4.Zero; - for (int yy = 0, j = minY; j <= maxY; j++, yy++) - { - float yWeight = Unsafe.Add(ref ySpanRef, yy); - - for (int xx = 0, i = minX; i <= maxX; i++, xx++) - { - float xWeight = Unsafe.Add(ref xSpanRef, xx); - var vector = source[i, j].ToVector4(); - - // Values are first premultiplied to prevent darkening of edge pixels - Vector4 multiplied = vector.Premultiply(); - sum += multiplied * xWeight * yWeight; - } - } - - ref TPixel dest = ref Unsafe.Add(ref destRowRef, x); - - // Reverse the premultiplication - dest.PackFromVector4(sum.UnPremultiply()); + kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan); } - }); + + PixelOperations.Instance.FromVector4(configuration, vectorSpan, targetRowSpan); + } + }); + } + finally + { + kernel.Dispose(); } } - - /// - /// Gets a transform matrix adjusted for final processing based upon the target image bounds. - /// - /// The source image bounds. - /// The destination image bounds. - /// - /// The . - /// - protected virtual Matrix3x2 GetProcessingMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle) - => this.TransformMatrix; } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs deleted file mode 100644 index adaee17665..0000000000 --- a/src/ImageSharp/Processing/Processors/Transforms/CenteredAffineTransformProcessor.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing.Processors.Transforms -{ - /// - /// A base class that provides methods to allow the automatic centering of affine transforms - /// - /// The pixel format. - internal abstract class CenteredAffineTransformProcessor : AffineTransformProcessor - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The transform matrix - /// The sampler to perform the transform operation. - /// The source image size - protected CenteredAffineTransformProcessor(Matrix3x2 matrix, IResampler sampler, Size sourceSize) - : base(matrix, sampler, GetTransformedDimensions(sourceSize, matrix)) - { - } - - /// - protected override Matrix3x2 GetProcessingMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle) - => TransformHelpers.GetCenteredTransformMatrix(sourceRectangle, destinationRectangle, this.TransformMatrix); - - private static Size GetTransformedDimensions(Size sourceDimensions, Matrix3x2 matrix) - { - var sourceRectangle = new Rectangle(0, 0, sourceDimensions.Width, sourceDimensions.Height); - return TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, matrix).Size; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs deleted file mode 100644 index 962b9e4c9d..0000000000 --- a/src/ImageSharp/Processing/Processors/Transforms/CenteredProjectiveTransformProcessor.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing.Processors.Transforms -{ - /// - /// A base class that provides methods to allow the automatic centering of non-affine transforms - /// - /// The pixel format. - internal abstract class CenteredProjectiveTransformProcessor : ProjectiveTransformProcessor - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The transform matrix - /// The sampler to perform the transform operation. - /// The source image size - protected CenteredProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Size sourceSize) - : base(matrix, sampler, GetTransformedDimensions(sourceSize, matrix)) - { - } - - /// - protected override Matrix4x4 GetProcessingMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle) - { - return TransformHelpers.GetCenteredTransformMatrix(sourceRectangle, destinationRectangle, this.TransformMatrix); - } - - private static Size GetTransformedDimensions(Size sourceDimensions, Matrix4x4 matrix) - { - var sourceRectangle = new Rectangle(0, 0, sourceDimensions.Width, sourceDimensions.Height); - return TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, matrix).Size; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index df1ac32746..3b1d7e94dd 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs @@ -4,8 +4,8 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -22,8 +22,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// Initializes a new instance of the class. /// /// The target cropped rectangle. - public CropProcessor(Rectangle cropRectangle) + /// The source image size. + public CropProcessor(Rectangle cropRectangle, Size sourceSize) { + // Check bounds here and throw if we are passed a rectangle exceeding our source bounds. + Guard.IsTrue(new Rectangle(Point.Empty, sourceSize).Contains(cropRectangle), nameof(cropRectangle), "Crop rectangle should be smaller than the source bounds."); this.CropRectangle = cropRectangle; } @@ -53,21 +56,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return; } - int minY = Math.Max(this.CropRectangle.Y, sourceRectangle.Y); - int maxY = Math.Min(this.CropRectangle.Bottom, sourceRectangle.Bottom); - int minX = Math.Max(this.CropRectangle.X, sourceRectangle.X); - int maxX = Math.Min(this.CropRectangle.Right, sourceRectangle.Right); + Rectangle rect = this.CropRectangle; - ParallelFor.WithConfiguration( - minY, - maxY, - configuration, - y => - { - Span sourceRow = source.GetPixelRowSpan(y).Slice(minX); - Span targetRow = destination.GetPixelRowSpan(y - minY); - sourceRow.Slice(0, maxX - minX).CopyTo(targetRow); - }); + // Copying is cheap, we should process more pixels per task: + ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); + + ParallelHelper.IterateRows( + rect, + parallelSettings, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = source.GetPixelRowSpan(y).Slice(rect.Left); + Span targetRow = destination.GetPixelRowSpan(y - rect.Top); + sourceRow.Slice(0, rect.Width).CopyTo(targetRow); + } + }); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs index 8eeae5d1fc..6de717afd9 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); } - new CropProcessor(rectangle).Apply(source, sourceRectangle); + new CropProcessor(rectangle, source.Size()).Apply(source, sourceRectangle); } /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs index cea6df391f..c6f5e9d7b8 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs @@ -2,9 +2,11 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; @@ -55,27 +57,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private void FlipX(ImageFrame source, Configuration configuration) { int height = source.Height; - int halfHeight = (int)Math.Ceiling(source.Height * .5F); - using (Buffer2D targetPixels = configuration.MemoryAllocator.Allocate2D(source.Size())) + using (IMemoryOwner tempBuffer = configuration.MemoryAllocator.Allocate(source.Width)) { - ParallelFor.WithConfiguration( - 0, - halfHeight, - configuration, - y => - { - int newY = height - y - 1; - Span sourceRow = source.GetPixelRowSpan(y); - Span altSourceRow = source.GetPixelRowSpan(newY); - Span targetRow = targetPixels.GetRowSpan(y); - Span altTargetRow = targetPixels.GetRowSpan(newY); - - sourceRow.CopyTo(altTargetRow); - altSourceRow.CopyTo(targetRow); - }); + Span temp = tempBuffer.Memory.Span; - Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); + for (int yTop = 0; yTop < height / 2; yTop++) + { + int yBottom = height - yTop - 1; + Span topRow = source.GetPixelRowSpan(yBottom); + Span bottomRow = source.GetPixelRowSpan(yTop); + topRow.CopyTo(temp); + bottomRow.CopyTo(topRow); + temp.CopyTo(bottomRow); + } } } @@ -86,31 +81,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The configuration. private void FlipY(ImageFrame source, Configuration configuration) { - int width = source.Width; - int height = source.Height; - int halfWidth = (int)Math.Ceiling(width * .5F); - - using (Buffer2D targetPixels = configuration.MemoryAllocator.Allocate2D(source.Size())) - { - ParallelFor.WithConfiguration( - 0, - height, - configuration, - y => + ParallelHelper.IterateRows( + source.Bounds(), + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) { - Span sourceRow = source.GetPixelRowSpan(y); - Span targetRow = targetPixels.GetRowSpan(y); - - for (int x = 0; x < halfWidth; x++) - { - int newX = width - x - 1; - targetRow[x] = sourceRow[newX]; - targetRow[newX] = sourceRow[x]; - } - }); - - Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); - } + source.GetPixelRowSpan(y).Reverse(); + } + }); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/InterpolatedTransformProcessorBase.cs b/src/ImageSharp/Processing/Processors/Transforms/InterpolatedTransformProcessorBase.cs deleted file mode 100644 index c1abb4a5e1..0000000000 --- a/src/ImageSharp/Processing/Processors/Transforms/InterpolatedTransformProcessorBase.cs +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Processing.Processors.Transforms -{ - /// - /// The base class for performing interpolated affine and non-affine transforms. - /// - /// The pixel format. - internal abstract class InterpolatedTransformProcessorBase : TransformProcessorBase - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The sampler to perform the transform operation. - protected InterpolatedTransformProcessorBase(IResampler sampler) - { - Guard.NotNull(sampler, nameof(sampler)); - this.Sampler = sampler; - } - - /// - /// Gets the sampler to perform interpolation of the transform operation. - /// - public IResampler Sampler { get; } - - /// - /// Calculated the weights for the given point. - /// This method uses more samples than the upscaled version to ensure edge pixels are correctly rendered. - /// Additionally the weights are normalized. - /// - /// The minimum sampling offset - /// The maximum sampling offset - /// The minimum source bounds - /// The maximum source bounds - /// The transformed point dimension - /// The sampler - /// The transformed image scale relative to the source - /// The reference to the collection of weights - /// The length of the weights collection - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected static void CalculateWeightsDown(int min, int max, int sourceMin, int sourceMax, float point, IResampler sampler, float scale, ref float weightsRef, int length) - { - float sum = 0; - - // Downsampling weights requires more edge sampling plus normalization of the weights - for (int x = 0, i = min; i <= max; i++, x++) - { - int index = i; - if (index < sourceMin) - { - index = sourceMin; - } - - if (index > sourceMax) - { - index = sourceMax; - } - - float weight = sampler.GetValue((index - point) / scale); - sum += weight; - Unsafe.Add(ref weightsRef, x) = weight; - } - - if (sum > 0) - { - for (int i = 0; i < length; i++) - { - ref float wRef = ref Unsafe.Add(ref weightsRef, i); - wRef = wRef / sum; - } - } - } - - /// - /// Calculated the weights for the given point. - /// - /// The minimum source bounds - /// The maximum source bounds - /// The transformed point dimension - /// The sampler - /// The reference to the collection of weights - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected static void CalculateWeightsScaleUp(int sourceMin, int sourceMax, float point, IResampler sampler, ref float weightsRef) - { - for (int x = 0, i = sourceMin; i <= sourceMax; i++, x++) - { - float weight = sampler.GetValue(i - point); - Unsafe.Add(ref weightsRef, x) = weight; - } - } - - /// - /// Calculates the sampling radius for the current sampler - /// - /// The source dimension size - /// The destination dimension size - /// The radius, and scaling factor - protected (float radius, float scale, float ratio) GetSamplingRadius(int sourceSize, int destinationSize) - { - float ratio = (float)sourceSize / destinationSize; - float scale = ratio; - - if (scale < 1F) - { - scale = 1F; - } - - return (MathF.Ceiling(scale * this.Sampler.Radius), scale, ratio); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs index 15816cb4dd..98d488a420 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -5,13 +5,9 @@ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -20,22 +16,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// Provides the base methods to perform non-affine transforms on an image. /// /// The pixel format. - internal class ProjectiveTransformProcessor : InterpolatedTransformProcessorBase + internal class ProjectiveTransformProcessor : TransformProcessorBase where TPixel : struct, IPixel { /// /// Initializes a new instance of the class. /// - /// The transform matrix + /// The transform matrix. /// The sampler to perform the transform operation. - /// The target dimensions to constrain the transformed image to. + /// The target dimensions. public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Size targetDimensions) - : base(sampler) { + Guard.NotNull(sampler, nameof(sampler)); + this.Sampler = sampler; this.TransformMatrix = matrix; this.TargetDimensions = targetDimensions; } + /// + /// Gets the sampler to perform interpolation of the transform operation. + /// + public IResampler Sampler { get; } + /// /// Gets the matrix used to supply the projective transform /// @@ -60,167 +62,85 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) { - int height = this.TargetDimensions.Height; - int width = this.TargetDimensions.Width; - - Rectangle sourceBounds = source.Bounds(); - var targetBounds = new Rectangle(0, 0, width, height); + // Handle tranforms that result in output identical to the original. + if (this.TransformMatrix.Equals(default) || this.TransformMatrix.Equals(Matrix4x4.Identity)) + { + // The clone will be blank here copy all the pixel data over + source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); + return; + } - // Since could potentially be resizing the canvas we might need to re-calculate the matrix - Matrix4x4 matrix = this.GetProcessingMatrix(sourceBounds, targetBounds); + int width = this.TargetDimensions.Width; + var targetBounds = new Rectangle(Point.Empty, this.TargetDimensions); // Convert from screen to world space. - Matrix4x4.Invert(matrix, out matrix); + Matrix4x4.Invert(this.TransformMatrix, out Matrix4x4 matrix); const float Epsilon = 0.0000001F; if (this.Sampler is NearestNeighborResampler) { - ParallelFor.WithConfiguration( - 0, - height, + ParallelHelper.IterateRows( + targetBounds, configuration, - y => - { - Span destRow = destination.GetPixelRowSpan(y); - - for (int x = 0; x < width; x++) + rows => { - var v3 = Vector3.Transform(new Vector3(x, y, 1), matrix); + for (int y = rows.Min; y < rows.Max; y++) + { + Span destRow = destination.GetPixelRowSpan(y); - float z = MathF.Max(v3.Z, Epsilon); - int px = (int)MathF.Round(v3.X / z); - int py = (int)MathF.Round(v3.Y / z); + for (int x = 0; x < width; x++) + { + var v3 = Vector3.Transform(new Vector3(x, y, 1), matrix); - if (sourceBounds.Contains(px, py)) - { - destRow[x] = source[px, py]; + float z = MathF.Max(v3.Z, Epsilon); + int px = (int)MathF.Round(v3.X / z); + int py = (int)MathF.Round(v3.Y / z); + + if (sourceRectangle.Contains(px, py)) + { + destRow[x] = source[px, py]; + } + } } - } - }); + }); return; } - int maxSourceX = source.Width - 1; - int maxSourceY = source.Height - 1; - (float radius, float scale, float ratio) xRadiusScale = this.GetSamplingRadius(source.Width, destination.Width); - (float radius, float scale, float ratio) yRadiusScale = this.GetSamplingRadius(source.Height, destination.Height); - float xScale = xRadiusScale.scale; - float yScale = yRadiusScale.scale; - - // Using Vector4 with dummy 0-s, because Vector2 SIMD implementation is not reliable: - var radius = new Vector4(xRadiusScale.radius, yRadiusScale.radius, 0, 0); - - IResampler sampler = this.Sampler; - var maxSource = new Vector4(maxSourceX, maxSourceY, maxSourceX, maxSourceY); - int xLength = (int)MathF.Ceiling((radius.X * 2) + 2); - int yLength = (int)MathF.Ceiling((radius.Y * 2) + 2); - - MemoryAllocator memoryAllocator = configuration.MemoryAllocator; - - using (Buffer2D yBuffer = memoryAllocator.Allocate2D(yLength, height)) - using (Buffer2D xBuffer = memoryAllocator.Allocate2D(xLength, height)) + var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.Sampler); + try { - ParallelFor.WithConfiguration( - 0, - height, + ParallelHelper.IterateRowsWithTempBuffer( + targetBounds, configuration, - y => + (rows, vectorBuffer) => { - ref TPixel destRowRef = ref MemoryMarshal.GetReference(destination.GetPixelRowSpan(y)); - ref float ySpanRef = ref MemoryMarshal.GetReference(yBuffer.GetRowSpan(y)); - ref float xSpanRef = ref MemoryMarshal.GetReference(xBuffer.GetRowSpan(y)); - - for (int x = 0; x < width; x++) + Span vectorSpan = vectorBuffer.Span; + for (int y = rows.Min; y < rows.Max; y++) { - // Use the single precision position to calculate correct bounding pixels - // otherwise we get rogue pixels outside of the bounds. - var v3 = Vector3.Transform(new Vector3(x, y, 1), matrix); - float z = MathF.Max(v3.Z, Epsilon); - - // Using Vector4 with dummy 0-s, because Vector2 SIMD implementation is not reliable: - Vector4 point = new Vector4(v3.X, v3.Y, 0, 0) / z; - - // Clamp sampling pixel radial extents to the source image edges - Vector4 maxXY = point + radius; - Vector4 minXY = point - radius; - - // max, maxY, minX, minY - var extents = new Vector4( - MathF.Floor(maxXY.X + .5F), - MathF.Floor(maxXY.Y + .5F), - MathF.Ceiling(minXY.X - .5F), - MathF.Ceiling(minXY.Y - .5F)); - - int right = (int)extents.X; - int bottom = (int)extents.Y; - int left = (int)extents.Z; - int top = (int)extents.W; - - extents = Vector4.Clamp(extents, Vector4.Zero, maxSource); - - int maxX = (int)extents.X; - int maxY = (int)extents.Y; - int minX = (int)extents.Z; - int minY = (int)extents.W; - - if (minX == maxX || minY == maxY) - { - continue; - } - - // It appears these have to be calculated on-the-fly. - // Precalulating transformed weights would require prior knowledge of every transformed pixel location - // since they can be at sub-pixel positions on both axis. - // I've optimized where I can but am always open to suggestions. - if (yScale > 1 && xScale > 1) - { - CalculateWeightsDown(top, bottom, minY, maxY, point.Y, sampler, yScale, ref ySpanRef, yLength); - CalculateWeightsDown(left, right, minX, maxX, point.X, sampler, xScale, ref xSpanRef, xLength); - } - else - { - CalculateWeightsScaleUp(minY, maxY, point.Y, sampler, ref ySpanRef); - CalculateWeightsScaleUp(minX, maxX, point.X, sampler, ref xSpanRef); - } + Span targetRowSpan = destination.GetPixelRowSpan(y); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); + ref float ySpanRef = ref kernel.GetYStartReference(y); + ref float xSpanRef = ref kernel.GetXStartReference(y); - // Now multiply the results against the offsets - Vector4 sum = Vector4.Zero; - for (int yy = 0, j = minY; j <= maxY; j++, yy++) + for (int x = 0; x < width; x++) { - float yWeight = Unsafe.Add(ref ySpanRef, yy); - - for (int xx = 0, i = minX; i <= maxX; i++, xx++) - { - float xWeight = Unsafe.Add(ref xSpanRef, xx); - var vector = source[i, j].ToVector4(); + // Use the single precision position to calculate correct bounding pixels + // otherwise we get rogue pixels outside of the bounds. + var v3 = Vector3.Transform(new Vector3(x, y, 1F), matrix); + Vector2 point = new Vector2(v3.X, v3.Y) / MathF.Max(v3.Z, Epsilon); - // Values are first premultiplied to prevent darkening of edge pixels - Vector4 multiplied = vector.Premultiply(); - sum += multiplied * xWeight * yWeight; - } + kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan); } - ref TPixel dest = ref Unsafe.Add(ref destRowRef, x); - - // Reverse the premultiplication - dest.PackFromVector4(sum.UnPremultiply()); + PixelOperations.Instance.FromVector4(configuration, vectorSpan, targetRowSpan); } }); } - } - - /// - /// Gets a transform matrix adjusted for final processing based upon the target image bounds. - /// - /// The source image bounds. - /// The destination image bounds. - /// - /// The . - /// - protected virtual Matrix4x4 GetProcessingMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle) - { - return this.TransformMatrix; + finally + { + kernel.Dispose(); + } } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/BicubicResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Transforms/BicubicResampler.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/BoxResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Transforms/BoxResampler.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/CatmullRomResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/CatmullRomResampler.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Transforms/CatmullRomResampler.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/CatmullRomResampler.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/HermiteResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/HermiteResampler.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Transforms/HermiteResampler.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/HermiteResampler.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/Lanczos2Resampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos2Resampler.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Transforms/Lanczos2Resampler.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos2Resampler.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/Lanczos3Resampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos3Resampler.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Transforms/Lanczos3Resampler.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos3Resampler.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/Lanczos5Resampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos5Resampler.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Transforms/Lanczos5Resampler.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos5Resampler.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/Lanczos8Resampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos8Resampler.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Transforms/Lanczos8Resampler.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos8Resampler.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/MitchellNetravaliResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/MitchellNetravaliResampler.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Transforms/MitchellNetravaliResampler.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/MitchellNetravaliResampler.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/NearestNeighborResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Transforms/NearestNeighborResampler.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/RobidouxResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxResampler.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Transforms/RobidouxResampler.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxResampler.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/RobidouxSharpResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxSharpResampler.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Transforms/RobidouxSharpResampler.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxSharpResampler.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/SplineResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/SplineResampler.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Transforms/SplineResampler.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/SplineResampler.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/TriangleResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Transforms/TriangleResampler.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/WelchResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Transforms/WelchResampler.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs new file mode 100644 index 0000000000..f349634ac0 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs @@ -0,0 +1,94 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Points to a collection of of weights allocated in . + /// + internal readonly unsafe struct ResizeKernel + { + private readonly float* bufferPtr; + + /// + /// Initializes a new instance of the struct. + /// + [MethodImpl(InliningOptions.ShortMethod)] + internal ResizeKernel(int left, float* bufferPtr, int length) + { + this.Left = left; + this.bufferPtr = bufferPtr; + this.Length = length; + } + + /// + /// Gets the left index for the destination row + /// + public int Left { get; } + + /// + /// Gets the the length of the kernel + /// + public int Length { get; } + + /// + /// Gets the span representing the portion of the that this window covers + /// + /// The + /// + public Span Values + { + [MethodImpl(InliningOptions.ShortMethod)] + get => new Span(this.bufferPtr, this.Length); + } + + /// + /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance. + /// + /// The input span of vectors + /// The weighted sum + [MethodImpl(InliningOptions.ShortMethod)] + public Vector4 Convolve(Span rowSpan) + { + ref float horizontalValues = ref Unsafe.AsRef(this.bufferPtr); + int left = this.Left; + ref Vector4 vecPtr = ref Unsafe.Add(ref MemoryMarshal.GetReference(rowSpan), left); + + // Destination color components + Vector4 result = Vector4.Zero; + + for (int i = 0; i < this.Length; i++) + { + float weight = Unsafe.Add(ref horizontalValues, i); + Vector4 v = Unsafe.Add(ref vecPtr, i); + result += v * weight; + } + + return result; + } + + /// + /// Copy the contents of altering + /// to the value . + /// + internal ResizeKernel AlterLeftValue(int left) + { + return new ResizeKernel(left, this.bufferPtr, this.Length); + } + + internal void Fill(Span values) + { + DebugGuard.IsTrue(values.Length == this.Length, nameof(values), "ResizeKernel.Fill: values.Length != this.Length!"); + + for (int i = 0; i < this.Length; i++) + { + this.Values[i] = (float)values[i]; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs new file mode 100644 index 0000000000..4b81aaa64e --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs @@ -0,0 +1,81 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.Memory; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Contains + /// + internal partial class ResizeKernelMap + { + /// + /// Memory-optimized where repeating rows are stored only once. + /// + private sealed class PeriodicKernelMap : ResizeKernelMap + { + private readonly int period; + + private readonly int cornerInterval; + + public PeriodicKernelMap( + MemoryAllocator memoryAllocator, + IResampler sampler, + int sourceLength, + int destinationLength, + double ratio, + double scale, + int radius, + int period, + int cornerInterval) + : base( + memoryAllocator, + sampler, + sourceLength, + destinationLength, + (cornerInterval * 2) + period, + ratio, + scale, + radius) + { + this.cornerInterval = cornerInterval; + this.period = period; + } + + internal override string Info => base.Info + $"|period:{this.period}|cornerInterval:{this.cornerInterval}"; + + protected override void Initialize() + { + // Build top corner data + one period of the mosaic data: + int startOfFirstRepeatedMosaic = this.cornerInterval + this.period; + + for (int i = 0; i < startOfFirstRepeatedMosaic; i++) + { + ResizeKernel kernel = this.BuildKernel(i, i); + this.kernels[i] = kernel; + } + + // Copy the mosaics: + int bottomStartDest = this.DestinationLength - this.cornerInterval; + for (int i = startOfFirstRepeatedMosaic; i < bottomStartDest; i++) + { + double center = ((i + .5) * this.ratio) - .5; + int left = (int)TolerantMath.Ceiling(center - this.radius); + ResizeKernel kernel = this.kernels[i - this.period]; + this.kernels[i] = kernel.AlterLeftValue(left); + } + + // Build bottom corner data: + int bottomStartData = this.cornerInterval + this.period; + for (int i = 0; i < this.cornerInterval; i++) + { + ResizeKernel kernel = this.BuildKernel(bottomStartDest + i, bottomStartData + i); + this.kernels[bottomStartDest + i] = kernel; + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs new file mode 100644 index 0000000000..2ab574df2a --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs @@ -0,0 +1,247 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Memory; +using SixLabors.Memory; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Provides values from an optimized, + /// contiguous memory region. + /// + internal partial class ResizeKernelMap : IDisposable + { + private static readonly TolerantMath TolerantMath = TolerantMath.Default; + + private readonly IResampler sampler; + + private readonly int sourceLength; + + private readonly double ratio; + + private readonly double scale; + + private readonly int radius; + + private readonly MemoryHandle pinHandle; + + private readonly Buffer2D data; + + private readonly ResizeKernel[] kernels; + + // To avoid both GC allocations, and MemoryAllocator ceremony: + private readonly double[] tempValues; + + private ResizeKernelMap( + MemoryAllocator memoryAllocator, + IResampler sampler, + int sourceLength, + int destinationLength, + int bufferHeight, + double ratio, + double scale, + int radius) + { + this.sampler = sampler; + this.ratio = ratio; + this.scale = scale; + this.radius = radius; + this.sourceLength = sourceLength; + this.DestinationLength = destinationLength; + int maxWidth = (radius * 2) + 1; + this.data = memoryAllocator.Allocate2D(maxWidth, bufferHeight, AllocationOptions.Clean); + this.pinHandle = this.data.Memory.Pin(); + this.kernels = new ResizeKernel[destinationLength]; + this.tempValues = new double[maxWidth]; + } + + /// + /// Gets the length of the destination row/column + /// + public int DestinationLength { get; } + + /// + /// Gets a string of information to help debugging + /// + internal virtual string Info => + $"radius:{this.radius}|sourceSize:{this.sourceLength}|destinationSize:{this.DestinationLength}|ratio:{this.ratio}|scale:{this.scale}"; + + /// + /// Disposes instance releasing it's backing buffer. + /// + public void Dispose() + { + this.pinHandle.Dispose(); + this.data.Dispose(); + } + + /// + /// Returns a for an index value between 0 and DestinationSize - 1. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public ref ResizeKernel GetKernel(int destIdx) => ref this.kernels[destIdx]; + + /// + /// Computes the weights to apply at each pixel when resizing. + /// + /// The + /// The destination size + /// The source size + /// The to use for buffer allocations + /// The + public static ResizeKernelMap Calculate( + IResampler sampler, + int destinationSize, + int sourceSize, + MemoryAllocator memoryAllocator) + { + double ratio = (double)sourceSize / destinationSize; + double scale = ratio; + + if (scale < 1) + { + scale = 1; + } + + int radius = (int)TolerantMath.Ceiling(scale * sampler.Radius); + + // 'ratio' is a rational number. + // Multiplying it by LCM(sourceSize, destSize)/sourceSize will result in a whole number "again". + // This value is determining the length of the periods in repeating kernel map rows. + int period = ImageMaths.LeastCommonMultiple(sourceSize, destinationSize) / sourceSize; + + // the center position at i == 0: + double center0 = (ratio - 1) * 0.5; + double firstNonNegativeLeftVal = (radius - center0 - 1) / ratio; + + // The number of rows building a "stairway" at the top and the bottom of the kernel map + // corresponding to the corners of the image. + // If we do not normalize the kernel values, these rows also fit the periodic logic, + // however, it's just simpler to calculate them separately. + int cornerInterval = (int)TolerantMath.Ceiling(firstNonNegativeLeftVal); + + // If firstNonNegativeLeftVal was an integral value, we need firstNonNegativeLeftVal+1 + // instead of Ceiling: + if (TolerantMath.AreEqual(firstNonNegativeLeftVal, cornerInterval)) + { + cornerInterval++; + } + + // If 'cornerInterval' is too big compared to 'period', we can't apply the periodic optimization. + // If we don't have at least 2 periods, we go with the basic implementation: + bool hasAtLeast2Periods = 2 * (cornerInterval + period) < destinationSize; + + ResizeKernelMap result = hasAtLeast2Periods + ? new PeriodicKernelMap( + memoryAllocator, + sampler, + sourceSize, + destinationSize, + ratio, + scale, + radius, + period, + cornerInterval) + : new ResizeKernelMap( + memoryAllocator, + sampler, + sourceSize, + destinationSize, + destinationSize, + ratio, + scale, + radius); + + result.Initialize(); + + return result; + } + + protected virtual void Initialize() + { + for (int i = 0; i < this.DestinationLength; i++) + { + ResizeKernel kernel = this.BuildKernel(i, i); + this.kernels[i] = kernel; + } + } + + /// + /// Builds a for the row (in ) + /// referencing the data at row within , + /// so the data reusable by other data rows. + /// + private ResizeKernel BuildKernel(int destRowIndex, int dataRowIndex) + { + double center = ((destRowIndex + .5) * this.ratio) - .5; + + // Keep inside bounds. + int left = (int)TolerantMath.Ceiling(center - this.radius); + if (left < 0) + { + left = 0; + } + + int right = (int)TolerantMath.Floor(center + this.radius); + if (right > this.sourceLength - 1) + { + right = this.sourceLength - 1; + } + + ResizeKernel kernel = this.CreateKernel(dataRowIndex, left, right); + + Span kernelValues = this.tempValues.AsSpan().Slice(0, kernel.Length); + double sum = 0; + + for (int j = left; j <= right; j++) + { + double value = this.sampler.GetValue((float)((j - center) / this.scale)); + sum += value; + + kernelValues[j - left] = value; + } + + // Normalize, best to do it here rather than in the pixel loop later on. + if (sum > 0) + { + for (int j = 0; j < kernel.Length; j++) + { + // weights[w] = weights[w] / sum: + ref double kRef = ref kernelValues[j]; + kRef /= sum; + } + } + + kernel.Fill(kernelValues); + + return kernel; + } + + /// + /// Returns a referencing values of + /// at row . + /// + private unsafe ResizeKernel CreateKernel(int dataRowIndex, int left, int right) + { + int length = right - left + 1; + + if (length > this.data.Width) + { + throw new InvalidOperationException( + $"Error in KernelMap.CreateKernel({dataRowIndex},{left},{right}): left > this.data.Width"); + } + + Span rowSpan = this.data.GetRowSpan(dataRowIndex); + + ref float rowReference = ref MemoryMarshal.GetReference(rowSpan); + float* rowPtr = (float*)Unsafe.AsPointer(ref rowReference); + return new ResizeKernel(left, rowPtr, length); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs similarity index 53% rename from src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index b1c0632c66..189e21de7a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -2,15 +2,16 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Threading.Tasks; + using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.ColorSpaces.Companding; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; @@ -26,8 +27,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms where TPixel : struct, IPixel { // The following fields are not immutable but are optionally created on demand. - private WeightsBuffer horizontalWeights; - private WeightsBuffer verticalWeights; + private ResizeKernelMap horizontalKernelMap; + private ResizeKernelMap verticalKernelMap; /// /// Initializes a new instance of the class. @@ -39,25 +40,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Guard.NotNull(options, nameof(options)); Guard.NotNull(options.Sampler, nameof(options.Sampler)); - int tempWidth = options.Size.Width; - int tempHeight = options.Size.Height; + int targetWidth = options.Size.Width; + int targetHeight = options.Size.Height; // Ensure size is populated across both dimensions. // These dimensions are used to calculate the final dimensions determined by the mode algorithm. - if (tempWidth == 0 && tempHeight > 0) + // If only one of the incoming dimensions is 0, it will be modified here to maintain aspect ratio. + // If it is not possible to keep aspect ratio, make sure at least the minimum is is kept. + const int min = 1; + if (targetWidth == 0 && targetHeight > 0) { - tempWidth = (int)MathF.Round(sourceSize.Width * tempHeight / (float)sourceSize.Height); + targetWidth = (int)MathF.Max(min, MathF.Round(sourceSize.Width * targetHeight / (float)sourceSize.Height)); } - if (tempHeight == 0 && tempWidth > 0) + if (targetHeight == 0 && targetWidth > 0) { - tempHeight = (int)MathF.Round(sourceSize.Height * tempWidth / (float)sourceSize.Width); + targetHeight = (int)MathF.Max(min, MathF.Round(sourceSize.Height * targetWidth / (float)sourceSize.Width)); } - Guard.MustBeGreaterThan(tempWidth, 0, nameof(tempWidth)); - Guard.MustBeGreaterThan(tempHeight, 0, nameof(tempHeight)); + Guard.MustBeGreaterThan(targetWidth, 0, nameof(targetWidth)); + Guard.MustBeGreaterThan(targetHeight, 0, nameof(targetHeight)); - (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options, tempWidth, tempHeight); + (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options, targetWidth, targetHeight); this.Sampler = options.Sampler; this.Width = size.Width; @@ -94,15 +98,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Guard.NotNull(sampler, nameof(sampler)); // Ensure size is populated across both dimensions. + // If only one of the incoming dimensions is 0, it will be modified here to maintain aspect ratio. + // If it is not possible to keep aspect ratio, make sure at least the minimum is is kept. + const int min = 1; if (width == 0 && height > 0) { - width = (int)MathF.Round(sourceSize.Width * height / (float)sourceSize.Height); + width = (int)MathF.Max(min, MathF.Round(sourceSize.Width * height / (float)sourceSize.Height)); resizeRectangle.Width = width; } if (height == 0 && width > 0) { - height = (int)MathF.Round(sourceSize.Height * width / (float)sourceSize.Width); + height = (int)MathF.Max(min, MathF.Round(sourceSize.Height * width / (float)sourceSize.Width)); resizeRectangle.Height = height; } @@ -141,76 +148,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public bool Compand { get; } - /// - /// Computes the weights to apply at each pixel when resizing. - /// - /// The to use for buffer allocations - /// The destination size - /// The source size - /// The - // TODO: Made internal to simplify experimenting with weights data. Make it private when finished figuring out how to optimize all the stuff! - internal WeightsBuffer PrecomputeWeights(MemoryAllocator memoryAllocator, int destinationSize, int sourceSize) - { - float ratio = (float)sourceSize / destinationSize; - float scale = ratio; - - if (scale < 1F) - { - scale = 1F; - } - - IResampler sampler = this.Sampler; - float radius = MathF.Ceiling(scale * sampler.Radius); - var result = new WeightsBuffer(memoryAllocator, sourceSize, destinationSize); - - for (int i = 0; i < destinationSize; i++) - { - float center = ((i + .5F) * ratio) - .5F; - - // Keep inside bounds. - int left = (int)MathF.Ceiling(center - radius); - if (left < 0) - { - left = 0; - } - - int right = (int)MathF.Floor(center + radius); - if (right > sourceSize - 1) - { - right = sourceSize - 1; - } - - float sum = 0; - - WeightsWindow ws = result.GetWeightsWindow(i, left, right); - result.Weights[i] = ws; - - ref float weightsBaseRef = ref ws.GetStartReference(); - - for (int j = left; j <= right; j++) - { - float weight = sampler.GetValue((j - center) / scale); - sum += weight; - - // weights[j - left] = weight: - Unsafe.Add(ref weightsBaseRef, j - left) = weight; - } - - // Normalize, best to do it here rather than in the pixel loop later on. - if (sum > 0) - { - for (int w = 0; w < ws.Length; w++) - { - // weights[w] = weights[w] / sum: - ref float wRef = ref Unsafe.Add(ref weightsBaseRef, w); - wRef /= sum; - } - } - } - - return result; - } - /// protected override Image CreateDestination(Image source, Rectangle sourceRectangle) { @@ -228,15 +165,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { // Since all image frame dimensions have to be the same we can calculate this for all frames. MemoryAllocator memoryAllocator = source.GetMemoryAllocator(); - this.horizontalWeights = this.PrecomputeWeights( - memoryAllocator, + this.horizontalKernelMap = ResizeKernelMap.Calculate( + this.Sampler, this.ResizeRectangle.Width, - sourceRectangle.Width); + sourceRectangle.Width, + memoryAllocator); - this.verticalWeights = this.PrecomputeWeights( - memoryAllocator, + this.verticalKernelMap = ResizeKernelMap.Calculate( + this.Sampler, this.ResizeRectangle.Height, - sourceRectangle.Height); + sourceRectangle.Height, + memoryAllocator); } } @@ -267,105 +206,112 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms if (this.Sampler is NearestNeighborResampler) { + var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + // Scaling factors float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; - ParallelFor.WithConfiguration( - minY, - maxY, + ParallelHelper.IterateRows( + workingRect, configuration, - y => - { - // Y coordinates of source points - Span sourceRow = source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); - Span targetRow = destination.GetPixelRowSpan(y); - - for (int x = minX; x < maxX; x++) + rows => { - // X coordinates of source points - targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; - } - }); + for (int y = rows.Min; y < rows.Max; y++) + { + // Y coordinates of source points + Span sourceRow = + source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); + Span targetRow = destination.GetPixelRowSpan(y); + + for (int x = minX; x < maxX; x++) + { + // X coordinates of source points + targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; + } + } + }); return; } + int sourceHeight = source.Height; + // Interpolate the image using the calculated weights. // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. - // TODO: Using a transposed variant of 'firstPassPixels' could eliminate the need for the WeightsWindow.ComputeWeightedColumnSum() method, and improve speed! - using (Buffer2D firstPassPixels = source.MemoryAllocator.Allocate2D(width, source.Height)) + using (Buffer2D firstPassPixelsTransposed = source.MemoryAllocator.Allocate2D(sourceHeight, width)) { - firstPassPixels.MemorySource.Clear(); + firstPassPixelsTransposed.MemorySource.Clear(); - ParallelFor.WithTemporaryBuffer( - 0, - sourceRectangle.Bottom, + var processColsRect = new Rectangle(0, 0, source.Width, sourceRectangle.Bottom); + + ParallelHelper.IterateRowsWithTempBuffer( + processColsRect, configuration, - source.Width, - (int y, IMemoryOwner tempRowBuffer) => + (rows, tempRowBuffer) => { - ref Vector4 firstPassRow = ref MemoryMarshal.GetReference(firstPassPixels.GetRowSpan(y)); - Span sourceRow = source.GetPixelRowSpan(y); - Span tempRowSpan = tempRowBuffer.GetSpan(); + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = source.GetPixelRowSpan(y).Slice(sourceX); + Span tempRowSpan = tempRowBuffer.Span.Slice(sourceX); - PixelOperations.Instance.ToVector4(sourceRow, tempRowSpan, sourceRow.Length); + PixelOperations.Instance.ToVector4(configuration, sourceRow, tempRowSpan); + Vector4Utils.Premultiply(tempRowSpan); - if (this.Compand) - { - for (int x = minX; x < maxX; x++) + ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y]; + + if (this.Compand) { - WeightsWindow window = this.horizontalWeights.Weights[x - startX]; - Unsafe.Add(ref firstPassRow, x) = window.ComputeExpandedWeightedRowSum(tempRowSpan, sourceX); + SRgbCompanding.Expand(tempRowSpan); } - } - else - { + for (int x = minX; x < maxX; x++) { - WeightsWindow window = this.horizontalWeights.Weights[x - startX]; - Unsafe.Add(ref firstPassRow, x) = window.ComputeWeightedRowSum(tempRowSpan, sourceX); + ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX); + Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) = + kernel.Convolve(tempRowSpan); } } }); + var processRowsRect = Rectangle.FromLTRB(0, minY, width, maxY); + // Now process the rows. - ParallelFor.WithConfiguration( - minY, - maxY, + ParallelHelper.IterateRowsWithTempBuffer( + processRowsRect, configuration, - y => - { - // Ensure offsets are normalized for cropping and padding. - WeightsWindow window = this.verticalWeights.Weights[y - startY]; - ref TPixel targetRow = ref MemoryMarshal.GetReference(destination.GetPixelRowSpan(y)); - - if (this.Compand) + (rows, tempRowBuffer) => { - for (int x = 0; x < width; x++) - { - // Destination color components - Vector4 destinationVector = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY); - destinationVector = destinationVector.Compress(); + Span tempRowSpan = tempRowBuffer.Span; - ref TPixel pixel = ref Unsafe.Add(ref targetRow, x); - pixel.PackFromVector4(destinationVector); - } - } - else - { - for (int x = 0; x < width; x++) + for (int y = rows.Min; y < rows.Max; y++) { - // Destination color components - Vector4 destinationVector = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY); + // Ensure offsets are normalized for cropping and padding. + ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); + + ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempRowSpan); + + for (int x = 0; x < width; x++) + { + Span firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x).Slice(sourceY); + + // Destination color components + Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn); + } - ref TPixel pixel = ref Unsafe.Add(ref targetRow, x); - pixel.PackFromVector4(destinationVector); + Vector4Utils.UnPremultiply(tempRowSpan); + + if (this.Compand) + { + SRgbCompanding.Compress(tempRowSpan); + } + + Span targetRowSpan = destination.GetPixelRowSpan(y); + PixelOperations.Instance.FromVector4(configuration, tempRowSpan, targetRowSpan); } - } - }); + }); } } @@ -374,10 +320,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms base.AfterImageApply(source, destination, sourceRectangle); // TODO: An exception in the processing chain can leave these buffers undisposed. We should consider making image processors IDisposable! - this.horizontalWeights?.Dispose(); - this.horizontalWeights = null; - this.verticalWeights?.Dispose(); - this.verticalWeights = null; + this.horizontalKernelMap?.Dispose(); + this.horizontalKernelMap = null; + this.verticalKernelMap?.Dispose(); + this.verticalKernelMap = null; } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index 93c847d598..69ecf59215 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -2,11 +2,12 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Threading.Tasks; +using System.Numerics; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -15,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// Provides methods that allow the rotating of images. /// /// The pixel format. - internal class RotateProcessor : CenteredAffineTransformProcessor + internal class RotateProcessor : AffineTransformProcessor where TPixel : struct, IPixel { /// @@ -35,9 +36,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The sampler to perform the rotating operation. /// The source image size public RotateProcessor(float degrees, IResampler sampler, Size sourceSize) - : base(Matrix3x2Extensions.CreateRotationDegrees(degrees, PointF.Empty), sampler, sourceSize) + : this( + TransformUtils.CreateRotationMatrixDegrees(degrees, sourceSize), + sampler, + sourceSize) + => this.Degrees = degrees; + + // Helper constructor + private RotateProcessor(Matrix3x2 rotationMatrix, IResampler sampler, Size sourceSize) + : base(rotationMatrix, sampler, TransformUtils.GetTransformedSize(sourceSize, rotationMatrix)) { - this.Degrees = degrees; } /// @@ -83,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The private static float WrapDegrees(float degrees) { - degrees = degrees % 360; + degrees %= 360; while (degrees < 0) { @@ -147,25 +155,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int height = source.Height; Rectangle destinationBounds = destination.Bounds(); - ParallelFor.WithConfiguration( - 0, - height, + ParallelHelper.IterateRows( + source.Bounds(), configuration, - y => - { - Span sourceRow = source.GetPixelRowSpan(y); - for (int x = 0; x < width; x++) + rows => { - int newX = height - y - 1; - newX = height - newX - 1; - int newY = width - x - 1; - - if (destinationBounds.Contains(newX, newY)) + for (int y = rows.Min; y < rows.Max; y++) { - destination[newX, newY] = sourceRow[x]; + Span sourceRow = source.GetPixelRowSpan(y); + for (int x = 0; x < width; x++) + { + int newX = height - y - 1; + newX = height - newX - 1; + int newY = width - x - 1; + + if (destinationBounds.Contains(newX, newY)) + { + destination[newX, newY] = sourceRow[x]; + } + } } - } - }); + }); } /// @@ -179,20 +189,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int width = source.Width; int height = source.Height; - ParallelFor.WithConfiguration( - 0, - height, + ParallelHelper.IterateRows( + source.Bounds(), configuration, - y => - { - Span sourceRow = source.GetPixelRowSpan(y); - Span targetRow = destination.GetPixelRowSpan(height - y - 1); - - for (int x = 0; x < width; x++) + rows => { - targetRow[width - x - 1] = sourceRow[x]; - } - }); + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = source.GetPixelRowSpan(y); + Span targetRow = destination.GetPixelRowSpan(height - y - 1); + + for (int x = 0; x < width; x++) + { + targetRow[width - x - 1] = sourceRow[x]; + } + } + }); } /// @@ -207,22 +219,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int height = source.Height; Rectangle destinationBounds = destination.Bounds(); - ParallelFor.WithConfiguration( - 0, - height, + ParallelHelper.IterateRows( + source.Bounds(), configuration, - y => - { - Span sourceRow = source.GetPixelRowSpan(y); - int newX = height - y - 1; - for (int x = 0; x < width; x++) + rows => { - if (destinationBounds.Contains(newX, x)) + for (int y = rows.Min; y < rows.Max; y++) { - destination[newX, x] = sourceRow[x]; + Span sourceRow = source.GetPixelRowSpan(y); + int newX = height - y - 1; + for (int x = 0; x < width; x++) + { + if (destinationBounds.Contains(newX, x)) + { + destination[newX, x] = sourceRow[x]; + } + } } - } - }); + }); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index a0cfa63794..c7b1d74104 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -1,8 +1,9 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System.Numerics; + using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -11,7 +12,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// Provides methods that allow the skewing of images. /// /// The pixel format. - internal class SkewProcessor : CenteredAffineTransformProcessor + internal class SkewProcessor : AffineTransformProcessor where TPixel : struct, IPixel { /// @@ -33,12 +34,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The sampler to perform the skew operation. /// The source image size public SkewProcessor(float degreesX, float degreesY, IResampler sampler, Size sourceSize) - : base(Matrix3x2Extensions.CreateSkewDegrees(degreesX, degreesY, PointF.Empty), sampler, sourceSize) + : this( + TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, sourceSize), + sampler, + sourceSize) { this.DegreesX = degreesX; this.DegreesY = degreesY; } + // Helper constructor: + private SkewProcessor(Matrix3x2 skewMatrix, IResampler sampler, Size sourceSize) + : base(skewMatrix, sampler, TransformUtils.GetTransformedSize(sourceSize, skewMatrix)) + { + } + /// /// Gets the angle of rotation along the x-axis in degrees. /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformHelpers.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformHelpers.cs deleted file mode 100644 index b22fa64cfd..0000000000 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformHelpers.cs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using SixLabors.ImageSharp.MetaData.Profiles.Exif; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing.Processors.Transforms -{ - /// - /// Contains helper methods for working with affine and non-affine transforms - /// - internal static class TransformHelpers - { - /// - /// Updates the dimensional metadata of a transformed image - /// - /// The pixel format. - /// The image to update - public static void UpdateDimensionalMetData(Image image) - where TPixel : struct, IPixel - { - ExifProfile profile = image.MetaData.ExifProfile; - if (profile is null) - { - return; - } - - // Removing the previously stored value allows us to set a value with our own data tag if required. - if (profile.GetValue(ExifTag.PixelXDimension) != null) - { - profile.RemoveValue(ExifTag.PixelXDimension); - - if (image.Width <= ushort.MaxValue) - { - profile.SetValue(ExifTag.PixelXDimension, (ushort)image.Width); - } - else - { - profile.SetValue(ExifTag.PixelXDimension, (uint)image.Width); - } - } - - if (profile.GetValue(ExifTag.PixelYDimension) != null) - { - profile.RemoveValue(ExifTag.PixelYDimension); - - if (image.Height <= ushort.MaxValue) - { - profile.SetValue(ExifTag.PixelYDimension, (ushort)image.Height); - } - else - { - profile.SetValue(ExifTag.PixelYDimension, (uint)image.Height); - } - } - } - - /// - /// Gets the centered transform matrix based upon the source and destination rectangles - /// - /// The source image bounds. - /// The destination image bounds. - /// The transformation matrix. - /// The - public static Matrix3x2 GetCenteredTransformMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle, Matrix3x2 matrix) - { - // We invert the matrix to handle the transformation from screen to world space. - // This ensures scaling matrices are correct. - Matrix3x2.Invert(matrix, out Matrix3x2 inverted); - - var translationToTargetCenter = Matrix3x2.CreateTranslation(-destinationRectangle.Width * .5F, -destinationRectangle.Height * .5F); - var translateToSourceCenter = Matrix3x2.CreateTranslation(sourceRectangle.Width * .5F, sourceRectangle.Height * .5F); - - Matrix3x2.Invert(translationToTargetCenter * inverted * translateToSourceCenter, out Matrix3x2 centered); - - // Translate back to world to pass to the Transform method. - return centered; - } - - /// - /// Gets the centered transform matrix based upon the source and destination rectangles - /// - /// The source image bounds. - /// The destination image bounds. - /// The transformation matrix. - /// The - public static Matrix4x4 GetCenteredTransformMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle, Matrix4x4 matrix) - { - // We invert the matrix to handle the transformation from screen to world space. - // This ensures scaling matrices are correct. - Matrix4x4.Invert(matrix, out Matrix4x4 inverted); - - var translationToTargetCenter = Matrix4x4.CreateTranslation(-destinationRectangle.Width * .5F, -destinationRectangle.Height * .5F, 0); - var translateToSourceCenter = Matrix4x4.CreateTranslation(sourceRectangle.Width * .5F, sourceRectangle.Height * .5F, 0); - - Matrix4x4.Invert(translationToTargetCenter * inverted * translateToSourceCenter, out Matrix4x4 centered); - - // Translate back to world to pass to the Transform method. - return centered; - } - - /// - /// Returns the bounding rectangle relative to the source for the given transformation matrix. - /// - /// The source rectangle. - /// The transformation matrix. - /// - /// The . - /// - public static Rectangle GetTransformedBoundingRectangle(Rectangle rectangle, Matrix3x2 matrix) - { - // Calculate the position of the four corners in world space by applying - // The world matrix to the four corners in object space (0, 0, width, height) - var tl = Vector2.Transform(Vector2.Zero, matrix); - var tr = Vector2.Transform(new Vector2(rectangle.Width, 0), matrix); - var bl = Vector2.Transform(new Vector2(0, rectangle.Height), matrix); - var br = Vector2.Transform(new Vector2(rectangle.Width, rectangle.Height), matrix); - - return GetBoundingRectangle(tl, tr, bl, br); - } - - /// - /// Returns the bounding rectangle relative to the source for the given transformation matrix. - /// - /// The source rectangle. - /// The transformation matrix. - /// - /// The . - /// - public static Rectangle GetTransformedBoundingRectangle(Rectangle rectangle, Matrix4x4 matrix) - { - // Calculate the position of the four corners in world space by applying - // The world matrix to the four corners in object space (0, 0, width, height) - var tl = Vector2.Transform(Vector2.Zero, matrix); - var tr = Vector2.Transform(new Vector2(rectangle.Width, 0), matrix); - var bl = Vector2.Transform(new Vector2(0, rectangle.Height), matrix); - var br = Vector2.Transform(new Vector2(rectangle.Width, rectangle.Height), matrix); - - return GetBoundingRectangle(tl, tr, bl, br); - } - - private static Rectangle GetBoundingRectangle(Vector2 tl, Vector2 tr, Vector2 bl, Vector2 br) - { - // Find the minimum and maximum "corners" based on the given vectors - float minX = MathF.Min(tl.X, MathF.Min(tr.X, MathF.Min(bl.X, br.X))); - float maxX = MathF.Max(tl.X, MathF.Max(tr.X, MathF.Max(bl.X, br.X))); - float minY = MathF.Min(tl.Y, MathF.Min(tr.Y, MathF.Min(bl.Y, br.Y))); - float maxY = MathF.Max(tl.Y, MathF.Max(tr.Y, MathF.Max(bl.Y, br.Y))); - float sizeX = maxX - minX + .5F; - float sizeY = maxY - minY + .5F; - - return new Rectangle((int)(MathF.Ceiling(minX) - .5F), (int)(MathF.Ceiling(minY) - .5F), (int)MathF.Floor(sizeX), (int)MathF.Floor(sizeY)); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformKernelMap.cs new file mode 100644 index 0000000000..573120888f --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformKernelMap.cs @@ -0,0 +1,161 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Contains the methods required to calculate transform kernel convolution. + /// + internal class TransformKernelMap : IDisposable + { + private readonly Buffer2D yBuffer; + private readonly Buffer2D xBuffer; + private readonly Vector2 extents; + private Vector4 maxSourceExtents; + private readonly IResampler sampler; + + /// + /// Initializes a new instance of the class. + /// + /// The configuration. + /// The source size. + /// The destination size. + /// The sampler. + public TransformKernelMap(Configuration configuration, Size source, Size destination, IResampler sampler) + { + this.sampler = sampler; + float yRadius = this.GetSamplingRadius(source.Height, destination.Height); + float xRadius = this.GetSamplingRadius(source.Width, destination.Width); + + this.extents = new Vector2(xRadius, yRadius); + int xLength = (int)MathF.Ceiling((this.extents.X * 2) + 2); + int yLength = (int)MathF.Ceiling((this.extents.Y * 2) + 2); + + // We use 2D buffers so that we can access the weight spans per row in parallel. + this.yBuffer = configuration.MemoryAllocator.Allocate2D(yLength, destination.Height); + this.xBuffer = configuration.MemoryAllocator.Allocate2D(xLength, destination.Height); + + int maxX = source.Width - 1; + int maxY = source.Height - 1; + this.maxSourceExtents = new Vector4(maxX, maxY, maxX, maxY); + } + + /// + /// Gets a reference to the first item of the y window. + /// + /// The reference to the first item of the window. + [MethodImpl(InliningOptions.ShortMethod)] + public ref float GetYStartReference(int y) + => ref MemoryMarshal.GetReference(this.yBuffer.GetRowSpan(y)); + + /// + /// Gets a reference to the first item of the x window. + /// + /// The reference to the first item of the window. + [MethodImpl(InliningOptions.ShortMethod)] + public ref float GetXStartReference(int y) + => ref MemoryMarshal.GetReference(this.xBuffer.GetRowSpan(y)); + + public void Convolve( + Vector2 transformedPoint, + int column, + ref float ySpanRef, + ref float xSpanRef, + Buffer2D sourcePixels, + Span targetRow) + where TPixel : struct, IPixel + { + // Clamp sampling pixel radial extents to the source image edges + Vector2 minXY = transformedPoint - this.extents; + Vector2 maxXY = transformedPoint + this.extents; + + // left, top, right, bottom + var extents = new Vector4( + MathF.Ceiling(minXY.X - .5F), + MathF.Ceiling(minXY.Y - .5F), + MathF.Floor(maxXY.X + .5F), + MathF.Floor(maxXY.Y + .5F)); + + extents = Vector4.Clamp(extents, Vector4.Zero, this.maxSourceExtents); + + int left = (int)extents.X; + int top = (int)extents.Y; + int right = (int)extents.Z; + int bottom = (int)extents.W; + + if (left == right || top == bottom) + { + return; + } + + this.CalculateWeights(top, bottom, transformedPoint.Y, ref ySpanRef); + this.CalculateWeights(left, right, transformedPoint.X, ref xSpanRef); + + Vector4 sum = Vector4.Zero; + for (int kernelY = 0, y = top; y <= bottom; y++, kernelY++) + { + float yWeight = Unsafe.Add(ref ySpanRef, kernelY); + + for (int kernelX = 0, x = left; x <= right; x++, kernelX++) + { + float xWeight = Unsafe.Add(ref xSpanRef, kernelX); + + // Values are first premultiplied to prevent darkening of edge pixels. + var current = sourcePixels[x, y].ToVector4(); + Vector4Utils.Premultiply(ref current); + sum += current * xWeight * yWeight; + } + } + + // Reverse the premultiplication + Vector4Utils.UnPremultiply(ref sum); + targetRow[column] = sum; + } + + /// + /// Calculated the normalized weights for the given point. + /// + /// The minimum sampling offset + /// The maximum sampling offset + /// The transformed point dimension + /// The reference to the collection of weights + [MethodImpl(InliningOptions.ShortMethod)] + private void CalculateWeights(int min, int max, float point, ref float weightsRef) + { + float sum = 0; + for (int x = 0, i = min; i <= max; i++, x++) + { + float weight = this.sampler.GetValue(i - point); + sum += weight; + Unsafe.Add(ref weightsRef, x) = weight; + } + } + + [MethodImpl(InliningOptions.ShortMethod)] + private float GetSamplingRadius(int sourceSize, int destinationSize) + { + float scale = (float)sourceSize / destinationSize; + + if (scale < 1F) + { + scale = 1F; + } + + return MathF.Ceiling(scale * this.sampler.Radius); + } + + public void Dispose() + { + this.yBuffer?.Dispose(); + this.xBuffer?.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs index 13ee90a062..4973b90f46 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs @@ -16,6 +16,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) - => TransformHelpers.UpdateDimensionalMetData(destination); + => TransformProcessorHelpers.UpdateDimensionalMetData(destination); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs new file mode 100644 index 0000000000..f5536d0467 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs @@ -0,0 +1,58 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.MetaData.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Contains helper methods for working with transforms. + /// + internal static class TransformProcessorHelpers + { + /// + /// Updates the dimensional metadata of a transformed image + /// + /// The pixel format. + /// The image to update + public static void UpdateDimensionalMetData(Image image) + where TPixel : struct, IPixel + { + ExifProfile profile = image.MetaData.ExifProfile; + if (profile is null) + { + return; + } + + // Removing the previously stored value allows us to set a value with our own data tag if required. + if (profile.GetValue(ExifTag.PixelXDimension) != null) + { + profile.RemoveValue(ExifTag.PixelXDimension); + + if (image.Width <= ushort.MaxValue) + { + profile.SetValue(ExifTag.PixelXDimension, (ushort)image.Width); + } + else + { + profile.SetValue(ExifTag.PixelXDimension, (uint)image.Width); + } + } + + if (profile.GetValue(ExifTag.PixelYDimension) != null) + { + profile.RemoveValue(ExifTag.PixelYDimension); + + if (image.Height <= ushort.MaxValue) + { + profile.SetValue(ExifTag.PixelYDimension, (ushort)image.Height); + } + else + { + profile.SetValue(ExifTag.PixelYDimension, (uint)image.Height); + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs new file mode 100644 index 0000000000..24b15d3098 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs @@ -0,0 +1,332 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Contains utility methods for working with transforms. + /// + internal static class TransformUtils + { + /// + /// Creates a centered rotation matrix using the given rotation in degrees and the source size. + /// + /// The amount of rotation, in degrees. + /// The source image size. + /// The . + public static Matrix3x2 CreateRotationMatrixDegrees(float degrees, Size size) + => CreateCenteredTransformMatrix( + new Rectangle(Point.Empty, size), + Matrix3x2Extensions.CreateRotationDegrees(degrees, PointF.Empty)); + + /// + /// Creates a centered rotation matrix using the given rotation in radians and the source size. + /// + /// The amount of rotation, in radians. + /// The source image size. + /// The . + public static Matrix3x2 CreateRotationMatrixRadians(float radians, Size size) + => CreateCenteredTransformMatrix( + new Rectangle(Point.Empty, size), + Matrix3x2Extensions.CreateRotation(radians, PointF.Empty)); + + /// + /// Creates a centered skew matrix from the give angles in degrees and the source size. + /// + /// The X angle, in degrees. + /// The Y angle, in degrees. + /// The source image size. + /// The . + public static Matrix3x2 CreateSkewMatrixDegrees(float degreesX, float degreesY, Size size) + => CreateCenteredTransformMatrix( + new Rectangle(Point.Empty, size), + Matrix3x2Extensions.CreateSkewDegrees(degreesX, degreesY, PointF.Empty)); + + /// + /// Creates a centered skew matrix from the give angles in radians and the source size. + /// + /// The X angle, in radians. + /// The Y angle, in radians. + /// The source image size. + /// The . + public static Matrix3x2 CreateSkewMatrixRadians(float radiansX, float radiansY, Size size) + => CreateCenteredTransformMatrix( + new Rectangle(Point.Empty, size), + Matrix3x2Extensions.CreateSkew(radiansX, radiansY, PointF.Empty)); + + /// + /// Gets the centered transform matrix based upon the source and destination rectangles. + /// + /// The source image bounds. + /// The transformation matrix. + /// The + public static Matrix3x2 CreateCenteredTransformMatrix(Rectangle sourceRectangle, Matrix3x2 matrix) + { + Rectangle destinationRectangle = GetTransformedBoundingRectangle(sourceRectangle, matrix); + + // We invert the matrix to handle the transformation from screen to world space. + // This ensures scaling matrices are correct. + Matrix3x2.Invert(matrix, out Matrix3x2 inverted); + + var translationToTargetCenter = Matrix3x2.CreateTranslation(new Vector2(-destinationRectangle.Width, -destinationRectangle.Height) * .5F); + var translateToSourceCenter = Matrix3x2.CreateTranslation(new Vector2(sourceRectangle.Width, sourceRectangle.Height) * .5F); + + // Translate back to world space. + Matrix3x2.Invert(translationToTargetCenter * inverted * translateToSourceCenter, out Matrix3x2 centered); + + return centered; + } + + /// + /// Creates a matrix that performs a tapering projective transform. + /// + /// + /// The rectangular size of the image being transformed. + /// An enumeration that indicates the side of the rectangle that tapers. + /// An enumeration that indicates on which corners to taper the rectangle. + /// The amount to taper. + /// The + public static Matrix4x4 CreateTaperMatrix(Size size, TaperSide side, TaperCorner corner, float fraction) + { + Matrix4x4 matrix = Matrix4x4.Identity; + + switch (side) + { + case TaperSide.Left: + matrix.M11 = fraction; + matrix.M22 = fraction; + matrix.M13 = (fraction - 1) / size.Width; + + switch (corner) + { + case TaperCorner.RightOrBottom: + break; + + case TaperCorner.LeftOrTop: + matrix.M12 = size.Height * matrix.M13; + matrix.M32 = size.Height * (1 - fraction); + break; + + case TaperCorner.Both: + matrix.M12 = size.Height * .5F * matrix.M13; + matrix.M32 = size.Height * (1 - fraction) / 2; + break; + } + + break; + + case TaperSide.Top: + matrix.M11 = fraction; + matrix.M22 = fraction; + matrix.M23 = (fraction - 1) / size.Height; + + switch (corner) + { + case TaperCorner.RightOrBottom: + break; + + case TaperCorner.LeftOrTop: + matrix.M21 = size.Width * matrix.M23; + matrix.M31 = size.Width * (1 - fraction); + break; + + case TaperCorner.Both: + matrix.M21 = size.Width * .5F * matrix.M23; + matrix.M31 = size.Width * (1 - fraction) / 2; + break; + } + + break; + + case TaperSide.Right: + matrix.M11 = 1 / fraction; + matrix.M13 = (1 - fraction) / (size.Width * fraction); + + switch (corner) + { + case TaperCorner.RightOrBottom: + break; + + case TaperCorner.LeftOrTop: + matrix.M12 = size.Height * matrix.M13; + break; + + case TaperCorner.Both: + matrix.M12 = size.Height * .5F * matrix.M13; + break; + } + + break; + + case TaperSide.Bottom: + matrix.M22 = 1 / fraction; + matrix.M23 = (1 - fraction) / (size.Height * fraction); + + switch (corner) + { + case TaperCorner.RightOrBottom: + break; + + case TaperCorner.LeftOrTop: + matrix.M21 = size.Width * matrix.M23; + break; + + case TaperCorner.Both: + matrix.M21 = size.Width * .5F * matrix.M23; + break; + } + + break; + } + + return matrix; + } + + /// + /// Returns the rectangle bounds relative to the source for the given transformation matrix. + /// + /// The source rectangle. + /// The transformation matrix. + /// + /// The . + /// + public static Rectangle GetTransformedBoundingRectangle(Rectangle rectangle, Matrix3x2 matrix) + { + Rectangle transformed = GetTransformedRectangle(rectangle, matrix); + return new Rectangle(0, 0, transformed.Width, transformed.Height); + } + + /// + /// Returns the rectangle relative to the source for the given transformation matrix. + /// + /// The source rectangle. + /// The transformation matrix. + /// + /// The . + /// + public static Rectangle GetTransformedRectangle(Rectangle rectangle, Matrix3x2 matrix) + { + if (rectangle.Equals(default) || Matrix3x2.Identity.Equals(matrix)) + { + return rectangle; + } + + var tl = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Top), matrix); + var tr = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Top), matrix); + var bl = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Bottom), matrix); + var br = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Bottom), matrix); + + return GetBoundingRectangle(tl, tr, bl, br); + } + + /// + /// Returns the size relative to the source for the given transformation matrix. + /// + /// The source size. + /// The transformation matrix. + /// + /// The . + /// + public static Size GetTransformedSize(Size size, Matrix3x2 matrix) + { + Guard.IsTrue(size.Width > 0 && size.Height > 0, nameof(size), "Source size dimensions cannot be 0!"); + + if (matrix.Equals(default) || matrix.Equals(Matrix3x2.Identity)) + { + return size; + } + + Rectangle rectangle = GetTransformedRectangle(new Rectangle(Point.Empty, size), matrix); + + return ConstrainSize(rectangle); + } + + /// + /// Returns the rectangle relative to the source for the given transformation matrix. + /// + /// The source rectangle. + /// The transformation matrix. + /// + /// The . + /// + public static Rectangle GetTransformedRectangle(Rectangle rectangle, Matrix4x4 matrix) + { + if (rectangle.Equals(default) || Matrix4x4.Identity.Equals(matrix)) + { + return rectangle; + } + + Vector2 GetVector(float x, float y) + { + const float Epsilon = 0.0000001F; + var v3 = Vector3.Transform(new Vector3(x, y, 1F), matrix); + return new Vector2(v3.X, v3.Y) / MathF.Max(v3.Z, Epsilon); + } + + Vector2 tl = GetVector(rectangle.Left, rectangle.Top); + Vector2 tr = GetVector(rectangle.Right, rectangle.Top); + Vector2 bl = GetVector(rectangle.Left, rectangle.Bottom); + Vector2 br = GetVector(rectangle.Right, rectangle.Bottom); + + return GetBoundingRectangle(tl, tr, bl, br); + } + + /// + /// Returns the size relative to the source for the given transformation matrix. + /// + /// The source size. + /// The transformation matrix. + /// + /// The . + /// + public static Size GetTransformedSize(Size size, Matrix4x4 matrix) + { + Guard.IsTrue(size.Width > 0 && size.Height > 0, nameof(size), "Source size dimensions cannot be 0!"); + + if (matrix.Equals(default) || matrix.Equals(Matrix4x4.Identity)) + { + return size; + } + + Rectangle rectangle = GetTransformedRectangle(new Rectangle(Point.Empty, size), matrix); + + return ConstrainSize(rectangle); + } + + private static Size ConstrainSize(Rectangle rectangle) + { + // We want to resize the canvas here taking into account any translations. + int height = rectangle.Top < 0 ? rectangle.Bottom : Math.Max(rectangle.Height, rectangle.Bottom); + int width = rectangle.Left < 0 ? rectangle.Right : Math.Max(rectangle.Width, rectangle.Right); + + // If location in either direction is translated to a negative value equal to or exceeding the + // dimensions in eith direction we need to reassign the dimension. + if (height <= 0) + { + height = rectangle.Height; + } + + if (width <= 0) + { + width = rectangle.Width; + } + + return new Size(width, height); + } + + private static Rectangle GetBoundingRectangle(Vector2 tl, Vector2 tr, Vector2 bl, Vector2 br) + { + // Find the minimum and maximum "corners" based on the given vectors + float left = MathF.Min(tl.X, MathF.Min(tr.X, MathF.Min(bl.X, br.X))); + float top = MathF.Min(tl.Y, MathF.Min(tr.Y, MathF.Min(bl.Y, br.Y))); + float right = MathF.Max(tl.X, MathF.Max(tr.X, MathF.Max(bl.X, br.X))); + float bottom = MathF.Max(tl.Y, MathF.Max(tr.Y, MathF.Max(bl.Y, br.Y))); + + return Rectangle.Round(RectangleF.FromLTRB(left, top, right, bottom)); + } + } +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/WeightsBuffer.cs b/src/ImageSharp/Processing/Processors/Transforms/WeightsBuffer.cs deleted file mode 100644 index 68133a5489..0000000000 --- a/src/ImageSharp/Processing/Processors/Transforms/WeightsBuffer.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; - -using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; - -namespace SixLabors.ImageSharp.Processing.Processors.Transforms -{ - /// - /// Holds the values in an optimized contigous memory region. - /// - internal class WeightsBuffer : IDisposable - { - private readonly Buffer2D dataBuffer; - - /// - /// Initializes a new instance of the class. - /// - /// The to use for allocations. - /// The size of the source window - /// The size of the destination window - public WeightsBuffer(MemoryAllocator memoryAllocator, int sourceSize, int destinationSize) - { - this.dataBuffer = memoryAllocator.Allocate2D(sourceSize, destinationSize, AllocationOptions.Clean); - this.Weights = new WeightsWindow[destinationSize]; - } - - /// - /// Gets the calculated values. - /// - public WeightsWindow[] Weights { get; } - - /// - /// Disposes instance releasing it's backing buffer. - /// - public void Dispose() - { - this.dataBuffer.Dispose(); - } - - /// - /// Slices a weights value at the given positions. - /// - /// The index in destination buffer - /// The local left index value - /// The local right index value - /// The weights - public WeightsWindow GetWeightsWindow(int destIdx, int leftIdx, int rightIdx) - { - return new WeightsWindow(destIdx, leftIdx, this.dataBuffer, rightIdx - leftIdx + 1); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs deleted file mode 100644 index 01cf97e591..0000000000 --- a/src/ImageSharp/Processing/Processors/Transforms/WeightsWindow.cs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Buffers; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -using SixLabors.ImageSharp.Memory; -using SixLabors.Memory; - -namespace SixLabors.ImageSharp.Processing.Processors.Transforms -{ - /// - /// Points to a collection of of weights allocated in . - /// - internal struct WeightsWindow - { - /// - /// The local left index position - /// - public int Left; - - /// - /// The length of the weights window - /// - public int Length; - - /// - /// The index in the destination buffer - /// - private readonly int flatStartIndex; - - /// - /// The buffer containing the weights values. - /// - private readonly MemorySource buffer; - - /// - /// Initializes a new instance of the struct. - /// - /// The destination index in the buffer - /// The local left index - /// The span - /// The length of the window - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal WeightsWindow(int index, int left, Buffer2D buffer, int length) - { - this.flatStartIndex = (index * buffer.Width) + left; - this.Left = left; - this.buffer = buffer.MemorySource; - this.Length = length; - } - - /// - /// Gets a reference to the first item of the window. - /// - /// The reference to the first item of the window - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref float GetStartReference() - { - Span span = this.buffer.GetSpan(); - return ref span[this.flatStartIndex]; - } - - /// - /// Gets the span representing the portion of the that this window covers - /// - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetWindowSpan() => this.buffer.GetSpan().Slice(this.flatStartIndex, this.Length); - - /// - /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance. - /// - /// The input span of vectors - /// The source row position. - /// The weighted sum - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ComputeWeightedRowSum(Span rowSpan, int sourceX) - { - ref float horizontalValues = ref this.GetStartReference(); - int left = this.Left; - ref Vector4 vecPtr = ref Unsafe.Add(ref MemoryMarshal.GetReference(rowSpan), left + sourceX); - - // Destination color components - Vector4 result = Vector4.Zero; - - for (int i = 0; i < this.Length; i++) - { - float weight = Unsafe.Add(ref horizontalValues, i); - Vector4 v = Unsafe.Add(ref vecPtr, i); - result += v.Premultiply() * weight; - } - - return result; - } - - /// - /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance. - /// Applies to all input vectors. - /// - /// The input span of vectors - /// The source row position. - /// The weighted sum - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ComputeExpandedWeightedRowSum(Span rowSpan, int sourceX) - { - ref float horizontalValues = ref this.GetStartReference(); - int left = this.Left; - ref Vector4 vecPtr = ref Unsafe.Add(ref MemoryMarshal.GetReference(rowSpan), left + sourceX); - - // Destination color components - Vector4 result = Vector4.Zero; - - for (int i = 0; i < this.Length; i++) - { - float weight = Unsafe.Add(ref horizontalValues, i); - Vector4 v = Unsafe.Add(ref vecPtr, i); - result += v.Premultiply().Expand() * weight; - } - - return result.UnPremultiply(); - } - - /// - /// Computes the sum of vectors in 'firstPassPixels' at a row pointed by 'x', - /// weighted by weight values, pointed by this instance. - /// - /// The buffer of input vectors in row first order - /// The row position - /// The source column position. - /// The weighted sum - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ComputeWeightedColumnSum(Buffer2D firstPassPixels, int x, int sourceY) - { - ref float verticalValues = ref this.GetStartReference(); - int left = this.Left; - - // Destination color components - Vector4 result = Vector4.Zero; - - for (int i = 0; i < this.Length; i++) - { - float yw = Unsafe.Add(ref verticalValues, i); - int index = left + i + sourceY; - result += firstPassPixels[x, index] * yw; - } - - return result.UnPremultiply(); - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs new file mode 100644 index 0000000000..c29941d071 --- /dev/null +++ b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs @@ -0,0 +1,319 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Numerics; +using SixLabors.ImageSharp.Processing.Processors.Transforms; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// A helper class for constructing instances for use in projective transforms. + /// + public class ProjectiveTransformBuilder + { + private readonly List> matrixFactories = new List>(); + + /// + /// Prepends a matrix that performs a tapering projective transform. + /// + /// An enumeration that indicates the side of the rectangle that tapers. + /// An enumeration that indicates on which corners to taper the rectangle. + /// The amount to taper. + /// The . + public ProjectiveTransformBuilder PrependTaper(TaperSide side, TaperCorner corner, float fraction) + => this.Prepend(size => TransformUtils.CreateTaperMatrix(size, side, corner, fraction)); + + /// + /// Appends a matrix that performs a tapering projective transform. + /// + /// An enumeration that indicates the side of the rectangle that tapers. + /// An enumeration that indicates on which corners to taper the rectangle. + /// The amount to taper. + /// The . + public ProjectiveTransformBuilder AppendTaper(TaperSide side, TaperCorner corner, float fraction) + => this.Append(size => TransformUtils.CreateTaperMatrix(size, side, corner, fraction)); + + /// + /// Prepends a centered rotation matrix using the given rotation in degrees. + /// + /// The amount of rotation, in degrees. + /// The . + public ProjectiveTransformBuilder PrependRotationDegrees(float degrees) + => this.PrependRotationRadians(GeometryUtilities.DegreeToRadian(degrees)); + + /// + /// Prepends a centered rotation matrix using the given rotation in radians. + /// + /// The amount of rotation, in radians. + /// The . + public ProjectiveTransformBuilder PrependRotationRadians(float radians) + => this.Prepend(size => new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, size))); + + /// + /// Prepends a centered rotation matrix using the given rotation in degrees at the given origin. + /// + /// The amount of rotation, in radians. + /// The rotation origin point. + /// The . + internal ProjectiveTransformBuilder PrependRotationDegrees(float degrees, Vector2 origin) + => this.PrependRotationRadians(GeometryUtilities.DegreeToRadian(degrees), origin); + + /// + /// Prepends a centered rotation matrix using the given rotation in radians at the given origin. + /// + /// The amount of rotation, in radians. + /// The rotation origin point. + /// The . + internal ProjectiveTransformBuilder PrependRotationRadians(float radians, Vector2 origin) + => this.PrependMatrix(Matrix4x4.CreateRotationZ(radians, new Vector3(origin, 0))); + + /// + /// Appends a centered rotation matrix using the given rotation in degrees. + /// + /// The amount of rotation, in degrees. + /// The . + public ProjectiveTransformBuilder AppendRotationDegrees(float degrees) + => this.AppendRotationRadians(GeometryUtilities.DegreeToRadian(degrees)); + + /// + /// Appends a centered rotation matrix using the given rotation in radians. + /// + /// The amount of rotation, in radians. + /// The . + public ProjectiveTransformBuilder AppendRotationRadians(float radians) + => this.Append(size => new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, size))); + + /// + /// Appends a centered rotation matrix using the given rotation in degrees at the given origin. + /// + /// The amount of rotation, in radians. + /// The rotation origin point. + /// The . + internal ProjectiveTransformBuilder AppendRotationDegrees(float degrees, Vector2 origin) + => this.AppendRotationRadians(GeometryUtilities.DegreeToRadian(degrees), origin); + + /// + /// Appends a centered rotation matrix using the given rotation in radians at the given origin. + /// + /// The amount of rotation, in radians. + /// The rotation origin point. + /// The . + internal ProjectiveTransformBuilder AppendRotationRadians(float radians, Vector2 origin) + => this.AppendMatrix(Matrix4x4.CreateRotationZ(radians, new Vector3(origin, 0))); + + /// + /// Prepends a scale matrix from the given uniform scale. + /// + /// The uniform scale. + /// The . + public ProjectiveTransformBuilder PrependScale(float scale) + => this.PrependMatrix(Matrix4x4.CreateScale(scale)); + + /// + /// Prepends a scale matrix from the given vector scale. + /// + /// The horizontal and vertical scale. + /// The . + public ProjectiveTransformBuilder PrependScale(SizeF scale) + => this.PrependScale((Vector2)scale); + + /// + /// Prepends a scale matrix from the given vector scale. + /// + /// The horizontal and vertical scale. + /// The . + public ProjectiveTransformBuilder PrependScale(Vector2 scales) + => this.PrependMatrix(Matrix4x4.CreateScale(new Vector3(scales, 1F))); + + /// + /// Appends a scale matrix from the given uniform scale. + /// + /// The uniform scale. + /// The . + public ProjectiveTransformBuilder AppendScale(float scale) + => this.AppendMatrix(Matrix4x4.CreateScale(scale)); + + /// + /// Appends a scale matrix from the given vector scale. + /// + /// The horizontal and vertical scale. + /// The . + public ProjectiveTransformBuilder AppendScale(SizeF scales) + => this.AppendScale((Vector2)scales); + + /// + /// Appends a scale matrix from the given vector scale. + /// + /// The horizontal and vertical scale. + /// The . + public ProjectiveTransformBuilder AppendScale(Vector2 scales) + => this.AppendMatrix(Matrix4x4.CreateScale(new Vector3(scales, 1F))); + + /// + /// Prepends a centered skew matrix from the give angles in degrees. + /// + /// The X angle, in degrees. + /// The Y angle, in degrees. + /// The . + internal ProjectiveTransformBuilder PrependSkewDegrees(float degreesX, float degreesY) + => this.PrependSkewRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY)); + + /// + /// Prepends a centered skew matrix from the give angles in radians. + /// + /// The X angle, in radians. + /// The Y angle, in radians. + /// The . + public ProjectiveTransformBuilder PrependSkewRadians(float radiansX, float radiansY) + => this.Prepend(size => new Matrix4x4(TransformUtils.CreateSkewMatrixRadians(radiansX, radiansY, size))); + + /// + /// Prepends a skew matrix using the given angles in degrees at the given origin. + /// + /// The X angle, in degrees. + /// The Y angle, in degrees. + /// The skew origin point. + /// The . + public ProjectiveTransformBuilder PrependSkewDegrees(float degreesX, float degreesY, Vector2 origin) + => this.PrependSkewRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), origin); + + /// + /// Prepends a skew matrix using the given angles in radians at the given origin. + /// + /// The X angle, in radians. + /// The Y angle, in radians. + /// The skew origin point. + /// The . + public ProjectiveTransformBuilder PrependSkewRadians(float radiansX, float radiansY, Vector2 origin) + => this.PrependMatrix(new Matrix4x4(Matrix3x2.CreateSkew(radiansX, radiansY, origin))); + + /// + /// Appends a centered skew matrix from the give angles in degrees. + /// + /// The X angle, in degrees. + /// The Y angle, in degrees. + /// The . + internal ProjectiveTransformBuilder AppendSkewDegrees(float degreesX, float degreesY) + => this.AppendSkewRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY)); + + /// + /// Appends a centered skew matrix from the give angles in radians. + /// + /// The X angle, in radians. + /// The Y angle, in radians. + /// The . + public ProjectiveTransformBuilder AppendSkewRadians(float radiansX, float radiansY) + => this.Append(size => new Matrix4x4(TransformUtils.CreateSkewMatrixRadians(radiansX, radiansY, size))); + + /// + /// Appends a skew matrix using the given angles in degrees at the given origin. + /// + /// The X angle, in degrees. + /// The Y angle, in degrees. + /// The skew origin point. + /// The . + public ProjectiveTransformBuilder AppendSkewDegrees(float degreesX, float degreesY, Vector2 origin) + => this.AppendSkewRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), origin); + + /// + /// Appends a skew matrix using the given angles in radians at the given origin. + /// + /// The X angle, in radians. + /// The Y angle, in radians. + /// The skew origin point. + /// The . + public ProjectiveTransformBuilder AppendSkewRadians(float radiansX, float radiansY, Vector2 origin) + => this.AppendMatrix(new Matrix4x4(Matrix3x2.CreateSkew(radiansX, radiansY, origin))); + + /// + /// Prepends a translation matrix from the given vector. + /// + /// The translation position. + /// The . + public ProjectiveTransformBuilder PrependTranslation(PointF position) + => this.PrependTranslation((Vector2)position); + + /// + /// Prepends a translation matrix from the given vector. + /// + /// The translation position. + /// The . + public ProjectiveTransformBuilder PrependTranslation(Vector2 position) + => this.PrependMatrix(Matrix4x4.CreateTranslation(new Vector3(position, 0))); + + /// + /// Appends a translation matrix from the given vector. + /// + /// The translation position. + /// The . + public ProjectiveTransformBuilder AppendTranslation(PointF position) + => this.AppendTranslation((Vector2)position); + + /// + /// Appends a translation matrix from the given vector. + /// + /// The translation position. + /// The . + public ProjectiveTransformBuilder AppendTranslation(Vector2 position) + => this.AppendMatrix(Matrix4x4.CreateTranslation(new Vector3(position, 0))); + + /// + /// Prepends a raw matrix. + /// + /// The matrix to prepend. + /// The . + public ProjectiveTransformBuilder PrependMatrix(Matrix4x4 matrix) => this.Prepend(_ => matrix); + + /// + /// Appends a raw matrix. + /// + /// The matrix to append. + /// The . + public ProjectiveTransformBuilder AppendMatrix(Matrix4x4 matrix) => this.Append(_ => matrix); + + /// + /// Returns the combined matrix for a given source size. + /// + /// The source image size. + /// The . + public Matrix4x4 BuildMatrix(Size sourceSize) => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize)); + + /// + /// Returns the combined matrix for a given source rectangle. + /// + /// The rectangle in the source image. + /// The . + public Matrix4x4 BuildMatrix(Rectangle sourceRectangle) + { + Guard.MustBeGreaterThan(sourceRectangle.Width, 0, nameof(sourceRectangle)); + Guard.MustBeGreaterThan(sourceRectangle.Height, 0, nameof(sourceRectangle)); + + // Translate the origin matrix to cater for source rectangle offsets. + var matrix = Matrix4x4.CreateTranslation(new Vector3(-sourceRectangle.Location, 0)); + + Size size = sourceRectangle.Size; + + foreach (Func factory in this.matrixFactories) + { + matrix *= factory(size); + } + + return matrix; + } + + private ProjectiveTransformBuilder Prepend(Func factory) + { + this.matrixFactories.Insert(0, factory); + return this; + } + + private ProjectiveTransformBuilder Append(Func factory) + { + this.matrixFactories.Add(factory); + return this; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/ProjectiveTransformHelper.cs b/src/ImageSharp/Processing/ProjectiveTransformHelper.cs deleted file mode 100644 index 4057ec586c..0000000000 --- a/src/ImageSharp/Processing/ProjectiveTransformHelper.cs +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Numerics; -using SixLabors.Primitives; - -namespace SixLabors.ImageSharp.Processing -{ - /// - /// Enumerates the various options which determine which side to taper - /// - public enum TaperSide - { - /// - /// Taper the left side - /// - Left, - - /// - /// Taper the top side - /// - Top, - - /// - /// Taper the right side - /// - Right, - - /// - /// Taper the bottom side - /// - Bottom - } - - /// - /// Enumerates the various options which determine how to taper corners - /// - public enum TaperCorner - { - /// - /// Taper the left or top corner - /// - LeftOrTop, - - /// - /// Taper the right or bottom corner - /// - RightOrBottom, - - /// - /// Taper the both sets of corners - /// - Both - } - - /// - /// Provides helper methods for working with generalized projective transforms. - /// - public static class ProjectiveTransformHelper - { - /// - /// Creates a matrix that performs a tapering projective transform. - /// - /// - /// The rectangular size of the image being transformed. - /// An enumeration that indicates the side of the rectangle that tapers. - /// An enumeration that indicates on which corners to taper the rectangle. - /// The amount to taper. - /// The - public static Matrix4x4 CreateTaperMatrix(Size size, TaperSide taperSide, TaperCorner taperCorner, float taperFraction) - { - Matrix4x4 matrix = Matrix4x4.Identity; - - switch (taperSide) - { - case TaperSide.Left: - matrix.M11 = taperFraction; - matrix.M22 = taperFraction; - matrix.M13 = (taperFraction - 1) / size.Width; - - switch (taperCorner) - { - case TaperCorner.RightOrBottom: - break; - - case TaperCorner.LeftOrTop: - matrix.M12 = size.Height * matrix.M13; - matrix.M32 = size.Height * (1 - taperFraction); - break; - - case TaperCorner.Both: - matrix.M12 = (size.Height * 0.5f) * matrix.M13; - matrix.M32 = size.Height * (1 - taperFraction) / 2; - break; - } - - break; - - case TaperSide.Top: - matrix.M11 = taperFraction; - matrix.M22 = taperFraction; - matrix.M23 = (taperFraction - 1) / size.Height; - - switch (taperCorner) - { - case TaperCorner.RightOrBottom: - break; - - case TaperCorner.LeftOrTop: - matrix.M21 = size.Width * matrix.M23; - matrix.M31 = size.Width * (1 - taperFraction); - break; - - case TaperCorner.Both: - matrix.M21 = (size.Width * 0.5f) * matrix.M23; - matrix.M31 = size.Width * (1 - taperFraction) / 2; - break; - } - - break; - - case TaperSide.Right: - matrix.M11 = 1 / taperFraction; - matrix.M13 = (1 - taperFraction) / (size.Width * taperFraction); - - switch (taperCorner) - { - case TaperCorner.RightOrBottom: - break; - - case TaperCorner.LeftOrTop: - matrix.M12 = size.Height * matrix.M13; - break; - - case TaperCorner.Both: - matrix.M12 = (size.Height * 0.5f) * matrix.M13; - break; - } - - break; - - case TaperSide.Bottom: - matrix.M22 = 1 / taperFraction; - matrix.M23 = (1 - taperFraction) / (size.Height * taperFraction); - - switch (taperCorner) - { - case TaperCorner.RightOrBottom: - break; - - case TaperCorner.LeftOrTop: - matrix.M21 = size.Width * matrix.M23; - break; - - case TaperCorner.Both: - matrix.M21 = (size.Width * 0.5f) * matrix.M23; - break; - } - - break; - } - - return matrix; - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/ResizeExtensions.cs b/src/ImageSharp/Processing/ResizeExtensions.cs index 8a370db693..7b6c14d7de 100644 --- a/src/ImageSharp/Processing/ResizeExtensions.cs +++ b/src/ImageSharp/Processing/ResizeExtensions.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing /// The image to resize. /// The resize options. /// The - /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image + /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options) where TPixel : struct, IPixel => source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize())); @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Processing /// The image to resize. /// The target image size. /// The - /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image + /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size) where TPixel : struct, IPixel => Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, false); @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing /// 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 + /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, bool compand) where TPixel : struct, IPixel => Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, compand); @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing /// The target image width. /// The target image height. /// The - /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image + /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height) where TPixel : struct, IPixel => Resize(source, width, height, KnownResamplers.Bicubic, false); @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing /// The target image height. /// 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 + /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, bool compand) where TPixel : struct, IPixel => Resize(source, width, height, KnownResamplers.Bicubic, compand); @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing /// The target image height. /// The to perform the resampling. /// The - /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image + /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler) where TPixel : struct, IPixel => Resize(source, width, height, sampler, false); @@ -99,7 +99,7 @@ namespace SixLabors.ImageSharp.Processing /// The to perform the resampling. /// 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 + /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, IResampler sampler, bool compand) where TPixel : struct, IPixel => Resize(source, size.Width, size.Height, sampler, new Rectangle(0, 0, size.Width, size.Height), compand); @@ -114,7 +114,7 @@ namespace SixLabors.ImageSharp.Processing /// The to perform the resampling. /// 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 + /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, bool compand) where TPixel : struct, IPixel => Resize(source, width, height, sampler, new Rectangle(0, 0, width, height), compand); @@ -136,7 +136,7 @@ namespace SixLabors.ImageSharp.Processing /// /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The - /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image + /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize( this IImageProcessingContext source, int width, @@ -161,7 +161,7 @@ namespace SixLabors.ImageSharp.Processing /// /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The - /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image + /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize( this IImageProcessingContext source, int width, diff --git a/src/ImageSharp/Processing/ResizeHelper.cs b/src/ImageSharp/Processing/ResizeHelper.cs index b9233937b1..3ae632162f 100644 --- a/src/ImageSharp/Processing/ResizeHelper.cs +++ b/src/ImageSharp/Processing/ResizeHelper.cs @@ -333,25 +333,21 @@ namespace SixLabors.ImageSharp.Processing return (new Size(sourceWidth, sourceWidth), new Rectangle(0, 0, sourceWidth, sourceHeight)); } - // 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 = sourceWidth - width; int heightDiff = sourceHeight - height; if (widthDiff < heightDiff) { + float sourceRatio = (float)sourceHeight / sourceWidth; destinationHeight = (int)MathF.Round(width * sourceRatio); height = destinationHeight; destinationWidth = width; } else if (widthDiff > heightDiff) { - destinationWidth = (int)MathF.Round(height / sourceRatio); + float sourceRatioInverse = (float)sourceWidth / sourceHeight; + destinationWidth = (int)MathF.Round(height * sourceRatioInverse); destinationHeight = height; width = destinationWidth; } @@ -360,12 +356,14 @@ namespace SixLabors.ImageSharp.Processing if (height > width) { destinationWidth = width; + float percentWidth = MathF.Abs(width / (float)sourceWidth); destinationHeight = (int)MathF.Round(sourceHeight * percentWidth); height = destinationHeight; } else { destinationHeight = height; + float percentHeight = MathF.Abs(height / (float)sourceHeight); destinationWidth = (int)MathF.Round(sourceWidth * percentHeight); width = destinationWidth; } diff --git a/src/ImageSharp/Processing/TaperCorner.cs b/src/ImageSharp/Processing/TaperCorner.cs new file mode 100644 index 0000000000..395b171424 --- /dev/null +++ b/src/ImageSharp/Processing/TaperCorner.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Enumerates the various options which determine how to taper corners + /// + public enum TaperCorner + { + /// + /// Taper the left or top corner + /// + LeftOrTop, + + /// + /// Taper the right or bottom corner + /// + RightOrBottom, + + /// + /// Taper the both sets of corners + /// + Both + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/TaperSide.cs b/src/ImageSharp/Processing/TaperSide.cs new file mode 100644 index 0000000000..226d11aed2 --- /dev/null +++ b/src/ImageSharp/Processing/TaperSide.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// Enumerates the various options which determine which side to taper + /// + public enum TaperSide + { + /// + /// Taper the left side + /// + Left, + + /// + /// Taper the top side + /// + Top, + + /// + /// Taper the right side + /// + Right, + + /// + /// Taper the bottom side + /// + Bottom + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/TransformExtensions.cs b/src/ImageSharp/Processing/TransformExtensions.cs index 0ec1e295d9..db14b6baf9 100644 --- a/src/ImageSharp/Processing/TransformExtensions.cs +++ b/src/ImageSharp/Processing/TransformExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.Primitives; @@ -14,109 +15,147 @@ namespace SixLabors.ImageSharp.Processing public static class TransformExtensions { /// - /// Transforms an image by the given matrix. + /// Performs an affine transform of an image. /// /// The pixel format. /// The image to transform. - /// The transformation matrix. + /// The affine transform builder. /// The - public static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix3x2 matrix) + public static IImageProcessingContext Transform( + this IImageProcessingContext source, + AffineTransformBuilder builder) where TPixel : struct, IPixel - => Transform(source, matrix, KnownResamplers.Bicubic); + => Transform(source, builder, KnownResamplers.Bicubic); /// - /// Transforms an image by the given matrix using the specified sampling algorithm. + /// Performs an affine transform of an image using the specified sampling algorithm. /// /// The pixel format. - /// The image to transform. - /// The transformation matrix. + /// The . + /// The affine transform builder. /// The to perform the resampling. /// The - public static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix3x2 matrix, IResampler sampler) + public static IImageProcessingContext Transform( + this IImageProcessingContext ctx, + AffineTransformBuilder builder, + IResampler sampler) where TPixel : struct, IPixel - => source.ApplyProcessor(new AffineTransformProcessor(matrix, sampler, source.GetCurrentSize())); + => ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler); /// - /// Transforms an image by the given matrix using the specified sampling algorithm - /// and a rectangle defining the transform origin in the source image and the size of the result image. + /// Performs an affine transform of an image using the specified sampling algorithm. /// /// The pixel format. - /// The image to transform. - /// The transformation matrix. + /// The . + /// The source rectangle + /// The affine transform builder. /// The to perform the resampling. - /// - /// The rectangle defining the transform origin in the source image, and the size of the result image. - /// /// The public static IImageProcessingContext Transform( - this IImageProcessingContext source, - Matrix3x2 matrix, - IResampler sampler, - Rectangle rectangle) + this IImageProcessingContext ctx, + Rectangle sourceRectangle, + AffineTransformBuilder builder, + IResampler sampler) where TPixel : struct, IPixel { - var t = Matrix3x2.CreateTranslation(-rectangle.Location); - Matrix3x2 combinedMatrix = t * matrix; - return source.ApplyProcessor(new AffineTransformProcessor(combinedMatrix, sampler, rectangle.Size)); + Matrix3x2 transform = builder.BuildMatrix(sourceRectangle); + Size targetDimensions = TransformUtils.GetTransformedSize(sourceRectangle.Size, transform); + return ctx.Transform(sourceRectangle, transform, targetDimensions, sampler); } /// - /// Transforms an image by the given matrix using the specified sampling algorithm, - /// cropping or extending the image according to . + /// Performs an affine transform of an image using the specified sampling algorithm. /// /// The pixel format. - /// The image to transform. - /// The transformation matrix. + /// The . + /// The source rectangle + /// The transformation matrix. + /// The size of the result image. /// The to perform the resampling. - /// The size of the destination image. /// The public static IImageProcessingContext Transform( - this IImageProcessingContext source, - Matrix3x2 matrix, - IResampler sampler, - Size destinationSize) + this IImageProcessingContext ctx, + Rectangle sourceRectangle, + Matrix3x2 transform, + Size targetDimensions, + IResampler sampler) where TPixel : struct, IPixel - => source.ApplyProcessor(new AffineTransformProcessor(matrix, sampler, destinationSize)); + { + return ctx.ApplyProcessor( + new AffineTransformProcessor(transform, sampler, targetDimensions), + sourceRectangle); + } /// - /// Transforms an image by the given matrix. + /// Performs a projective transform of an image. /// /// The pixel format. /// The image to transform. - /// The transformation matrix. + /// The affine transform builder. /// The - public static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix4x4 matrix) + public static IImageProcessingContext Transform( + this IImageProcessingContext source, + ProjectiveTransformBuilder builder) where TPixel : struct, IPixel - => Transform(source, matrix, KnownResamplers.Bicubic); + => Transform(source, builder, KnownResamplers.Bicubic); /// - /// Applies a projective transform to the image by the given matrix using the specified sampling algorithm. + /// Performs a projective transform of an image using the specified sampling algorithm. /// /// The pixel format. - /// The image to transform. - /// The transformation matrix. + /// The . + /// The projective transform builder. /// The to perform the resampling. /// The - public static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix4x4 matrix, IResampler sampler) + public static IImageProcessingContext Transform( + this IImageProcessingContext ctx, + ProjectiveTransformBuilder builder, + IResampler sampler) where TPixel : struct, IPixel - => source.ApplyProcessor(new ProjectiveTransformProcessor(matrix, sampler, source.GetCurrentSize())); + => ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler); /// - /// Applies a projective transform to the image by the given matrix using the specified sampling algorithm. - /// TODO: Should we be offsetting the matrix here? + /// Performs a projective transform of an image using the specified sampling algorithm. /// /// The pixel format. - /// The image to transform. - /// The transformation matrix. + /// The . + /// The source rectangle + /// The projective transform builder. /// The to perform the resampling. - /// The rectangle to constrain the transformed image to. /// The - internal static IImageProcessingContext Transform(this IImageProcessingContext source, Matrix4x4 matrix, IResampler sampler, Rectangle rectangle) + public static IImageProcessingContext Transform( + this IImageProcessingContext ctx, + Rectangle sourceRectangle, + ProjectiveTransformBuilder builder, + IResampler sampler) + where TPixel : struct, IPixel + { + Matrix4x4 transform = builder.BuildMatrix(sourceRectangle); + Size targetDimensions = TransformUtils.GetTransformedSize(sourceRectangle.Size, transform); + return ctx.Transform(sourceRectangle, transform, targetDimensions, sampler); + } + + /// + /// Performs a projective transform of an image using the specified sampling algorithm. + /// + /// The pixel format. + /// The . + /// The source rectangle + /// The transformation matrix. + /// The size of the result image. + /// The to perform the resampling. + /// The + public static IImageProcessingContext Transform( + this IImageProcessingContext ctx, + Rectangle sourceRectangle, + Matrix4x4 transform, + Size targetDimensions, + IResampler sampler) where TPixel : struct, IPixel { - var t = Matrix4x4.CreateTranslation(new Vector3(-rectangle.Location, 0)); - Matrix4x4 combinedMatrix = t * matrix; - return source.ApplyProcessor(new ProjectiveTransformProcessor(combinedMatrix, sampler, rectangle.Size)); + return ctx.ApplyProcessor( + new ProjectiveTransformProcessor(transform, sampler, targetDimensions), + sourceRectangle); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Codecs/CopyPixels.cs b/tests/ImageSharp.Benchmarks/Codecs/CopyPixels.cs deleted file mode 100644 index d55c231a73..0000000000 --- a/tests/ImageSharp.Benchmarks/Codecs/CopyPixels.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Threading.Tasks; - -using BenchmarkDotNet.Attributes; - -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Benchmarks.Codecs -{ - public class CopyPixels : BenchmarkBase - { - [Benchmark(Baseline = true, Description = "PixelAccessor Copy by indexer")] - public Rgba32 CopyByPixelAccesor() - { - using (var source = new Image(1024, 768)) - using (var target = new Image(1024, 768)) - { - Buffer2D sourcePixels = source.GetRootFramePixelBuffer(); - Buffer2D targetPixels = target.GetRootFramePixelBuffer(); - ParallelFor.WithConfiguration( - 0, - source.Height, - Configuration.Default, - y => - { - for (int x = 0; x < source.Width; x++) - { - targetPixels[x, y] = sourcePixels[x, y]; - } - }); - - return targetPixels[0, 0]; - } - } - - [Benchmark(Description = "PixelAccessor Copy by Span")] - public Rgba32 CopyByPixelAccesorSpan() - { - using (var source = new Image(1024, 768)) - using (var target = new Image(1024, 768)) - { - Buffer2D sourcePixels = source.GetRootFramePixelBuffer(); - Buffer2D targetPixels = target.GetRootFramePixelBuffer(); - ParallelFor.WithConfiguration( - 0, - source.Height, - Configuration.Default, - y => - { - Span sourceRow = sourcePixels.GetRowSpan(y); - Span targetRow = targetPixels.GetRowSpan(y); - - for (int x = 0; x < source.Width; x++) - { - targetRow[x] = sourceRow[x]; - } - }); - - return targetPixels[0, 0]; - } - } - - [Benchmark(Description = "Copy by indexer")] - public Rgba32 Copy() - { - using (var source = new Image(1024, 768)) - using (var target = new Image(1024, 768)) - { - ParallelFor.WithConfiguration( - 0, - source.Height, - Configuration.Default, - y => - { - for (int x = 0; x < source.Width; x++) - { - target[x, y] = source[x, y]; - } - }); - - return target[0, 0]; - } - } - - [Benchmark(Description = "Copy by Span")] - public Rgba32 CopySpan() - { - using (var source = new Image(1024, 768)) - using (var target = new Image(1024, 768)) - { - ParallelFor.WithConfiguration( - 0, - source.Height, - Configuration.Default, - y => - { - Span sourceRow = source.Frames.RootFrame.GetPixelRowSpan(y); - Span targetRow = target.Frames.RootFrame.GetPixelRowSpan(y); - - for (int x = 0; x < source.Width; x++) - { - targetRow[x] = sourceRow[x]; - } - }); - - return target[0, 0]; - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Codecs/EncodeGif.cs b/tests/ImageSharp.Benchmarks/Codecs/EncodeGif.cs index 12e74ccdbb..89eb63d629 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/EncodeGif.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/EncodeGif.cs @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs public void GifCore() { // Try to get as close to System.Drawing's output as possible - var options = new GifEncoder { Quantizer = new PaletteQuantizer(false) }; + var options = new GifEncoder { Quantizer = new WebSafePaletteQuantizer(false) }; using (var memoryStream = new MemoryStream()) { this.bmpCore.SaveAsGif(memoryStream, options); diff --git a/tests/ImageSharp.Benchmarks/Codecs/EncodeGifMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/EncodeGifMultiple.cs index 9b94347f34..bf9627f4c1 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/EncodeGifMultiple.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/EncodeGifMultiple.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs this.ForEachImageSharpImage((img, ms) => { // Try to get as close to System.Drawing's output as possible - var options = new GifEncoder { Quantizer = new PaletteQuantizer(false) }; + var options = new GifEncoder { Quantizer = new WebSafePaletteQuantizer(false) }; img.Save(ms, options); return null; }); } diff --git a/tests/ImageSharp.Benchmarks/Codecs/EncodeIndexedPng.cs b/tests/ImageSharp.Benchmarks/Codecs/EncodeIndexedPng.cs index 962b34eb7c..639d1594ee 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/EncodeIndexedPng.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/EncodeIndexedPng.cs @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs { using (var memoryStream = new MemoryStream()) { - var options = new PngEncoder { Quantizer = KnownQuantizers.Palette }; + var options = new PngEncoder { Quantizer = KnownQuantizers.WebSafe }; this.bmpCore.SaveAsPng(memoryStream, options); } } @@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs { using (var memoryStream = new MemoryStream()) { - var options = new PngEncoder { Quantizer = new PaletteQuantizer(false) }; + var options = new PngEncoder { Quantizer = new WebSafePaletteQuantizer(false) }; this.bmpCore.SaveAsPng(memoryStream, options); } } diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs new file mode 100644 index 0000000000..bf9b1af338 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo1x1.cs @@ -0,0 +1,133 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp.Formats.Jpeg.Components; +using SixLabors.ImageSharp.Memory; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations +{ + public class Block8x8F_CopyTo1x1 + { + private Block8x8F block; + + private Buffer2D buffer; + + private BufferArea destArea; + + [GlobalSetup] + public void Setup() + { + if (!SimdUtils.IsAvx2CompatibleArchitecture) + { + throw new InvalidOperationException("Benchmark Block8x8F_CopyTo1x1 is invalid on platforms without AVX2 support."); + } + + this.buffer = Configuration.Default.MemoryAllocator.Allocate2D(1000, 500); + this.destArea = this.buffer.GetArea(200, 100, 64, 64); + } + + [Benchmark(Baseline = true)] + public void Original() + { + ref byte selfBase = ref Unsafe.As(ref this.block); + ref byte destBase = ref Unsafe.As(ref this.destArea.GetReferenceToOrigin()); + int destStride = this.destArea.Stride * sizeof(float); + + CopyRowImpl(ref selfBase, ref destBase, destStride, 0); + CopyRowImpl(ref selfBase, ref destBase, destStride, 1); + CopyRowImpl(ref selfBase, ref destBase, destStride, 2); + CopyRowImpl(ref selfBase, ref destBase, destStride, 3); + CopyRowImpl(ref selfBase, ref destBase, destStride, 4); + CopyRowImpl(ref selfBase, ref destBase, destStride, 5); + CopyRowImpl(ref selfBase, ref destBase, destStride, 6); + CopyRowImpl(ref selfBase, ref destBase, destStride, 7); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void CopyRowImpl(ref byte selfBase, ref byte destBase, int destStride, int row) + { + ref byte s = ref Unsafe.Add(ref selfBase, row * 8 * sizeof(float)); + ref byte d = ref Unsafe.Add(ref destBase, row * destStride); + Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float)); + } + + [Benchmark] + public void UseVector8() + { + ref Block8x8F s = ref this.block; + ref float origin = ref this.destArea.GetReferenceToOrigin(); + int stride = this.destArea.Stride; + + ref Vector d0 = ref Unsafe.As>(ref origin); + ref Vector d1 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride)); + ref Vector d2 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 2)); + ref Vector d3 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 3)); + ref Vector d4 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 4)); + ref Vector d5 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 5)); + ref Vector d6 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 6)); + ref Vector d7 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 7)); + + Vector row0 = Unsafe.As>(ref s.V0L); + Vector row1 = Unsafe.As>(ref s.V1L); + Vector row2 = Unsafe.As>(ref s.V2L); + Vector row3 = Unsafe.As>(ref s.V3L); + Vector row4 = Unsafe.As>(ref s.V4L); + Vector row5 = Unsafe.As>(ref s.V5L); + Vector row6 = Unsafe.As>(ref s.V6L); + Vector row7 = Unsafe.As>(ref s.V7L); + + d0 = row0; + d1 = row1; + d2 = row2; + d3 = row3; + d4 = row4; + d5 = row5; + d6 = row6; + d7 = row7; + } + + [Benchmark] + public void UseVector8_V2() + { + ref Block8x8F s = ref this.block; + ref float origin = ref this.destArea.GetReferenceToOrigin(); + int stride = this.destArea.Stride; + + ref Vector d0 = ref Unsafe.As>(ref origin); + ref Vector d1 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride)); + ref Vector d2 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 2)); + ref Vector d3 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 3)); + ref Vector d4 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 4)); + ref Vector d5 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 5)); + ref Vector d6 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 6)); + ref Vector d7 = ref Unsafe.As>(ref Unsafe.Add(ref origin, stride * 7)); + + d0 = Unsafe.As>(ref s.V0L); + d1 = Unsafe.As>(ref s.V1L); + d2 = Unsafe.As>(ref s.V2L); + d3 = Unsafe.As>(ref s.V3L); + d4 = Unsafe.As>(ref s.V4L); + d5 = Unsafe.As>(ref s.V5L); + d6 = Unsafe.As>(ref s.V6L); + d7 = Unsafe.As>(ref s.V7L); + } + + // RESULTS: + // + // Method | Mean | Error | StdDev | Scaled | + // -------------- |---------:|----------:|----------:|-------:| + // Original | 22.53 ns | 0.1660 ns | 0.1553 ns | 1.00 | + // UseVector8 | 21.59 ns | 0.3079 ns | 0.2571 ns | 0.96 | + // UseVector8_V2 | 22.57 ns | 0.1699 ns | 0.1506 ns | 1.00 | + // + // Conclusion: + // Doesn't worth to bother with this + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs new file mode 100644 index 0000000000..65176af5bb --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_CopyTo2x2.cs @@ -0,0 +1,404 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using System.Runtime.CompilerServices; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp.Formats.Jpeg.Components; +using SixLabors.ImageSharp.Memory; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations +{ + public class Block8x8F_CopyTo2x2 + { + private Block8x8F block; + + private Buffer2D buffer; + + private BufferArea destArea; + + [GlobalSetup] + public void Setup() + { + this.buffer = Configuration.Default.MemoryAllocator.Allocate2D(1000, 500); + this.destArea = this.buffer.GetArea(200, 100, 128, 128); + } + + [Benchmark(Baseline = true)] + public void Original() + { + ref float destBase = ref this.destArea.GetReferenceToOrigin(); + int destStride = this.destArea.Stride; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2(ref src, ref destBase, 7, destStride); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl2x2(ref Block8x8F src, ref float destBase, int row, int destStride) + { + ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); + ref float destLocalOrigo = ref Unsafe.Add(ref destBase, row * 2 * destStride); + + Unsafe.Add(ref destLocalOrigo, 0) = selfLeft.X; + Unsafe.Add(ref destLocalOrigo, 1) = selfLeft.X; + Unsafe.Add(ref destLocalOrigo, 2) = selfLeft.Y; + Unsafe.Add(ref destLocalOrigo, 3) = selfLeft.Y; + Unsafe.Add(ref destLocalOrigo, 4) = selfLeft.Z; + Unsafe.Add(ref destLocalOrigo, 5) = selfLeft.Z; + Unsafe.Add(ref destLocalOrigo, 6) = selfLeft.W; + Unsafe.Add(ref destLocalOrigo, 7) = selfLeft.W; + + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 0) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 1) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 2) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 3) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 4) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 5) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 6) = selfRight.W; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, 8), 7) = selfRight.W; + + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 0) = selfLeft.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 1) = selfLeft.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 2) = selfLeft.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 3) = selfLeft.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 4) = selfLeft.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 5) = selfLeft.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 6) = selfLeft.W; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride), 7) = selfLeft.W; + + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 0) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 1) = selfRight.X; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 2) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 3) = selfRight.Y; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 4) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 5) = selfRight.Z; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 6) = selfRight.W; + Unsafe.Add(ref Unsafe.Add(ref destLocalOrigo, destStride + 8), 7) = selfRight.W; + } + + [Benchmark] + public void Original_V2() + { + ref float destBase = ref this.destArea.GetReferenceToOrigin(); + int destStride = this.destArea.Stride; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2_V2(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2_V2(ref src, ref destBase, 7, destStride); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl2x2_V2(ref Block8x8F src, ref float destBase, int row, int destStride) + { + ref Vector4 selfLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 selfRight = ref Unsafe.Add(ref selfLeft, 1); + ref float dest0 = ref Unsafe.Add(ref destBase, row * 2 * destStride); + + Unsafe.Add(ref dest0, 0) = selfLeft.X; + Unsafe.Add(ref dest0, 1) = selfLeft.X; + Unsafe.Add(ref dest0, 2) = selfLeft.Y; + Unsafe.Add(ref dest0, 3) = selfLeft.Y; + Unsafe.Add(ref dest0, 4) = selfLeft.Z; + Unsafe.Add(ref dest0, 5) = selfLeft.Z; + Unsafe.Add(ref dest0, 6) = selfLeft.W; + Unsafe.Add(ref dest0, 7) = selfLeft.W; + + ref float dest1 = ref Unsafe.Add(ref dest0, 8); + + Unsafe.Add(ref dest1, 0) = selfRight.X; + Unsafe.Add(ref dest1, 1) = selfRight.X; + Unsafe.Add(ref dest1, 2) = selfRight.Y; + Unsafe.Add(ref dest1, 3) = selfRight.Y; + Unsafe.Add(ref dest1, 4) = selfRight.Z; + Unsafe.Add(ref dest1, 5) = selfRight.Z; + Unsafe.Add(ref dest1, 6) = selfRight.W; + Unsafe.Add(ref dest1, 7) = selfRight.W; + + ref float dest2 = ref Unsafe.Add(ref dest0, destStride); + + Unsafe.Add(ref dest2, 0) = selfLeft.X; + Unsafe.Add(ref dest2, 1) = selfLeft.X; + Unsafe.Add(ref dest2, 2) = selfLeft.Y; + Unsafe.Add(ref dest2, 3) = selfLeft.Y; + Unsafe.Add(ref dest2, 4) = selfLeft.Z; + Unsafe.Add(ref dest2, 5) = selfLeft.Z; + Unsafe.Add(ref dest2, 6) = selfLeft.W; + Unsafe.Add(ref dest2, 7) = selfLeft.W; + + ref float dest3 = ref Unsafe.Add(ref dest2, 8); + + Unsafe.Add(ref dest3, 0) = selfRight.X; + Unsafe.Add(ref dest3, 1) = selfRight.X; + Unsafe.Add(ref dest3, 2) = selfRight.Y; + Unsafe.Add(ref dest3, 3) = selfRight.Y; + Unsafe.Add(ref dest3, 4) = selfRight.Z; + Unsafe.Add(ref dest3, 5) = selfRight.Z; + Unsafe.Add(ref dest3, 6) = selfRight.W; + Unsafe.Add(ref dest3, 7) = selfRight.W; + } + + [Benchmark] + public void UseVector2() + { + ref Vector2 destBase = ref Unsafe.As(ref this.destArea.GetReferenceToOrigin()); + int destStride = this.destArea.Stride / 2; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2_Vector2(ref src, ref destBase, 7, destStride); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl2x2_Vector2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) + { + ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); + + ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); + ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4); + ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); + ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4); + + var xLeft = new Vector2(sLeft.X); + var yLeft = new Vector2(sLeft.Y); + var zLeft = new Vector2(sLeft.Z); + var wLeft = new Vector2(sLeft.W); + + var xRight = new Vector2(sRight.X); + var yRight = new Vector2(sRight.Y); + var zRight = new Vector2(sRight.Z); + var wRight = new Vector2(sRight.W); + + dTopLeft = xLeft; + Unsafe.Add(ref dTopLeft, 1) = yLeft; + Unsafe.Add(ref dTopLeft, 2) = zLeft; + Unsafe.Add(ref dTopLeft, 3) = wLeft; + + dTopRight = xRight; + Unsafe.Add(ref dTopRight, 1) = yRight; + Unsafe.Add(ref dTopRight, 2) = zRight; + Unsafe.Add(ref dTopRight, 3) = wRight; + + dBottomLeft = xLeft; + Unsafe.Add(ref dBottomLeft, 1) = yLeft; + Unsafe.Add(ref dBottomLeft, 2) = zLeft; + Unsafe.Add(ref dBottomLeft, 3) = wLeft; + + dBottomRight = xRight; + Unsafe.Add(ref dBottomRight, 1) = yRight; + Unsafe.Add(ref dBottomRight, 2) = zRight; + Unsafe.Add(ref dBottomRight, 3) = wRight; + } + + [Benchmark] + public void UseVector4() + { + ref Vector2 destBase = ref Unsafe.As(ref this.destArea.GetReferenceToOrigin()); + int destStride = this.destArea.Stride / 2; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2_Vector4(ref src, ref destBase, 7, destStride); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl2x2_Vector4(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) + { + ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); + + ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); + ref Vector2 dTopRight = ref Unsafe.Add(ref dTopLeft, 4); + ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); + ref Vector2 dBottomRight = ref Unsafe.Add(ref dBottomLeft, 4); + + var xLeft = new Vector4(sLeft.X); + var yLeft = new Vector4(sLeft.Y); + var zLeft = new Vector4(sLeft.Z); + var wLeft = new Vector4(sLeft.W); + + var xRight = new Vector4(sRight.X); + var yRight = new Vector4(sRight.Y); + var zRight = new Vector4(sRight.Z); + var wRight = new Vector4(sRight.W); + + Unsafe.As(ref dTopLeft) = xLeft; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft; + + Unsafe.As(ref dTopRight) = xRight; + Unsafe.As(ref Unsafe.Add(ref dTopRight, 1)) = yRight; + Unsafe.As(ref Unsafe.Add(ref dTopRight, 2)) = zRight; + Unsafe.As(ref Unsafe.Add(ref dTopRight, 3)) = wRight; + + Unsafe.As(ref dBottomLeft) = xLeft; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft; + + Unsafe.As(ref dBottomRight) = xRight; + Unsafe.As(ref Unsafe.Add(ref dBottomRight, 1)) = yRight; + Unsafe.As(ref Unsafe.Add(ref dBottomRight, 2)) = zRight; + Unsafe.As(ref Unsafe.Add(ref dBottomRight, 3)) = wRight; + } + + [Benchmark] + public void UseVector4_SafeRightCorner() + { + ref Vector2 destBase = ref Unsafe.As(ref this.destArea.GetReferenceToOrigin()); + int destStride = this.destArea.Stride / 2; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2_Vector4_SafeRightCorner(ref src, ref destBase, 7, destStride); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl2x2_Vector4_SafeRightCorner(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) + { + ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); + + ref Vector2 dTopLeft = ref Unsafe.Add(ref destBase, 2 * row * destStride); + ref Vector2 dBottomLeft = ref Unsafe.Add(ref dTopLeft, destStride); + + var xLeft = new Vector4(sLeft.X); + var yLeft = new Vector4(sLeft.Y); + var zLeft = new Vector4(sLeft.Z); + var wLeft = new Vector4(sLeft.W); + + var xRight = new Vector4(sRight.X); + var yRight = new Vector4(sRight.Y); + var zRight = new Vector4(sRight.Z); + var wRight = new Vector2(sRight.W); + + Unsafe.As(ref dTopLeft) = xLeft; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 1)) = yLeft; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 2)) = zLeft; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 3)) = wLeft; + + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 4)) = xRight; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 5)) = yRight; + Unsafe.As(ref Unsafe.Add(ref dTopLeft, 6)) = zRight; + Unsafe.Add(ref dTopLeft, 7) = wRight; + + Unsafe.As(ref dBottomLeft) = xLeft; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 1)) = yLeft; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 2)) = zLeft; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 3)) = wLeft; + + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 4)) = xRight; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 5)) = yRight; + Unsafe.As(ref Unsafe.Add(ref dBottomLeft, 6)) = zRight; + Unsafe.Add(ref dBottomLeft, 7) = wRight; + } + + + [Benchmark] + public void UseVector4_V2() + { + ref Vector2 destBase = ref Unsafe.As(ref this.destArea.GetReferenceToOrigin()); + int destStride = this.destArea.Stride / 2; + + ref Block8x8F src = ref this.block; + + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 0, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 1, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 2, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 3, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 4, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 5, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 6, destStride); + WidenCopyImpl2x2_Vector4_V2(ref src, ref destBase, 7, destStride); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WidenCopyImpl2x2_Vector4_V2(ref Block8x8F src, ref Vector2 destBase, int row, int destStride) + { + ref Vector4 sLeft = ref Unsafe.Add(ref src.V0L, 2 * row); + ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); + + int offset = 2 * row * destStride; + ref Vector4 dTopLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset)); + ref Vector4 dBottomLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset + destStride)); + + var xyLeft = new Vector4(sLeft.X); + xyLeft.Z = sLeft.Y; + xyLeft.W = sLeft.Y; + + var zwLeft = new Vector4(sLeft.Z); + zwLeft.Z = sLeft.W; + zwLeft.W = sLeft.W; + + var xyRight = new Vector4(sRight.X); + xyRight.Z = sRight.Y; + xyRight.W = sRight.Y; + + var zwRight = new Vector4(sRight.Z); + zwRight.Z = sRight.W; + zwRight.W = sRight.W; + + dTopLeft = xyLeft; + Unsafe.Add(ref dTopLeft, 1) = zwLeft; + Unsafe.Add(ref dTopLeft, 2) = xyRight; + Unsafe.Add(ref dTopLeft, 3) = zwRight; + + dBottomLeft = xyLeft; + Unsafe.Add(ref dBottomLeft, 1) = zwLeft; + Unsafe.Add(ref dBottomLeft, 2) = xyRight; + Unsafe.Add(ref dBottomLeft, 3) = zwRight; + } + + // RESULTS: + // Method | Mean | Error | StdDev | Scaled | ScaledSD | + // --------------------------- |---------:|----------:|----------:|-------:|---------:| + // Original | 92.69 ns | 2.4722 ns | 2.7479 ns | 1.00 | 0.00 | + // Original_V2 | 91.72 ns | 1.2089 ns | 1.0095 ns | 0.99 | 0.03 | + // UseVector2 | 86.70 ns | 0.5873 ns | 0.5206 ns | 0.94 | 0.03 | + // UseVector4 | 55.42 ns | 0.2482 ns | 0.2322 ns | 0.60 | 0.02 | + // UseVector4_SafeRightCorner | 58.97 ns | 0.4152 ns | 0.3884 ns | 0.64 | 0.02 | + // UseVector4_V2 | 41.88 ns | 0.3531 ns | 0.3303 ns | 0.45 | 0.01 | + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs similarity index 96% rename from tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs rename to tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs index fcc5f9a592..5502475d43 100644 --- a/tests/ImageSharp.Benchmarks/General/Block8x8F_DivideRound.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_DivideRound.cs @@ -10,7 +10,7 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components; // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations { /// /// The goal of this benchmark is to measure the following Jpeg-related scenario: @@ -24,8 +24,8 @@ namespace SixLabors.ImageSharp.Benchmarks.General private static readonly Vector4 MinusOne = new Vector4(-1); private static readonly Vector4 Half = new Vector4(0.5f); - private Block8x8F inputDividend = default(Block8x8F); - private Block8x8F inputDivisior = default(Block8x8F); + private Block8x8F inputDividend; + private Block8x8F inputDivisior; [GlobalSetup] public void Setup() diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs new file mode 100644 index 0000000000..29ee402a00 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_LoadFromInt16.cs @@ -0,0 +1,53 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming + +using System; +using System.Numerics; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp.Formats.Jpeg.Components; + +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations +{ + public class Block8x8F_LoadFromInt16 + { + private Block8x8 source; + + private Block8x8F dest = default; + + [GlobalSetup] + public void Setup() + { + if (Vector.Count != 8) + { + throw new NotSupportedException("Vector.Count != 8"); + } + + for (short i = 0; i < Block8x8F.Size; i++) + { + this.source[i] = i; + } + } + + [Benchmark(Baseline = true)] + public void Scalar() + { + this.dest.LoadFromInt16Scalar(ref this.source); + } + + [Benchmark] + public void ExtendedAvx2() + { + this.dest.LoadFromInt16ExtendedAvx2(ref this.source); + } + + // RESULT: + // Method | Mean | Error | StdDev | Scaled | + // ------------- |---------:|----------:|----------:|-------:| + // Scalar | 34.88 ns | 0.3296 ns | 0.3083 ns | 1.00 | + // ExtendedAvx2 | 21.58 ns | 0.2125 ns | 0.1884 ns | 0.62 | + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs similarity index 90% rename from tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs rename to tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs index 200af64c25..c7b5802c4f 100644 --- a/tests/ImageSharp.Benchmarks/General/Block8x8F_Round.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/BlockOperations/Block8x8F_Round.cs @@ -1,4 +1,7 @@ -// ReSharper disable InconsistentNaming +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming using System; using System.Numerics; @@ -8,7 +11,7 @@ using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Formats.Jpeg.Components; -namespace SixLabors.ImageSharp.Benchmarks.General +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg.BlockOperations { public class Block8x8F_Round { diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs deleted file mode 100644 index 9b968e4db3..0000000000 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Drawing; -using System.IO; -using BenchmarkDotNet.Attributes; - -using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Tests; -using CoreSize = SixLabors.Primitives.Size; -using SDImage = System.Drawing.Image; - -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg -{ - [Config(typeof(Config.ShortClr))] - public class DecodeJpeg : BenchmarkBase - { - private byte[] jpegBytes; - - private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - - [Params(TestImages.Jpeg.Baseline.Jpeg420Exif, TestImages.Jpeg.Baseline.Calliphora)] - public string TestImage { get; set; } - - [GlobalSetup] - public void ReadImages() - { - if (this.jpegBytes == null) - { - this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); - } - } - - [Benchmark(Baseline = true, Description = "Decode Jpeg - System.Drawing")] - public Size JpegSystemDrawing() - { - using (var memoryStream = new MemoryStream(this.jpegBytes)) - { - using (var image = SDImage.FromStream(memoryStream)) - { - return image.Size; - } - } - } - - [Benchmark(Description = "Decode Jpeg - ImageSharp")] - public CoreSize JpegImageSharp() - { - using (var memoryStream = new MemoryStream(this.jpegBytes)) - { - using (var image = Image.Load(memoryStream, new JpegDecoder())) - { - return new CoreSize(image.Width, image.Height); - } - } - } - } -} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs deleted file mode 100644 index be0fe76b82..0000000000 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpegMultiple.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Collections.Generic; -using BenchmarkDotNet.Attributes; -using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.PixelFormats; -using SDImage = System.Drawing.Image; - -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg -{ - [Config(typeof(Config.ShortClr))] - public class DecodeJpegMultiple : MultiImageBenchmarkBase - { - protected override IEnumerable InputImageSubfoldersOrFiles => new[] - { - "Jpg/baseline", - "Jpg/progressive", - }; - - protected override IEnumerable SearchPatterns => new[] { "*.jpg" }; - - [Benchmark(Description = "DecodeJpegMultiple - ImageSharp")] - public void DecodeJpegImageSharp() - { - this.ForEachStream(ms => Image.Load(ms, new JpegDecoder())); - } - - [Benchmark(Baseline = true, Description = "DecodeJpegMultiple - System.Drawing")] - public void DecodeJpegSystemDrawing() - { - this.ForEachStream(SDImage.FromStream); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs new file mode 100644 index 0000000000..f8a7556ca5 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_Aggregate.cs @@ -0,0 +1,48 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests; + +using SDImage = System.Drawing.Image; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +{ + /// + /// An expensive Jpeg benchmark, running on a wide range of input images, showing aggregate results. + /// + [Config(typeof(MultiImageBenchmarkBase.Config))] + public class DecodeJpeg_Aggregate : MultiImageBenchmarkBase + { + protected override IEnumerable InputImageSubfoldersOrFiles => + new[] + { + TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome, + TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr, + TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, + TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr, + TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr, + }; + + [Params(InputImageCategory.AllImages)] + public override InputImageCategory InputCategory { get; set; } + + [Benchmark] + public void ImageSharp() + { + this.ForEachStream(ms => Image.Load(ms, new JpegDecoder())); + } + + [Benchmark(Baseline = true)] + public void SystemDrawing() + { + this.ForEachStream(SDImage.FromStream); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs new file mode 100644 index 0000000000..fe112042ef --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/DecodeJpeg_ImageSpecific.cs @@ -0,0 +1,119 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Drawing; +using System.IO; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Jobs; + +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests; +using CoreSize = SixLabors.Primitives.Size; +using SDImage = System.Drawing.Image; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +{ + /// + /// Image-specific Jpeg benchmarks + /// + [Config(typeof(Config.ShortClr))] + public class DecodeJpeg_ImageSpecific + { + public class Config : ManualConfig + { + public Config() + { + // Uncomment if you want to use any of the diagnoser + this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser()); + } + + public class ShortClr : Benchmarks.Config + { + public ShortClr() + { + this.Add( + //Job.Clr.WithLaunchCount(1).WithWarmupCount(2).WithTargetCount(3), + Job.Core.WithLaunchCount(1).WithWarmupCount(2).WithTargetCount(3) + ); + } + } + } + + private byte[] jpegBytes; + + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + + [Params( + TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, + TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr, + + // The scaled result for the large image "ExifGetString750Transform_Huge420YCbCr" + // is almost the same as the result for Jpeg420Exif, + // which proves that the execution time for the most common YCbCr 420 path scales linearly. + // + // TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr, + + TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr + )] + public string TestImage { get; set; } + + + [GlobalSetup] + public void ReadImages() + { + if (this.jpegBytes == null) + { + this.jpegBytes = File.ReadAllBytes(this.TestImageFullPath); + } + } + + [Benchmark(Baseline = true, Description = "Decode Jpeg - System.Drawing")] + public Size JpegSystemDrawing() + { + using (var memoryStream = new MemoryStream(this.jpegBytes)) + { + using (var image = SDImage.FromStream(memoryStream)) + { + return image.Size; + } + } + } + + [Benchmark(Description = "Decode Jpeg - ImageSharp")] + public CoreSize JpegImageSharp() + { + using (var memoryStream = new MemoryStream(this.jpegBytes)) + { + using (var image = Image.Load(memoryStream, new JpegDecoder(){ IgnoreMetadata = true})) + { + return new CoreSize(image.Width, image.Height); + } + } + } + + // RESULTS (2018 November 4): + // + // BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134 + // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores + // Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC + // .NET Core SDK=2.1.403 + // [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT + // + // Method | TestImage | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Gen 1 | Gen 2 | Allocated | + // ------------------------------- |-------------------------------------------- |-----------:|-----------:|----------:|-------:|---------:|----------:|---------:|---------:|------------:| + // 'Decode Jpeg - System.Drawing' | Jpg/baseline/Lake.jpg | 6.117 ms | 0.3923 ms | 0.0222 ms | 1.00 | 0.00 | 62.5000 | - | - | 205.83 KB | + // 'Decode Jpeg - ImageSharp' | Jpg/baseline/Lake.jpg | 18.126 ms | 0.6023 ms | 0.0340 ms | 2.96 | 0.01 | - | - | - | 19.97 KB | + // | | | | | | | | | | | + // 'Decode Jpeg - System.Drawing' | Jpg/baseline/jpeg420exif.jpg | 17.063 ms | 2.6096 ms | 0.1474 ms | 1.00 | 0.00 | 218.7500 | - | - | 757.04 KB | + // 'Decode Jpeg - ImageSharp' | Jpg/baseline/jpeg420exif.jpg | 41.366 ms | 1.0115 ms | 0.0572 ms | 2.42 | 0.02 | - | - | - | 21.94 KB | + // | | | | | | | | | | | + // 'Decode Jpeg - System.Drawing' | Jpg/issues/Issue518-Bad-RST-Progressive.jpg | 428.282 ms | 94.9163 ms | 5.3629 ms | 1.00 | 0.00 | 2375.0000 | - | - | 7403.76 KB | + // 'Decode Jpeg - ImageSharp' | Jpg/issues/Issue518-Bad-RST-Progressive.jpg | 386.698 ms | 33.0065 ms | 1.8649 ms | 0.90 | 0.01 | 125.0000 | 125.0000 | 125.0000 | 35186.97 KB | + // | | | | | | | | | | | + // 'Decode Jpeg - System.Drawing' | Jpg/issues/issue750-exif-tranform.jpg | 95.192 ms | 3.1762 ms | 0.1795 ms | 1.00 | 0.00 | 1750.0000 | - | - | 5492.63 KB | + // 'Decode Jpeg - ImageSharp' | Jpg/issues/issue750-exif-tranform.jpg | 230.158 ms | 48.8128 ms | 2.7580 ms | 2.42 | 0.02 | 312.5000 | 312.5000 | 312.5000 | 58834.66 KB | + } +} diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs deleted file mode 100644 index 77ed828ef1..0000000000 --- a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using BenchmarkDotNet.Attributes; -using System; -using System.IO; -using SixLabors.ImageSharp.Tests; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Imaging; -using SixLabors.ImageSharp.Processing; -using SDImage = System.Drawing.Image; -using SixLabors.ImageSharp.Formats.Jpeg; - -namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg -{ - [Config(typeof(Config.ShortClr))] - public class LoadResizeSave : BenchmarkBase - { - private readonly Configuration configuration = new Configuration(new JpegConfigurationModule()); - - private byte[] sourceBytes; - - private byte[] destBytes; - - private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); - - [Params( - TestImages.Jpeg.Baseline.Jpeg420Exif - //, TestImages.Jpeg.Baseline.Calliphora - )] - public string TestImage { get; set; } - - [Params(false, true)] - public bool EnableParallelExecution { get; set; } - - [GlobalSetup] - public void Setup() - { - this.configuration.MaxDegreeOfParallelism = - this.EnableParallelExecution ? Environment.ProcessorCount : 1; - - if (this.sourceBytes == null) - { - this.sourceBytes = File.ReadAllBytes(this.TestImageFullPath); - } - - if (this.destBytes == null) - { - this.destBytes = new byte[this.sourceBytes.Length]; - } - } - - [Benchmark(Baseline = true)] - public void SystemDrawing() - { - using (var sourceStream = new MemoryStream(this.sourceBytes)) - using (var destStream = new MemoryStream(this.destBytes)) - using (var source = SDImage.FromStream(sourceStream)) - using (var destination = new Bitmap(source.Width / 4, source.Height / 4)) - { - using (var graphics = Graphics.FromImage(destination)) - { - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.DrawImage(source, 0, 0, 400, 400); - } - - destination.Save(destStream, ImageFormat.Jpeg); - } - } - - [Benchmark] - public void ImageSharp() - { - var source = Image.Load(this.configuration, this.sourceBytes, new JpegDecoder { IgnoreMetadata = true }); - using (source) - using (var destStream = new MemoryStream(this.destBytes)) - { - source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4)); - source.SaveAsJpeg(destStream); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_Aggregate.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_Aggregate.cs new file mode 100644 index 0000000000..e39cfa6ba2 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_Aggregate.cs @@ -0,0 +1,96 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.IO; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +{ + [Config(typeof(MultiImageBenchmarkBase.Config))] + public class LoadResizeSave_Aggregate : MultiImageBenchmarkBase + { + protected override IEnumerable InputImageSubfoldersOrFiles => + new[] + { + TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome, + TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr, + TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, + TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr, + TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr, + }; + + [Params(InputImageCategory.AllImages)] + public override InputImageCategory InputCategory { get; set; } + + private readonly Configuration configuration = new Configuration(new JpegConfigurationModule()); + + private byte[] destBytes; + + public override void Setup() + { + base.Setup(); + + this.configuration.MaxDegreeOfParallelism = 1; + const int MaxOutputSizeInBytes = 2 * 1024 * 1024; // ~2 MB + this.destBytes = new byte[MaxOutputSizeInBytes]; + } + + [Benchmark(Baseline = true)] + public void SystemDrawing() + { + this.ForEachStream( + sourceStream => + { + using (var destStream = new MemoryStream(this.destBytes)) + using (var source = System.Drawing.Image.FromStream(sourceStream)) + using (var destination = new Bitmap(source.Width / 4, source.Height / 4)) + { + using (var g = Graphics.FromImage(destination)) + { + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.PixelOffsetMode = PixelOffsetMode.HighQuality; + g.CompositingQuality = CompositingQuality.HighQuality; + g.DrawImage(source, 0, 0, 400, 400); + } + + destination.Save(destStream, ImageFormat.Jpeg); + } + + return null; + }); + } + + [Benchmark] + public void ImageSharp() + { + this.ForEachStream( + sourceStream => + { + using (var source = Image.Load( + this.configuration, + sourceStream, + new JpegDecoder { IgnoreMetadata = true })) + { + using (var destStream = new MemoryStream(this.destBytes)) + { + source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4)); + source.SaveAsJpeg(destStream); + } + } + + return null; + }); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_ImageSpecific.cs b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_ImageSpecific.cs new file mode 100644 index 0000000000..1834f77eaf --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Codecs/Jpeg/LoadResizeSave_ImageSpecific.cs @@ -0,0 +1,107 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using BenchmarkDotNet.Attributes; +using System; +using System.IO; +using SixLabors.ImageSharp.Tests; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using SixLabors.ImageSharp.Processing; +using SDImage = System.Drawing.Image; +using SixLabors.ImageSharp.Formats.Jpeg; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Benchmarks.Codecs.Jpeg +{ + [Config(typeof(Config.ShortClr))] + public class LoadResizeSave_ImageSpecific + { + private readonly Configuration configuration = new Configuration(new JpegConfigurationModule()); + + private byte[] sourceBytes; + + private byte[] destBytes; + + private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); + + [Params( + TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, + TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr, + + TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr + )] + public string TestImage { get; set; } + + [Params(false, true)] + public bool ParallelExec { get; set; } + + [GlobalSetup] + public void Setup() + { + this.configuration.MaxDegreeOfParallelism = + this.ParallelExec ? Environment.ProcessorCount : 1; + + this.sourceBytes = File.ReadAllBytes(this.TestImageFullPath); + + this.destBytes = new byte[this.sourceBytes.Length * 2]; + } + + [Benchmark(Baseline = true)] + public void SystemDrawing() + { + using (var sourceStream = new MemoryStream(this.sourceBytes)) + using (var destStream = new MemoryStream(this.destBytes)) + using (var source = SDImage.FromStream(sourceStream)) + using (var destination = new Bitmap(source.Width / 4, source.Height / 4)) + { + using (var g = Graphics.FromImage(destination)) + { + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.PixelOffsetMode = PixelOffsetMode.HighQuality; + g.CompositingQuality = CompositingQuality.HighQuality; + g.DrawImage(source, 0, 0, 400, 400); + } + + destination.Save(destStream, ImageFormat.Jpeg); + } + } + + [Benchmark] + public void ImageSharp() + { + var source = Image.Load(this.configuration, this.sourceBytes, new JpegDecoder { IgnoreMetadata = true }); + using (source) + using (var destStream = new MemoryStream(this.destBytes)) + { + source.Mutate(c => c.Resize(source.Width / 4, source.Height / 4)); + source.SaveAsJpeg(destStream); + } + } + + // RESULTS (2018 October 31): + // + // BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134 + // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores + // Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC + // .NET Core SDK=2.1.403 + // [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT + // Job-ZPEZGV : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0 + // Job-SGOCJT : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT + // + // Method | Runtime | TestImage | ParallelExec | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Allocated | + // -------------- |-------- |----------------------------- |------------- |----------:|----------:|----------:|-------:|---------:|---------:|----------:| + // SystemDrawing | Clr | Jpg/baseline/jpeg420exif.jpg | False | 64.88 ms | 3.735 ms | 0.2110 ms | 1.00 | 0.00 | 250.0000 | 791.07 KB | + // ImageSharp | Clr | Jpg/baseline/jpeg420exif.jpg | False | 129.53 ms | 23.423 ms | 1.3234 ms | 2.00 | 0.02 | - | 50.09 KB | + // | | | | | | | | | | | + // SystemDrawing | Core | Jpg/baseline/jpeg420exif.jpg | False | 65.87 ms | 10.488 ms | 0.5926 ms | 1.00 | 0.00 | 250.0000 | 789.79 KB | + // ImageSharp | Core | Jpg/baseline/jpeg420exif.jpg | False | 92.00 ms | 7.241 ms | 0.4091 ms | 1.40 | 0.01 | - | 46.36 KB | + // | | | | | | | | | | | + // SystemDrawing | Clr | Jpg/baseline/jpeg420exif.jpg | True | 64.23 ms | 5.998 ms | 0.3389 ms | 1.00 | 0.00 | 250.0000 | 791.07 KB | + // ImageSharp | Clr | Jpg/baseline/jpeg420exif.jpg | True | 82.63 ms | 29.320 ms | 1.6566 ms | 1.29 | 0.02 | - | 57.59 KB | + // | | | | | | | | | | | + // SystemDrawing | Core | Jpg/baseline/jpeg420exif.jpg | True | 64.20 ms | 6.560 ms | 0.3707 ms | 1.00 | 0.00 | 250.0000 | 789.79 KB | + // ImageSharp | Core | Jpg/baseline/jpeg420exif.jpg | True | 68.08 ms | 18.376 ms | 1.0383 ms | 1.06 | 0.01 | - | 50.49 KB | + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs index f046f3033b..446c038596 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/MultiImageBenchmarkBase.cs @@ -3,6 +3,9 @@ // Licensed under the Apache License, Version 2.0. // +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Jobs; + using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Benchmarks.Codecs @@ -20,8 +23,27 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs using CoreImage = ImageSharp.Image; - public abstract class MultiImageBenchmarkBase : BenchmarkBase + public abstract class MultiImageBenchmarkBase { + public class Config : ManualConfig + { + public Config() + { + // Uncomment if you want to use any of the diagnoser + this.Add(new BenchmarkDotNet.Diagnosers.MemoryDiagnoser()); + } + + public class ShortClr : Benchmarks.Config + { + public ShortClr() + { + this.Add( + Job.Core.WithLaunchCount(1).WithWarmupCount(1).WithTargetCount(2) + ); + } + } + } + protected Dictionary FileNamesToBytes = new Dictionary(); protected Dictionary> FileNamesToImageSharpImages = new Dictionary>(); @@ -49,7 +71,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs /// /// Gets the file names containing these strings are substrings are not processed by the benchmark. /// - protected IEnumerable ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof", "CriticalEOF" }; + protected virtual IEnumerable ExcludeSubstringsInFileNames => new[] { "badeof", "BadEof", "CriticalEOF" }; /// /// Enumerates folders containing files OR files to be processed by the benchmark. @@ -87,7 +109,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs protected abstract IEnumerable InputImageSubfoldersOrFiles { get; } [GlobalSetup] - public void ReadImages() + public virtual void Setup() { if (!Vector.IsHardwareAccelerated) { @@ -107,11 +129,13 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs continue; } + string[] excludeStrings = this.ExcludeSubstringsInFileNames.Select(s => s.ToLower()).ToArray(); + string[] allFiles = this.SearchPatterns.SelectMany( f => Directory.EnumerateFiles(path, f, SearchOption.AllDirectories) - .Where(fn => !this.ExcludeSubstringsInFileNames.Any(w => fn.ToLower().Contains(w)))).ToArray(); + .Where(fn => !excludeStrings.Any(excludeStr => fn.ToLower().Contains(excludeStr)))).ToArray(); foreach (string fn in allFiles) { diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/FromRgba32Bytes.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/FromRgba32Bytes.cs new file mode 100644 index 0000000000..b964221764 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/FromRgba32Bytes.cs @@ -0,0 +1,92 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming + +using System.Buffers; +using System; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +{ + public abstract class FromRgba32Bytes + where TPixel : struct, IPixel + { + private IMemoryOwner destination; + + private IMemoryOwner source; + + private Configuration configuration; + + [Params( + 128, + 1024, + 2048)] + public int Count { get; set; } + + [GlobalSetup] + public void Setup() + { + this.configuration = Configuration.Default; + this.destination = this.configuration.MemoryAllocator.Allocate(this.Count); + this.source = this.configuration.MemoryAllocator.Allocate(this.Count * 4); + } + + [GlobalCleanup] + public void Cleanup() + { + this.destination.Dispose(); + this.source.Dispose(); + } + + //[Benchmark] + public void Naive() + { + Span s = this.source.GetSpan(); + Span d = this.destination.GetSpan(); + + for (int i = 0; i < this.Count; i++) + { + int i4 = i * 4; + var c = default(TPixel); + c.FromRgba32(new Rgba32(s[i4], s[i4 + 1], s[i4 + 2], s[i4 + 3])); + d[i] = c; + } + } + + [Benchmark(Baseline = true)] + public void CommonBulk() + { + new PixelOperations().FromRgba32Bytes(this.configuration, this.source.GetSpan(), this.destination.GetSpan(), this.Count); + } + + [Benchmark] + public void OptimizedBulk() + { + PixelOperations.Instance.FromRgba32Bytes(this.configuration, this.source.GetSpan(), this.destination.GetSpan(), this.Count); + } + } + + public class FromRgba32Bytes_ToRgba32 : FromRgba32Bytes + { + } + + public class FromRgba32Bytes_ToBgra32 : FromRgba32Bytes + { + // RESULTS: + // Method | Count | Mean | Error | StdDev | Scaled | + // -------------- |------ |-----------:|----------:|----------:|-------:| + // CommonBulk | 128 | 207.1 ns | 3.723 ns | 3.300 ns | 1.00 | + // OptimizedBulk | 128 | 166.5 ns | 1.204 ns | 1.005 ns | 0.80 | + // | | | | | | + // CommonBulk | 1024 | 1,333.9 ns | 12.426 ns | 11.624 ns | 1.00 | + // OptimizedBulk | 1024 | 974.1 ns | 18.803 ns | 16.669 ns | 0.73 | + // | | | | | | + // CommonBulk | 2048 | 2,625.4 ns | 30.143 ns | 26.721 ns | 1.00 | + // OptimizedBulk | 2048 | 1,843.0 ns | 20.505 ns | 18.177 ns | 0.70 | + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs new file mode 100644 index 0000000000..2b9573ed73 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/FromVector4.cs @@ -0,0 +1,130 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming + +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +{ + [Config(typeof(Config.ShortClr))] + public abstract class FromVector4 + where TPixel : struct, IPixel + { + protected IMemoryOwner source; + + protected IMemoryOwner destination; + + protected Configuration Configuration => Configuration.Default; + + [Params( + 64, + 2048 + )] + public int Count { get; set; } + + [GlobalSetup] + public void Setup() + { + this.destination = this.Configuration.MemoryAllocator.Allocate(this.Count); + this.source = this.Configuration.MemoryAllocator.Allocate(this.Count); + } + + [GlobalCleanup] + public void Cleanup() + { + this.destination.Dispose(); + this.source.Dispose(); + } + + //[Benchmark] + public void PerElement() + { + ref Vector4 s = ref MemoryMarshal.GetReference(this.source.GetSpan()); + ref TPixel d = ref MemoryMarshal.GetReference(this.destination.GetSpan()); + + for (int i = 0; i < this.Count; i++) + { + Unsafe.Add(ref d, i).FromVector4(Unsafe.Add(ref s, i)); + } + } + + [Benchmark] + public void PixelOperations_Base() + { + new PixelOperations().FromVector4(this.Configuration, this.source.GetSpan(), this.destination.GetSpan()); + } + + [Benchmark] + public void PixelOperations_Specialized() + { + PixelOperations.Instance.FromVector4(this.Configuration, this.source.GetSpan(), this.destination.GetSpan()); + } + } + + public class FromVector4Rgba32 : FromVector4 + { + [Benchmark] + public void FallbackIntrinsics128() + { + Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + + SimdUtils.FallbackIntrinsics128.BulkConvertNormalizedFloatToByteClampOverflows(sBytes, dFloats); + } + + [Benchmark(Baseline = true)] + public void BasicIntrinsics256() + { + Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + + SimdUtils.BasicIntrinsics256.BulkConvertNormalizedFloatToByteClampOverflows(sBytes, dFloats); + } + + [Benchmark] + public void ExtendedIntrinsic() + { + Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + + SimdUtils.ExtendedIntrinsics.BulkConvertNormalizedFloatToByteClampOverflows(sBytes, dFloats); + } + + // RESULTS (2018 October): + // Method | Runtime | Count | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Allocated | + // ---------------------------- |-------- |------ |-------------:|-------------:|------------:|-------:|---------:|-------:|----------:| + // FallbackIntrinsics128 | Clr | 64 | 340.38 ns | 22.319 ns | 1.2611 ns | 1.41 | 0.01 | - | 0 B | + // BasicIntrinsics256 | Clr | 64 | 240.79 ns | 11.421 ns | 0.6453 ns | 1.00 | 0.00 | - | 0 B | + // ExtendedIntrinsic | Clr | 64 | 199.09 ns | 124.239 ns | 7.0198 ns | 0.83 | 0.02 | - | 0 B | + // PixelOperations_Base | Clr | 64 | 647.99 ns | 24.003 ns | 1.3562 ns | 2.69 | 0.01 | 0.0067 | 24 B | + // PixelOperations_Specialized | Clr | 64 | 259.79 ns | 13.391 ns | 0.7566 ns | 1.08 | 0.00 | - | 0 B | <--- ceremonial overhead has been minimized! + // | | | | | | | | | | + // FallbackIntrinsics128 | Core | 64 | 234.64 ns | 12.320 ns | 0.6961 ns | 1.58 | 0.00 | - | 0 B | + // BasicIntrinsics256 | Core | 64 | 148.87 ns | 2.794 ns | 0.1579 ns | 1.00 | 0.00 | - | 0 B | + // ExtendedIntrinsic | Core | 64 | 94.06 ns | 10.015 ns | 0.5659 ns | 0.63 | 0.00 | - | 0 B | + // PixelOperations_Base | Core | 64 | 573.52 ns | 31.865 ns | 1.8004 ns | 3.85 | 0.01 | 0.0067 | 24 B | + // PixelOperations_Specialized | Core | 64 | 117.21 ns | 13.264 ns | 0.7494 ns | 0.79 | 0.00 | - | 0 B | + // | | | | | | | | | | + // FallbackIntrinsics128 | Clr | 2048 | 6,735.93 ns | 2,139.340 ns | 120.8767 ns | 1.71 | 0.03 | - | 0 B | + // BasicIntrinsics256 | Clr | 2048 | 3,929.29 ns | 334.027 ns | 18.8731 ns | 1.00 | 0.00 | - | 0 B | + // ExtendedIntrinsic | Clr | 2048 | 2,226.01 ns | 130.525 ns | 7.3749 ns |!! 0.57 | 0.00 | - | 0 B | <--- ExtendedIntrinsics rock! + // PixelOperations_Base | Clr | 2048 | 16,760.84 ns | 367.800 ns | 20.7814 ns | 4.27 | 0.02 | - | 24 B | <--- Extra copies using "Vector4 TPixel.ToVector4()" + // PixelOperations_Specialized | Clr | 2048 | 3,986.03 ns | 237.238 ns | 13.4044 ns | 1.01 | 0.00 | - | 0 B | <--- can't yet detect whether ExtendedIntrinsics are available :( + // | | | | | | | | | | + // FallbackIntrinsics128 | Core | 2048 | 6,644.65 ns | 2,677.090 ns | 151.2605 ns | 1.69 | 0.05 | - | 0 B | + // BasicIntrinsics256 | Core | 2048 | 3,923.70 ns | 1,971.760 ns | 111.4081 ns | 1.00 | 0.00 | - | 0 B | + // ExtendedIntrinsic | Core | 2048 | 2,092.32 ns | 375.657 ns | 21.2253 ns |!! 0.53 | 0.01 | - | 0 B | <--- ExtendedIntrinsics rock! + // PixelOperations_Base | Core | 2048 | 16,875.73 ns | 1,271.957 ns | 71.8679 ns | 4.30 | 0.10 | - | 24 B | + // PixelOperations_Specialized | Core | 2048 | 2,129.92 ns | 262.888 ns | 14.8537 ns |!! 0.54 | 0.01 | - | 0 B | <--- ExtendedIntrinsics rock! + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs deleted file mode 100644 index a5fa59ba07..0000000000 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -// ReSharper disable InconsistentNaming - -using System.Buffers; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -using BenchmarkDotNet.Attributes; - -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk -{ - [Config(typeof(Config.ShortClr))] - public abstract class PackFromVector4 - where TPixel : struct, IPixel - { - private IMemoryOwner source; - - private IMemoryOwner destination; - - [Params(16, 128, 512)] - public int Count { get; set; } - - [GlobalSetup] - public void Setup() - { - this.destination = Configuration.Default.MemoryAllocator.Allocate(this.Count); - this.source = Configuration.Default.MemoryAllocator.Allocate(this.Count); - } - - [GlobalCleanup] - public void Cleanup() - { - this.destination.Dispose(); - this.source.Dispose(); - } - - [Benchmark(Baseline = true)] - public void PerElement() - { - ref Vector4 s = ref MemoryMarshal.GetReference(this.source.GetSpan()); - ref TPixel d = ref MemoryMarshal.GetReference(this.destination.GetSpan()); - - for (int i = 0; i < this.Count; i++) - { - Unsafe.Add(ref d, i).PackFromVector4(Unsafe.Add(ref s, i)); - } - } - - [Benchmark] - public void CommonBulk() - { - new PixelOperations().PackFromVector4(this.source.GetSpan(), this.destination.GetSpan(), this.Count); - } - - [Benchmark] - public void OptimizedBulk() - { - PixelOperations.Instance.PackFromVector4(this.source.GetSpan(), this.destination.GetSpan(), this.Count); - } - } - - public class PackFromVector4_Rgba32 : PackFromVector4 - { - - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs deleted file mode 100644 index 7e7dfb3652..0000000000 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -// ReSharper disable InconsistentNaming - -using System.Buffers; -using System; - -using BenchmarkDotNet.Attributes; - -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk -{ - public abstract class PackFromXyzw - where TPixel : struct, IPixel - { - private IMemoryOwner destination; - - private IMemoryOwner source; - - [Params(16, 128, 1024)] - public int Count { get; set; } - - [GlobalSetup] - public void Setup() - { - this.destination = Configuration.Default.MemoryAllocator.Allocate(this.Count); - this.source = Configuration.Default.MemoryAllocator.Allocate(this.Count * 4); - } - - [GlobalCleanup] - public void Cleanup() - { - this.destination.Dispose(); - this.source.Dispose(); - } - - [Benchmark(Baseline = true)] - public void PerElement() - { - Span s = this.source.GetSpan(); - Span d = this.destination.GetSpan(); - - for (int i = 0; i < this.Count; i++) - { - int i4 = i * 4; - var c = default(TPixel); - c.PackFromRgba32(new Rgba32(s[i4], s[i4 + 1], s[i4 + 2], s[i4 + 3])); - d[i] = c; - } - } - - [Benchmark] - public void CommonBulk() - { - new PixelOperations().PackFromRgba32Bytes(this.source.GetSpan(), this.destination.GetSpan(), this.Count); - } - - [Benchmark] - public void OptimizedBulk() - { - PixelOperations.Instance.PackFromRgba32Bytes(this.source.GetSpan(), this.destination.GetSpan(), this.Count); - } - } - - public class PackFromXyzw_Rgba32 : PackFromXyzw - { - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/Rgb24Bytes.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/Rgb24Bytes.cs new file mode 100644 index 0000000000..294baa9d51 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/Rgb24Bytes.cs @@ -0,0 +1,60 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// ReSharper disable InconsistentNaming + +using System.Buffers; +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +{ + public abstract class Rgb24Bytes + where TPixel : struct, IPixel + { + private IMemoryOwner source; + + private IMemoryOwner destination; + + private Configuration configuration; + + [Params(16, 128, 1024)] + public int Count { get; set; } + + [GlobalSetup] + public void Setup() + { + this.configuration = Configuration.Default; + this.source = this.configuration.MemoryAllocator.Allocate(this.Count); + this.destination = this.configuration.MemoryAllocator.Allocate(this.Count * 3); + } + + [GlobalCleanup] + public void Cleanup() + { + this.source.Dispose(); + this.destination.Dispose(); + } + + [Benchmark(Baseline = true)] + public void CommonBulk() => + new PixelOperations().ToRgb24Bytes( + this.configuration, + this.source.GetSpan(), + this.destination.GetSpan(), + this.Count); + + [Benchmark] + public void OptimizedBulk() => + PixelOperations.Instance.ToRgb24Bytes( + this.configuration, + this.source.GetSpan(), + this.destination.GetSpan(), + this.Count); + } + + public class Rgb24Bytes_Rgba32 : Rgb24Bytes + { + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToRgba32Bytes.cs similarity index 54% rename from tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs rename to tests/ImageSharp.Benchmarks/Color/Bulk/ToRgba32Bytes.cs index 8166c8f465..7f4b2bc41d 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToRgba32Bytes.cs @@ -12,21 +12,24 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk { - public abstract class ToXyzw + public abstract class ToRgba32Bytes where TPixel : struct, IPixel { private IMemoryOwner source; private IMemoryOwner destination; + private Configuration configuration; + [Params(16, 128, 1024)] public int Count { get; set; } [GlobalSetup] public void Setup() { - this.source = Configuration.Default.MemoryAllocator.Allocate(this.Count); - this.destination = Configuration.Default.MemoryAllocator.Allocate(this.Count * 4); + this.configuration = Configuration.Default; + this.source = this.configuration.MemoryAllocator.Allocate(this.Count); + this.destination = this.configuration.MemoryAllocator.Allocate(this.Count * 4); } [GlobalCleanup] @@ -36,18 +39,17 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk this.destination.Dispose(); } - [Benchmark(Baseline = true)] - public void PerElement() + //[Benchmark] + public void Naive() { Span s = this.source.GetSpan(); Span d = this.destination.GetSpan(); - var rgba = default(Rgba32); - for (int i = 0; i < this.Count; i++) { TPixel c = s[i]; int i4 = i * 4; + Rgba32 rgba = default; c.ToRgba32(ref rgba); d[i4] = rgba.R; d[i4 + 1] = rgba.G; @@ -56,24 +58,32 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk } } - [Benchmark] - public void CommonBulk() - { - new PixelOperations().ToRgba32Bytes(this.source.GetSpan(), this.destination.GetSpan(), this.Count); - } + [Benchmark(Baseline = true)] + public void CommonBulk() => + new PixelOperations().ToRgba32Bytes( + this.configuration, + this.source.GetSpan(), + this.destination.GetSpan(), + this.Count); [Benchmark] - public void OptimizedBulk() - { - PixelOperations.Instance.ToRgba32Bytes(this.source.GetSpan(), this.destination.GetSpan(), this.Count); - } + public void OptimizedBulk() => + PixelOperations.Instance.ToRgba32Bytes( + this.configuration, + this.source.GetSpan(), + this.destination.GetSpan(), + this.Count); + } + + public class ToRgba32Bytes_FromRgba32 : ToRgba32Bytes + { } - public class ToXyzw_Rgba32 : ToXyzw + public class ToRgba32Bytes_FromArgb32 : ToRgba32Bytes { } - public class ToXyzw_Argb32 : ToXyzw + public class ToRgba32Bytes_FromBgra32 : ToRgba32Bytes { } } diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs index 50fac25139..70de8f4e27 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs @@ -17,18 +17,25 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk public abstract class ToVector4 where TPixel : struct, IPixel { - private IMemoryOwner source; + protected IMemoryOwner source; - private IMemoryOwner destination; + protected IMemoryOwner destination; - [Params(64, 300, 1024)] + protected Configuration Configuration => Configuration.Default; + + [Params( + 64, + 256, + //512, + //1024, + 2048)] public int Count { get; set; } [GlobalSetup] public void Setup() { - this.source = Configuration.Default.MemoryAllocator.Allocate(this.Count); - this.destination = Configuration.Default.MemoryAllocator.Allocate(this.Count); + this.source = this.Configuration.MemoryAllocator.Allocate(this.Count); + this.destination = this.Configuration.MemoryAllocator.Allocate(this.Count); } [GlobalCleanup] @@ -38,33 +45,26 @@ namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk this.destination.Dispose(); } - [Benchmark(Baseline = true)] - public void PerElement() + //[Benchmark] + public void Naive() { Span s = this.source.GetSpan(); Span d = this.destination.GetSpan(); for (int i = 0; i < this.Count; i++) { - TPixel c = s[i]; - d[i] = c.ToVector4(); + d[i] = s[i].ToVector4(); } } + [Benchmark] - public void CommonBulk() - { - new PixelOperations().ToVector4(this.source.GetSpan(), this.destination.GetSpan(), this.Count); - } - - [Benchmark] - public void OptimizedBulk() + public void PixelOperations_Specialized() { - PixelOperations.Instance.ToVector4(this.source.GetSpan(), this.destination.GetSpan(), this.Count); + PixelOperations.Instance.ToVector4( + this.Configuration, + this.source.GetSpan(), + this.destination.GetSpan()); } } - - public class ToVector4_Rgba32 : ToVector4 - { - } } \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs new file mode 100644 index 0000000000..39702d5253 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Bgra32.cs @@ -0,0 +1,41 @@ +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +{ + [Config(typeof(Config.ShortClr))] + public class ToVector4_Bgra32 : ToVector4 + { + [Benchmark(Baseline = true)] + public void PixelOperations_Base() + { + new PixelOperations().ToVector4( + this.Configuration, + this.source.GetSpan(), + this.destination.GetSpan()); + } + + // RESULTS: + // Method | Runtime | Count | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Allocated | + // ---------------------------- |-------- |------ |-----------:|------------:|-----------:|-------:|---------:|-------:|----------:| + // PixelOperations_Base | Clr | 64 | 339.9 ns | 138.30 ns | 7.8144 ns | 1.00 | 0.00 | 0.0072 | 24 B | + // PixelOperations_Specialized | Clr | 64 | 338.1 ns | 13.30 ns | 0.7515 ns | 0.99 | 0.02 | - | 0 B | + // | | | | | | | | | | + // PixelOperations_Base | Core | 64 | 245.6 ns | 29.05 ns | 1.6413 ns | 1.00 | 0.00 | 0.0072 | 24 B | + // PixelOperations_Specialized | Core | 64 | 257.1 ns | 37.89 ns | 2.1407 ns | 1.05 | 0.01 | - | 0 B | + // | | | | | | | | | | + // PixelOperations_Base | Clr | 256 | 972.7 ns | 61.98 ns | 3.5020 ns | 1.00 | 0.00 | 0.0057 | 24 B | + // PixelOperations_Specialized | Clr | 256 | 882.9 ns | 126.21 ns | 7.1312 ns | 0.91 | 0.01 | - | 0 B | + // | | | | | | | | | | + // PixelOperations_Base | Core | 256 | 910.0 ns | 90.87 ns | 5.1346 ns | 1.00 | 0.00 | 0.0067 | 24 B | + // PixelOperations_Specialized | Core | 256 | 448.4 ns | 15.77 ns | 0.8910 ns | 0.49 | 0.00 | - | 0 B | + // | | | | | | | | | | + // PixelOperations_Base | Clr | 2048 | 6,951.8 ns | 1,299.01 ns | 73.3963 ns | 1.00 | 0.00 | - | 24 B | + // PixelOperations_Specialized | Clr | 2048 | 5,852.3 ns | 630.56 ns | 35.6279 ns | 0.84 | 0.01 | - | 0 B | + // | | | | | | | | | | + // PixelOperations_Base | Core | 2048 | 6,937.5 ns | 1,692.19 ns | 95.6121 ns | 1.00 | 0.00 | - | 24 B | + // PixelOperations_Specialized | Core | 2048 | 2,994.5 ns | 1,126.65 ns | 63.6578 ns | 0.43 | 0.01 | - | 0 B | + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgba32.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgba32.cs new file mode 100644 index 0000000000..ab05a14073 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4_Rgba32.cs @@ -0,0 +1,164 @@ +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk +{ + [Config(typeof(Config.ShortClr))] + public class ToVector4_Rgba32 : ToVector4 + { + [Benchmark] + public void FallbackIntrinsics128() + { + Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + + SimdUtils.FallbackIntrinsics128.BulkConvertByteToNormalizedFloat(sBytes, dFloats); + } + + [Benchmark] + public void PixelOperations_Base() + { + new PixelOperations().ToVector4( + this.Configuration, + this.source.GetSpan(), + this.destination.GetSpan()); + } + + [Benchmark(Baseline = true)] + public void BasicIntrinsics256() + { + Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + + SimdUtils.BasicIntrinsics256.BulkConvertByteToNormalizedFloat(sBytes, dFloats); + } + + [Benchmark] + public void ExtendedIntrinsics() + { + Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + + SimdUtils.ExtendedIntrinsics.BulkConvertByteToNormalizedFloat(sBytes, dFloats); + } + + //[Benchmark] + public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat_2Loops() + { + Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + + int n = dFloats.Length / Vector.Count; + + ref Vector sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference((ReadOnlySpan)sBytes)); + ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dFloats)); + ref Vector destBaseU = ref Unsafe.As, Vector>(ref destBase); + + for (int i = 0; i < n; i++) + { + Vector b = Unsafe.Add(ref sourceBase, i); + + Vector.Widen(b, out Vector s0, out Vector s1); + Vector.Widen(s0, out Vector w0, out Vector w1); + Vector.Widen(s1, out Vector w2, out Vector w3); + + ref Vector d = ref Unsafe.Add(ref destBaseU, i * 4); + d = w0; + Unsafe.Add(ref d, 1) = w1; + Unsafe.Add(ref d, 2) = w2; + Unsafe.Add(ref d, 3) = w3; + } + + n = dFloats.Length / Vector.Count; + var scale = new Vector(1f / 255f); + + for (int i = 0; i < n; i++) + { + ref Vector dRef = ref Unsafe.Add(ref destBase, i); + + Vector du = Vector.AsVectorInt32(dRef); + Vector v = Vector.ConvertToSingle(du); + v *= scale; + + dRef = v; + } + } + + //[Benchmark] + public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat_ConvertInSameLoop() + { + Span sBytes = MemoryMarshal.Cast(this.source.GetSpan()); + Span dFloats = MemoryMarshal.Cast(this.destination.GetSpan()); + + int n = dFloats.Length / Vector.Count; + + ref Vector sourceBase = ref Unsafe.As>(ref MemoryMarshal.GetReference((ReadOnlySpan)sBytes)); + ref Vector destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dFloats)); + var scale = new Vector(1f / 255f); + + for (int i = 0; i < n; i++) + { + Vector b = Unsafe.Add(ref sourceBase, i); + + Vector.Widen(b, out Vector s0, out Vector s1); + Vector.Widen(s0, out Vector w0, out Vector w1); + Vector.Widen(s1, out Vector w2, out Vector w3); + + Vector f0 = ConvertToNormalizedSingle(w0, scale); + Vector f1 = ConvertToNormalizedSingle(w1, scale); + Vector f2 = ConvertToNormalizedSingle(w2, scale); + Vector f3 = ConvertToNormalizedSingle(w3, scale); + + ref Vector d = ref Unsafe.Add(ref destBase, i * 4); + d = f0; + Unsafe.Add(ref d, 1) = f1; + Unsafe.Add(ref d, 2) = f2; + Unsafe.Add(ref d, 3) = f3; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector ConvertToNormalizedSingle(Vector u, Vector scale) + { + Vector vi = Vector.AsVectorInt32(u); + Vector v = Vector.ConvertToSingle(vi); + v *= scale; + return v; + } + + // RESULTS (2018 October): + // + // Method | Runtime | Count | Mean | Error | StdDev | Scaled | ScaledSD | Gen 0 | Allocated | + // ---------------------------- |-------- |------ |------------:|-------------:|------------:|-------:|---------:|-------:|----------:| + // FallbackIntrinsics128 | Clr | 64 | 287.62 ns | 6.026 ns | 0.3405 ns | 1.19 | 0.00 | - | 0 B | + // BasicIntrinsics256 | Clr | 64 | 240.83 ns | 10.585 ns | 0.5981 ns | 1.00 | 0.00 | - | 0 B | + // ExtendedIntrinsics | Clr | 64 | 168.28 ns | 11.478 ns | 0.6485 ns | 0.70 | 0.00 | - | 0 B | + // PixelOperations_Base | Clr | 64 | 334.08 ns | 38.048 ns | 2.1498 ns | 1.39 | 0.01 | 0.0072 | 24 B | + // PixelOperations_Specialized | Clr | 64 | 255.41 ns | 10.939 ns | 0.6181 ns | 1.06 | 0.00 | - | 0 B | <--- ceremonial overhead has been minimized! + // | | | | | | | | | | + // FallbackIntrinsics128 | Core | 64 | 183.29 ns | 8.931 ns | 0.5046 ns | 1.32 | 0.00 | - | 0 B | + // BasicIntrinsics256 | Core | 64 | 139.18 ns | 7.633 ns | 0.4313 ns | 1.00 | 0.00 | - | 0 B | + // ExtendedIntrinsics | Core | 64 | 66.29 ns | 16.366 ns | 0.9247 ns | 0.48 | 0.01 | - | 0 B | + // PixelOperations_Base | Core | 64 | 257.75 ns | 16.959 ns | 0.9582 ns | 1.85 | 0.01 | 0.0072 | 24 B | + // PixelOperations_Specialized | Core | 64 | 90.14 ns | 9.955 ns | 0.5625 ns | 0.65 | 0.00 | - | 0 B | + // | | | | | | | | | | + // FallbackIntrinsics128 | Clr | 2048 | 5,011.84 ns | 347.991 ns | 19.6621 ns | 1.22 | 0.01 | - | 0 B | + // BasicIntrinsics256 | Clr | 2048 | 4,119.35 ns | 720.153 ns | 40.6900 ns | 1.00 | 0.00 | - | 0 B | + // ExtendedIntrinsics | Clr | 2048 | 1,195.29 ns | 164.389 ns | 9.2883 ns |!! 0.29 | 0.00 | - | 0 B | <--- ExtendedIntrinsics rock! + // PixelOperations_Base | Clr | 2048 | 6,820.58 ns | 823.433 ns | 46.5255 ns | 1.66 | 0.02 | - | 24 B | + // PixelOperations_Specialized | Clr | 2048 | 4,203.53 ns | 176.714 ns | 9.9847 ns | 1.02 | 0.01 | - | 0 B | <--- can't yet detect whether ExtendedIntrinsics are available :( + // | | | | | | | | | | + // FallbackIntrinsics128 | Core | 2048 | 5,017.89 ns | 4,021.533 ns | 227.2241 ns | 1.24 | 0.05 | - | 0 B | + // BasicIntrinsics256 | Core | 2048 | 4,046.51 ns | 1,150.390 ns | 64.9992 ns | 1.00 | 0.00 | - | 0 B | + // ExtendedIntrinsics | Core | 2048 | 1,130.59 ns | 832.588 ns | 47.0427 ns |!! 0.28 | 0.01 | - | 0 B | <--- ExtendedIntrinsics rock! + // PixelOperations_Base | Core | 2048 | 6,752.68 ns | 272.820 ns | 15.4148 ns | 1.67 | 0.02 | - | 24 B | + // PixelOperations_Specialized | Core | 2048 | 1,126.13 ns | 79.192 ns | 4.4745 ns |!! 0.28 | 0.00 | - | 0 B | <--- ExtendedIntrinsics rock! + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs deleted file mode 100644 index 4e9c6d10a6..0000000000 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -// ReSharper disable InconsistentNaming - -using System.Buffers; -using System; - -using BenchmarkDotNet.Attributes; - -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces.Bulk -{ - public abstract class ToXyz - where TPixel : struct, IPixel - { - private IMemoryOwner source; - - private IMemoryOwner destination; - - [Params(16, 128, 1024)] - public int Count { get; set; } - - [GlobalSetup] - public void Setup() - { - this.source = Configuration.Default.MemoryAllocator.Allocate(this.Count); - this.destination = Configuration.Default.MemoryAllocator.Allocate(this.Count * 3); - } - - [GlobalCleanup] - public void Cleanup() - { - this.source.Dispose(); - this.destination.Dispose(); - } - - [Benchmark(Baseline = true)] - public void PerElement() - { - Span s = this.source.GetSpan(); - Span d = this.destination.GetSpan(); - - var rgb = default(Rgb24); - - for (int i = 0; i < this.Count; i++) - { - TPixel c = s[i]; - int i3 = i * 3; - c.ToRgb24(ref rgb); - d[i3] = rgb.R; - d[i3 + 1] = rgb.G; - d[i3 + 2] = rgb.B; - } - } - - [Benchmark] - public void CommonBulk() - { - new PixelOperations().ToRgb24Bytes(this.source.GetSpan(), this.destination.GetSpan(), this.Count); - } - - [Benchmark] - public void OptimizedBulk() - { - PixelOperations.Instance.ToRgb24Bytes(this.source.GetSpan(), this.destination.GetSpan(), this.Count); - } - } - - public class ToXyz_Rgba32 : ToXyz - { - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs b/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs index eba6b5d9be..92008f6e20 100644 --- a/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs +++ b/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs @@ -14,7 +14,7 @@ private static readonly RGBColor RGBColor = new RGBColor(0.206162, 0.260277, 0.746717, RGBWorkingSpaces.WideGamutRGB); - private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; + private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(new ColorSpaceConverterOptions { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }); private static readonly ColourfulConverter ColourfulConverter = new ColourfulConverter { TargetRGBWorkingSpace = RGBWorkingSpaces.sRGB }; diff --git a/tests/ImageSharp.Benchmarks/General/Abs.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/Abs.cs similarity index 88% rename from tests/ImageSharp.Benchmarks/General/Abs.cs rename to tests/ImageSharp.Benchmarks/General/BasicMath/Abs.cs index a67f3f1078..ea53959b6a 100644 --- a/tests/ImageSharp.Benchmarks/General/Abs.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/Abs.cs @@ -1,9 +1,9 @@ -namespace SixLabors.ImageSharp.Benchmarks.General -{ - using System; +using System; - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +{ public class Abs { [Params(-1, 1)] diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs new file mode 100644 index 0000000000..3b7dea0955 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampFloat.cs @@ -0,0 +1,70 @@ +using System; +using System.Runtime.CompilerServices; + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +{ + public class ClampFloat + { + private readonly float min = -1.5f; + private readonly float max = 2.5f; + private static readonly float[] Values = { -10, -5, -3, -1.5f, -0.5f, 0f, 1f, 1.5f, 2.5f, 3, 10 }; + + [Benchmark(Baseline = true)] + public float UsingMathF() + { + float acc = 0; + + for (int i = 0; i < Values.Length; i++) + { + acc += ClampUsingMathF(Values[i], this.min, this.max); + } + + return acc; + } + + [Benchmark] + public float UsingBranching() + { + float acc = 0; + + for (int i = 0; i < Values.Length; i++) + { + acc += ClampUsingBranching(Values[i], this.min, this.max); + } + + return acc; + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float ClampUsingMathF(float x, float min, float max) + { + return Math.Min(max, Math.Max(min, x)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float ClampUsingBranching(float x, float min, float max) + { + if (x >= max) + { + return max; + } + + if (x <= min) + { + return min; + } + + return x; + } + + // RESULTS: + // Method | Mean | Error | StdDev | Scaled | + // --------------- |---------:|----------:|----------:|-------:| + // UsingMathF | 30.37 ns | 0.3764 ns | 0.3337 ns | 1.00 | + // UsingBranching | 18.66 ns | 0.1043 ns | 0.0871 ns | 0.61 | + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Clamp.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs similarity index 93% rename from tests/ImageSharp.Benchmarks/General/Clamp.cs rename to tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs index ef6bc3c402..6ce82ba115 100644 --- a/tests/ImageSharp.Benchmarks/General/Clamp.cs +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ClampInt32IntoByte.cs @@ -3,14 +3,14 @@ // Licensed under the Apache License, Version 2.0. // -namespace SixLabors.ImageSharp.Benchmarks.General -{ - using System; - using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; - using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; - public class Clamp +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +{ + public class ClampInt32IntoByte { [Params(-1, 0, 255, 256)] public int Value { get; set; } diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoConstant.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoConstant.cs new file mode 100644 index 0000000000..9ddfad7222 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoConstant.cs @@ -0,0 +1,23 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes.Jobs; + +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +{ + [LongRunJob] + public class ModuloPowerOfTwoConstant + { + private readonly int value = 42; + + [Benchmark(Baseline = true)] + public int Standard() + { + return this.value % 8; + } + + [Benchmark] + public int Bitwise() + { + return ImageMaths.Modulo8(this.value); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoVariable.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoVariable.cs new file mode 100644 index 0000000000..5c2fe81fa2 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/ModuloPowerOfTwoVariable.cs @@ -0,0 +1,32 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes.Jobs; + +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +{ + [LongRunJob] + public class ModuloPowerOfTwoVariable + { + private readonly int value = 42; + + private readonly int m = 32; + + [Benchmark(Baseline = true)] + public int Standard() + { + return this.value % this.m; + } + + [Benchmark] + public int Bitwise() + { + return ImageMaths.ModuloP2(this.value, this.m); + } + + // RESULTS: + // + // Method | Mean | Error | StdDev | Median | Scaled | ScaledSD | + // --------- |----------:|----------:|----------:|----------:|-------:|---------:| + // Standard | 1.2465 ns | 0.0093 ns | 0.0455 ns | 1.2423 ns | 1.00 | 0.00 | + // Bitwise | 0.0265 ns | 0.0103 ns | 0.0515 ns | 0.0000 ns | 0.02 | 0.04 | + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/BasicMath/Pow.cs b/tests/ImageSharp.Benchmarks/General/BasicMath/Pow.cs new file mode 100644 index 0000000000..0f256fc781 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/BasicMath/Pow.cs @@ -0,0 +1,41 @@ +using System; + +using BenchmarkDotNet.Attributes; + +namespace SixLabors.ImageSharp.Benchmarks.General.BasicMath +{ + public class Pow + { + [Params(-1.333F, 1.333F)] + public float X { get; set; } + + + [Benchmark(Baseline = true, Description = "Math.Pow 2")] + public float MathPow() + { + float x = this.X; + return (float)Math.Pow(x, 2); + } + + [Benchmark(Description = "Pow x2")] + public float PowMult() + { + float x = this.X; + return x * x; + } + + [Benchmark(Description = "Math.Pow 3")] + public float MathPow3() + { + float x = this.X; + return (float)Math.Pow(x, 3); + } + + [Benchmark(Description = "Pow x3")] + public float PowMult3() + { + float x = this.X; + return x * x * x; + } + } +} diff --git a/tests/ImageSharp.Benchmarks/General/Modulus.cs b/tests/ImageSharp.Benchmarks/General/Modulus.cs deleted file mode 100644 index e6d5ccce62..0000000000 --- a/tests/ImageSharp.Benchmarks/General/Modulus.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace SixLabors.ImageSharp.Benchmarks.General -{ - using BenchmarkDotNet.Attributes; - - public class Modulus - { - [Benchmark(Baseline = true, Description = "Standard Modulus using %")] - public int StandardModulus() - { - return 255 % 256; - } - - [Benchmark(Description = "Bitwise Modulus using &")] - public int BitwiseModulus() - { - return 255 & 255; - } - } -} diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs new file mode 100644 index 0000000000..b5f339fb37 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/ITestPixel.cs @@ -0,0 +1,28 @@ +using System.Numerics; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +{ + 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); + + void FromVector4(Vector4 source); + + void FromVector4(ref Vector4 source); + + Rgba32 ToRgba32(); + + void CopyToRgba32(ref Rgba32 dest); + + Vector4 ToVector4(); + + void CopyToVector4(ref Vector4 dest); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs new file mode 100644 index 0000000000..9f1b2721b4 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromRgba32.cs @@ -0,0 +1,215 @@ +// ReSharper disable InconsistentNaming + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats.Utils; + +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +{ + public abstract class PixelConversion_ConvertFromRgba32 + { + internal struct ConversionRunner + where T : struct, ITestPixel + { + public readonly T[] dest; + + public readonly 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); + } + } + } + + internal ConversionRunner compatibleMemLayoutRunner; + + internal ConversionRunner permutedRunnerRgbaToArgb; + + [Params( + 256, + 2048 + )] + public int Count { get; set; } + + [GlobalSetup] + public void Setup() + { + this.compatibleMemLayoutRunner = new ConversionRunner(this.Count); + this.permutedRunnerRgbaToArgb = new ConversionRunner(this.Count); + } + } + + public class PixelConversion_ConvertFromRgba32_Compatible : PixelConversion_ConvertFromRgba32 + { + [Benchmark(Baseline = true)] + public void ByRef() + { + this.compatibleMemLayoutRunner.RunByRefConversion(); + } + + [Benchmark] + public void ByVal() + { + this.compatibleMemLayoutRunner.RunByValConversion(); + } + + [Benchmark] + public void FromBytes() + { + this.compatibleMemLayoutRunner.RunFromBytesConversion(); + } + + [Benchmark] + public void Inline() + { + ref Rgba32 sBase = ref this.compatibleMemLayoutRunner.source[0]; + ref Rgba32 dBase = ref Unsafe.As(ref this.compatibleMemLayoutRunner.dest[0]); + + for (int i = 0; i < this.Count; i++) + { + Unsafe.Add(ref dBase, i) = Unsafe.Add(ref sBase, i); + } + } + + // Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | + // ---------- |------ |---------:|---------:|---------:|-------:|---------:| + // ByRef | 256 | 128.5 ns | 1.217 ns | 1.138 ns | 1.00 | 0.00 | + // ByVal | 256 | 196.7 ns | 2.792 ns | 2.612 ns | 1.53 | 0.02 | + // FromBytes | 256 | 321.7 ns | 2.180 ns | 1.820 ns | 2.50 | 0.03 | + // Inline | 256 | 129.9 ns | 2.759 ns | 2.581 ns | 1.01 | 0.02 | + } + + public class PixelConversion_ConvertFromRgba32_Permuted_RgbaToArgb : PixelConversion_ConvertFromRgba32 + { + [Benchmark(Baseline = true)] + public void ByRef() + { + this.permutedRunnerRgbaToArgb.RunByRefConversion(); + } + + [Benchmark] + public void ByVal() + { + this.permutedRunnerRgbaToArgb.RunByValConversion(); + } + + [Benchmark] + public void FromBytes() + { + this.permutedRunnerRgbaToArgb.RunFromBytesConversion(); + } + + [Benchmark] + public void InlineShuffle() + { + ref Rgba32 sBase = ref this.permutedRunnerRgbaToArgb.source[0]; + ref TestArgb dBase = ref this.permutedRunnerRgbaToArgb.dest[0]; + + for (int i = 0; i < this.Count; i++) + { + Rgba32 s = Unsafe.Add(ref sBase, i); + ref TestArgb d = ref Unsafe.Add(ref dBase, i); + + d.R = s.R; + d.G = s.G; + d.B = s.B; + d.A = s.A; + } + } + + [Benchmark] + public void PixelConverter_Rgba32_ToArgb32() + { + ref uint sBase = ref Unsafe.As(ref this.permutedRunnerRgbaToArgb.source[0]); + ref uint dBase = ref Unsafe.As(ref this.permutedRunnerRgbaToArgb.dest[0]); + + for (int i = 0; i < this.Count; i++) + { + uint s = Unsafe.Add(ref sBase, i); + Unsafe.Add(ref dBase, i) = PixelConverter.FromRgba32.ToArgb32(s); + } + } + + [Benchmark] + public void PixelConverter_Rgba32_ToArgb32_CopyThenWorkOnSingleBuffer() + { + Span source = MemoryMarshal.Cast(this.permutedRunnerRgbaToArgb.source); + Span dest = MemoryMarshal.Cast(this.permutedRunnerRgbaToArgb.dest); + source.CopyTo(dest); + + ref uint dBase = ref MemoryMarshal.GetReference(dest); + + for (int i = 0; i < this.Count; i++) + { + uint s = Unsafe.Add(ref dBase, i); + Unsafe.Add(ref dBase, i) = PixelConverter.FromRgba32.ToArgb32(s); + } + } + + // RESULTS: + // Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | + // ---------------------------------------------------------- |------ |-----------:|-----------:|-----------:|-------:|---------:| + // ByRef | 256 | 328.7 ns | 6.6141 ns | 6.1868 ns | 1.00 | 0.00 | + // ByVal | 256 | 322.0 ns | 4.3541 ns | 4.0728 ns | 0.98 | 0.02 | + // FromBytes | 256 | 321.5 ns | 3.3499 ns | 3.1335 ns | 0.98 | 0.02 | + // InlineShuffle | 256 | 330.7 ns | 4.2525 ns | 3.9778 ns | 1.01 | 0.02 | + // PixelConverter_Rgba32_ToArgb32 | 256 | 167.4 ns | 0.6357 ns | 0.5309 ns | 0.51 | 0.01 | + // PixelConverter_Rgba32_ToArgb32_CopyThenWorkOnSingleBuffer | 256 | 196.6 ns | 0.8929 ns | 0.7915 ns | 0.60 | 0.01 | + // | | | | | | | + // ByRef | 2048 | 2,534.4 ns | 8.2947 ns | 6.9265 ns | 1.00 | 0.00 | + // ByVal | 2048 | 2,638.5 ns | 52.6843 ns | 70.3320 ns | 1.04 | 0.03 | + // FromBytes | 2048 | 2,517.2 ns | 40.8055 ns | 38.1695 ns | 0.99 | 0.01 | + // InlineShuffle | 2048 | 2,546.5 ns | 21.2506 ns | 19.8778 ns | 1.00 | 0.01 | + // PixelConverter_Rgba32_ToArgb32 | 2048 | 1,265.7 ns | 5.1397 ns | 4.5562 ns | 0.50 | 0.00 | + // PixelConverter_Rgba32_ToArgb32_CopyThenWorkOnSingleBuffer | 2048 | 1,410.3 ns | 11.1939 ns | 9.9231 ns | 0.56 | 0.00 |// + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromVector4.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs similarity index 79% rename from tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromVector4.cs rename to tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs index 5b059e2e65..d0c8a3045c 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromVector4.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertFromVector4.cs @@ -1,62 +1,48 @@ // ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Benchmarks.General -{ - using System.Numerics; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using BenchmarkDotNet.Attributes; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; - public class PixelConversion_ConvertFromVector4 - { - interface ITestPixel - where T : struct, ITestPixel - { - void FromVector4(Vector4 source); +using BenchmarkDotNet.Attributes; - void FromVector4(ref Vector4 source); - } +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +{ + public class PixelConversion_ConvertFromVector4 + { [StructLayout(LayoutKind.Sequential)] - struct TestArgb : ITestPixel + struct TestRgbaVector : ITestPixel { - private byte a, r, g, b; + private Vector4 v; [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; + this.v = p; } [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; + this.v = p; } - } - - [StructLayout(LayoutKind.Sequential)] - struct TestRgbaVector : ITestPixel - { - private Vector4 v; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(Vector4 p) - { - this.v = p; - } + public Vector4 ToVector4() => this.v; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void FromVector4(ref Vector4 p) + public void CopyToVector4(ref Vector4 dest) { - this.v = p; + dest = this.v; } + + public void FromRgba32(Rgba32 source) => throw new System.NotImplementedException(); + public void FromRgba32(ref Rgba32 source) => throw new System.NotImplementedException(); + public void FromBytes(byte r, byte g, byte b, byte a) => throw new System.NotImplementedException(); + public Rgba32 ToRgba32() => throw new System.NotImplementedException(); + public void CopyToRgba32(ref Rgba32 dest) => throw new System.NotImplementedException(); } struct ConversionRunner diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertToRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs similarity index 67% rename from tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertToRgba32.cs rename to tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs index 5656904fe0..d205e1e63e 100644 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertToRgba32.cs +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32.cs @@ -1,14 +1,14 @@ // ReSharper disable InconsistentNaming -using SixLabors.ImageSharp.PixelFormats; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; -namespace SixLabors.ImageSharp.Benchmarks.General -{ - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; +using BenchmarkDotNet.Attributes; - using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.PixelFormats; +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +{ /// /// When implementing TPixel --> Rgba32 style conversions on IPixel, should which API should we prefer? /// 1. Rgba32 ToRgba32(); @@ -18,53 +18,6 @@ namespace SixLabors.ImageSharp.Benchmarks.General /// 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 { @@ -111,7 +64,7 @@ namespace SixLabors.ImageSharp.Benchmarks.General private ConversionRunner permutedRunner; - [Params(128)] + [Params(32)] public int Count { get; set; } [GlobalSetup] diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation.cs new file mode 100644 index 0000000000..fff9ae9bc7 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation.cs @@ -0,0 +1,113 @@ +using System.Runtime.CompilerServices; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +{ + public class PixelConversion_ConvertToRgba32_AsPartOfCompositeOperation + { + 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]; + + Rgba32 temp; + + for (int i = 0; i < count; i++) + { + temp = Unsafe.Add(ref sourceBaseRef, i).ToRgba32(); + + // manipulate pixel before saving to dest buffer: + temp.A = 0; + + Unsafe.Add(ref destBaseRef, i) = temp; + } + } + + [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]; + + Rgba32 temp = default; + + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref sourceBaseRef, i).CopyToRgba32(ref temp); + + // manipulate pixel before saving to dest buffer: + temp.A = 0; + + Unsafe.Add(ref destBaseRef, i) = temp; + } + } + } + + private ConversionRunner compatibleMemoryLayoutRunner; + + private ConversionRunner permutedRunner; + + [Params(32)] + public int Count { get; set; } + + [GlobalSetup] + 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 | Error | StdDev | Scaled | ScaledSD | + // ----------------- |------ |----------:|----------:|----------:|-------:|---------:| + // CompatibleRetval | 32 | 53.05 ns | 0.1865 ns | 0.1557 ns | 1.00 | 0.00 | + // CompatibleCopyTo | 32 | 36.12 ns | 0.3596 ns | 0.3003 ns | 0.68 | 0.01 | + // PermutedRetval | 32 | 303.61 ns | 5.1697 ns | 4.8358 ns | 5.72 | 0.09 | + // PermutedCopyTo | 32 | 38.05 ns | 0.8053 ns | 1.2297 ns | 0.72 | 0.02 | +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs new file mode 100644 index 0000000000..2bc3ee9716 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4.cs @@ -0,0 +1,83 @@ +using System.Numerics; +using System.Runtime.CompilerServices; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +{ + public class PixelConversion_ConvertToVector4 + { + struct ConversionRunner + where T : struct, ITestPixel + { + private T[] source; + + private Vector4[] dest; + + public ConversionRunner(int count) + { + this.source = new T[count]; + this.dest = new Vector4[count]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunRetvalConversion() + { + int count = this.source.Length; + + ref T sourceBaseRef = ref this.source[0]; + ref Vector4 destBaseRef = ref this.dest[0]; + + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref destBaseRef, i) = Unsafe.Add(ref sourceBaseRef, i).ToVector4(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunCopyToConversion() + { + int count = this.source.Length; + + ref T sourceBaseRef = ref this.source[0]; + ref Vector4 destBaseRef = ref this.dest[0]; + + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref sourceBaseRef, i).CopyToVector4(ref Unsafe.Add(ref destBaseRef, i)); + } + } + } + + private ConversionRunner runner; + + [Params(32)] + public int Count { get; set; } + + [GlobalSetup] + public void Setup() + { + this.runner = new ConversionRunner(this.Count); + } + + [Benchmark(Baseline = true)] + public void UseRetval() + { + this.runner.RunRetvalConversion(); + } + + [Benchmark] + public void UseCopyTo() + { + this.runner.RunCopyToConversion(); + } + + // RESULTS: + // Method | Count | Mean | Error | StdDev | Scaled | + // ---------- |------ |---------:|---------:|---------:|-------:| + // UseRetval | 32 | 109.0 ns | 1.202 ns | 1.125 ns | 1.00 | + // UseCopyTo | 32 | 108.6 ns | 1.151 ns | 1.020 ns | 1.00 | + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4_AsPartOfCompositeOperation.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4_AsPartOfCompositeOperation.cs new file mode 100644 index 0000000000..c6daf0f1e2 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_ConvertToVector4_AsPartOfCompositeOperation.cs @@ -0,0 +1,95 @@ +using System.Numerics; +using System.Runtime.CompilerServices; + +using BenchmarkDotNet.Attributes; + +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +{ + public class PixelConversion_ConvertToVector4_AsPartOfCompositeOperation + { + struct ConversionRunner + where T : struct, ITestPixel + { + private T[] source; + + private Vector4[] dest; + + public ConversionRunner(int count) + { + this.source = new T[count]; + this.dest = new Vector4[count]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunRetvalConversion() + { + int count = this.source.Length; + + ref T sourceBaseRef = ref this.source[0]; + ref Vector4 destBaseRef = ref this.dest[0]; + + Vector4 temp; + + for (int i = 0; i < count; i++) + { + temp = Unsafe.Add(ref sourceBaseRef, i).ToVector4(); + + // manipulate pixel before saving to dest buffer: + temp.W = 0; + + Unsafe.Add(ref destBaseRef, i) = temp; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RunCopyToConversion() + { + int count = this.source.Length; + + ref T sourceBaseRef = ref this.source[0]; + ref Vector4 destBaseRef = ref this.dest[0]; + + Vector4 temp = default; + + for (int i = 0; i < count; i++) + { + Unsafe.Add(ref sourceBaseRef, i).CopyToVector4(ref temp); + + // manipulate pixel before saving to dest buffer: + temp.W = 0; + + Unsafe.Add(ref destBaseRef, i) = temp; + } + } + } + + private ConversionRunner runner; + + [Params(32)] + public int Count { get; set; } + + [GlobalSetup] + public void Setup() + { + this.runner = new ConversionRunner(this.Count); + } + + [Benchmark(Baseline = true)] + public void UseRetval() + { + this.runner.RunRetvalConversion(); + } + + [Benchmark] + public void UseCopyTo() + { + this.runner.RunCopyToConversion(); + } + + // RESULTS: + // Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | + // ---------- |------ |---------:|---------:|---------:|-------:|---------:| + // UseRetval | 32 | 120.2 ns | 1.560 ns | 1.383 ns | 1.00 | 0.00 | + // UseCopyTo | 32 | 121.7 ns | 2.439 ns | 2.281 ns | 1.01 | 0.02 | + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Argb32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Argb32.cs new file mode 100644 index 0000000000..40893914e1 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Argb32.cs @@ -0,0 +1,176 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +{ + public class PixelConversion_Rgba32_To_Argb32 + { + private Rgba32[] source; + + private Argb32[] dest; + + [Params(64)] + public int Count { get; set; } + + [GlobalSetup] + public void Setup() + { + this.source = new Rgba32[this.Count]; + this.dest = new Argb32[this.Count]; + } + + [Benchmark(Baseline = true)] + public void Default() + { + ref Rgba32 sBase = ref this.source[0]; + ref Argb32 dBase = ref this.dest[0]; + + for (int i = 0; i < this.Count; i++) + { + Rgba32 s = Unsafe.Add(ref sBase, i); + Unsafe.Add(ref dBase, i).FromRgba32(s); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void Default_GenericImpl(ReadOnlySpan source, Span dest) + where TPixel : struct, IPixel + { + ref Rgba32 sBase = ref MemoryMarshal.GetReference(source); + ref TPixel dBase = ref MemoryMarshal.GetReference(dest); + + for (int i = 0; i < source.Length; i++) + { + Rgba32 s = Unsafe.Add(ref sBase, i); + Unsafe.Add(ref dBase, i).FromRgba32(s); + } + } + + [Benchmark] + public void Default_Generic() + { + Default_GenericImpl(this.source.AsSpan(), this.dest.AsSpan()); + } + + [Benchmark] + public void Default_Group2() + { + ref Rgba32 sBase = ref this.source[0]; + ref Argb32 dBase = ref this.dest[0]; + + for (int i = 0; i < this.Count; i += 2) + { + ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); + Rgba32 s1 = Unsafe.Add(ref s0, 1); + + ref Argb32 d0 = ref Unsafe.Add(ref dBase, i); + d0.FromRgba32(s0); + Unsafe.Add(ref d0, 1).FromRgba32(s1); + } + } + + + [Benchmark] + public void Default_Group4() + { + ref Rgba32 sBase = ref this.source[0]; + ref Argb32 dBase = ref this.dest[0]; + + for (int i = 0; i < this.Count; i += 4) + { + ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); + ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); + ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1); + Rgba32 s3 = Unsafe.Add(ref s2, 1); + + ref Argb32 d0 = ref Unsafe.Add(ref dBase, i); + ref Argb32 d1 = ref Unsafe.Add(ref d0, 1); + ref Argb32 d2 = ref Unsafe.Add(ref d1, 1); + + d0.FromRgba32(s0); + d1.FromRgba32(s1); + d2.FromRgba32(s2); + Unsafe.Add(ref d2, 1).FromRgba32(s3); + } + } + + [Benchmark] + public void BitOps() + { + ref uint sBase = ref Unsafe.As(ref this.source[0]); + ref uint dBase = ref Unsafe.As(ref this.dest[0]); + + for (int i = 0; i < this.Count; i++) + { + uint s = Unsafe.Add(ref sBase, i); + Unsafe.Add(ref dBase, i) = FromRgba32.ToArgb32(s); + } + } + + [Benchmark] + public void BitOps_GroupAsULong() + { + ref ulong sBase = ref Unsafe.As(ref this.source[0]); + ref ulong dBase = ref Unsafe.As(ref this.dest[0]); + + for (int i = 0; i < this.Count / 2; i++) + { + ulong s = Unsafe.Add(ref sBase, i); + uint lo = (uint)s; + uint hi = (uint)(s >> 32); + lo = FromRgba32.ToArgb32(lo); + hi = FromRgba32.ToArgb32(hi); + + s = (ulong)(hi << 32) | lo; + + Unsafe.Add(ref dBase, i) = s; + } + } + + public static class FromRgba32 + { + /// + /// Converts a packed to . + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static uint ToArgb32(uint packedRgba) + { + // packedRgba = [aa bb gg rr] + // ROL(8, packedRgba) = [bb gg rr aa] + return (packedRgba << 8) | (packedRgba >> 24); + } + + /// + /// Converts a packed to . + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static uint ToBgra32(uint packedRgba) + { + // packedRgba = [aa bb gg rr] + // tmp1 = [aa 00 gg 00] + // tmp2 = [00 bb 00 rr] + // tmp3=ROL(16, tmp2) = [00 rr 00 bb] + // tmp1 + tmp3 = [aa rr gg bb] + uint tmp1 = packedRgba & 0xFF00FF00; + uint tmp2 = packedRgba & 0x00FF00FF; + uint tmp3 = (tmp2 << 16) | (tmp2 >> 16); + return tmp1 + tmp3; + } + } + + // RESULTS: + // Method | Count | Mean | Error | StdDev | Scaled | + // -------------------- |------ |----------:|----------:|----------:|-------:| + // Default | 64 | 107.33 ns | 1.0633 ns | 0.9426 ns | 1.00 | + // Default_Generic | 64 | 111.15 ns | 0.3789 ns | 0.3544 ns | 1.04 | + // Default_Group2 | 64 | 90.36 ns | 0.7779 ns | 0.6896 ns | 0.84 | + // Default_Group4 | 64 | 82.39 ns | 0.2726 ns | 0.2550 ns | 0.77 | + // BitOps | 64 | 39.25 ns | 0.3266 ns | 0.2895 ns | 0.37 | + // BitOps_GroupAsULong | 64 | 41.80 ns | 0.2227 ns | 0.2083 ns | 0.39 | + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs new file mode 100644 index 0000000000..a8fea68661 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/PixelConversion_Rgba32_To_Bgra32.cs @@ -0,0 +1,392 @@ +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes.Jobs; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tuples; + +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +{ + //[MonoJob] + //[RyuJitX64Job] + public class PixelConversion_Rgba32_To_Bgra32 + { + private Rgba32[] source; + + private Bgra32[] dest; + + [StructLayout(LayoutKind.Sequential)] + struct Tuple4OfUInt32 + { + public uint V0, V1, V2, V3; + + public void ConvertMe() + { + this.V0 = FromRgba32.ToBgra32(this.V0); + this.V1 = FromRgba32.ToBgra32(this.V1); + this.V2 = FromRgba32.ToBgra32(this.V2); + this.V3 = FromRgba32.ToBgra32(this.V3); + } + } + + [Params(64)] + public int Count { get; set; } + + [GlobalSetup] + public void Setup() + { + this.source = new Rgba32[this.Count]; + this.dest = new Bgra32[this.Count]; + } + + [Benchmark(Baseline = true)] + public void Default() + { + ref Rgba32 sBase = ref this.source[0]; + ref Bgra32 dBase = ref this.dest[0]; + + for (int i = 0; i < this.Count; i++) + { + ref Rgba32 s = ref Unsafe.Add(ref sBase, i); + Unsafe.Add(ref dBase, i).FromRgba32(s); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void Default_GenericImpl(ReadOnlySpan source, Span dest) + where TPixel : struct, IPixel + { + ref Rgba32 sBase = ref MemoryMarshal.GetReference(source); + ref TPixel dBase = ref MemoryMarshal.GetReference(dest); + + for (int i = 0; i < source.Length; i++) + { + ref Rgba32 s = ref Unsafe.Add(ref sBase, i); + Unsafe.Add(ref dBase, i).FromRgba32(s); + } + } + + [Benchmark] + public void Default_Generic() + { + Default_GenericImpl(this.source.AsSpan(), this.dest.AsSpan()); + } + + [Benchmark] + public void Default_Group2() + { + ref Rgba32 sBase = ref this.source[0]; + ref Bgra32 dBase = ref this.dest[0]; + + for (int i = 0; i < this.Count; i+=2) + { + ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); + Rgba32 s1 = Unsafe.Add(ref s0, 1); + + ref Bgra32 d0 = ref Unsafe.Add(ref dBase, i); + d0.FromRgba32(s0); + Unsafe.Add(ref d0, 1).FromRgba32(s1); + } + } + + [Benchmark] + public void Default_Group4() + { + ref Rgba32 sBase = ref this.source[0]; + ref Bgra32 dBase = ref this.dest[0]; + + for (int i = 0; i < this.Count; i += 4) + { + ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); + ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); + ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1); + Rgba32 s3 = Unsafe.Add(ref s2, 1); + + ref Bgra32 d0 = ref Unsafe.Add(ref dBase, i); + ref Bgra32 d1 = ref Unsafe.Add(ref d0, 1); + ref Bgra32 d2 = ref Unsafe.Add(ref d1, 1); + + d0.FromRgba32(s0); + d1.FromRgba32(s1); + d2.FromRgba32(s2); + Unsafe.Add(ref d2, 1).FromRgba32(s3); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void Group4GenericImpl(ReadOnlySpan source, Span dest) + where TPixel : struct, IPixel + { + ref Rgba32 sBase = ref MemoryMarshal.GetReference(source); + ref TPixel dBase = ref MemoryMarshal.GetReference(dest); + + for (int i = 0; i < source.Length; i += 4) + { + ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); + ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); + ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1); + Rgba32 s3 = Unsafe.Add(ref s2, 1); + + ref TPixel d0 = ref Unsafe.Add(ref dBase, i); + ref TPixel d1 = ref Unsafe.Add(ref d0, 1); + ref TPixel d2 = ref Unsafe.Add(ref d1, 1); + + d0.FromRgba32(s0); + d1.FromRgba32(s1); + d2.FromRgba32(s2); + Unsafe.Add(ref d2, 1).FromRgba32(s3); + } + } + + //[Benchmark] + public void Default_Group4_Generic() + { + Group4GenericImpl(this.source.AsSpan(), this.dest.AsSpan()); + } + + //[Benchmark] + public void Default_Group8() + { + ref Rgba32 sBase = ref this.source[0]; + ref Bgra32 dBase = ref this.dest[0]; + + for (int i = 0; i < this.Count / 4; i += 4) + { + ref Rgba32 s0 = ref Unsafe.Add(ref sBase, i); + ref Rgba32 s1 = ref Unsafe.Add(ref s0, 1); + ref Rgba32 s2 = ref Unsafe.Add(ref s1, 1); + ref Rgba32 s3 = ref Unsafe.Add(ref s1, 1); + + ref Rgba32 s4 = ref Unsafe.Add(ref s3, 1); + ref Rgba32 s5 = ref Unsafe.Add(ref s4, 1); + ref Rgba32 s6 = ref Unsafe.Add(ref s5, 1); + Rgba32 s7 = Unsafe.Add(ref s6, 1); + + ref Bgra32 d0 = ref Unsafe.Add(ref dBase, i); + ref Bgra32 d1 = ref Unsafe.Add(ref d0, 1); + ref Bgra32 d2 = ref Unsafe.Add(ref d1, 1); + ref Bgra32 d3 = ref Unsafe.Add(ref d2, 1); + ref Bgra32 d4 = ref Unsafe.Add(ref d3, 1); + + ref Bgra32 d5 = ref Unsafe.Add(ref d4, 1); + ref Bgra32 d6 = ref Unsafe.Add(ref d5, 1); + + + d0.FromRgba32(s0); + d1.FromRgba32(s1); + d2.FromRgba32(s2); + d3.FromRgba32(s3); + + d4.FromRgba32(s4); + d5.FromRgba32(s5); + d6.FromRgba32(s6); + Unsafe.Add(ref d6, 1).FromRgba32(s7); + } + } + + [Benchmark] + public void BitOps() + { + ref uint sBase = ref Unsafe.As(ref this.source[0]); + ref uint dBase = ref Unsafe.As(ref this.dest[0]); + + for (int i = 0; i < this.Count; i++) + { + uint s = Unsafe.Add(ref sBase, i); + Unsafe.Add(ref dBase, i) = FromRgba32.ToBgra32(s); + } + } + + [Benchmark] + public void Bitops_Tuple() + { + ref Tuple4OfUInt32 sBase = ref Unsafe.As(ref this.source[0]); + ref Tuple4OfUInt32 dBase = ref Unsafe.As(ref this.dest[0]); + + for (int i = 0; i < this.Count / 4; i++) + { + ref Tuple4OfUInt32 d = ref Unsafe.Add(ref dBase, i); + d = Unsafe.Add(ref sBase, i); + d.ConvertMe(); + } + } + + //[Benchmark] + public void Bitops_SingleTuple() + { + ref Tuple4OfUInt32 sBase = ref Unsafe.As(ref this.source[0]); + + for (int i = 0; i < this.Count / 4; i++) + { + Unsafe.Add(ref sBase, i).ConvertMe(); + } + } + + //[Benchmark] + public void Bitops_Simd() + { + ref Octet.OfUInt32 sBase = ref Unsafe.As(ref this.source[0]); + ref Octet.OfUInt32 dBase = ref Unsafe.As(ref this.dest[0]); + + for (int i = 0; i < this.Count / 8; i++) + { + BitopsSimdImpl(ref Unsafe.Add(ref sBase, i), ref Unsafe.Add(ref dBase, i)); + } + } + + [StructLayout(LayoutKind.Sequential)] + struct B + { + public uint tmp2, tmp5, tmp8, tmp11, tmp14, tmp17, tmp20, tmp23; + } + + [StructLayout(LayoutKind.Sequential)] + struct C + { + public uint tmp3, tmp6, tmp9, tmp12, tmp15, tmp18, tmp21, tmp24; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void BitopsSimdImpl(ref Octet.OfUInt32 s, ref Octet.OfUInt32 d) + { + Vector sVec = Unsafe.As>(ref s); + Vector aMask = new Vector(0xFF00FF00); + Vector bMask = new Vector(0x00FF00FF); + + Vector aa = sVec & aMask; + Vector bb = sVec & bMask; + + B b = Unsafe.As, B>(ref bb); + + C c = default; + + c.tmp3 = (b.tmp2 << 16) | (b.tmp2 >> 16); + c.tmp6 = (b.tmp5 << 16) | (b.tmp5 >> 16); + c.tmp9 = (b.tmp8 << 16) | (b.tmp8 >> 16); + c.tmp12 = (b.tmp11 << 16) | (b.tmp11 >> 16); + c.tmp15 = (b.tmp14 << 16) | (b.tmp14 >> 16); + c.tmp18 = (b.tmp17 << 16) | (b.tmp17 >> 16); + c.tmp21 = (b.tmp20 << 16) | (b.tmp20 >> 16); + c.tmp24 = (b.tmp23 << 16) | (b.tmp23 >> 16); + + Vector cc = Unsafe.As>(ref c); + Vector dd = aa + cc; + + d = Unsafe.As, Octet.OfUInt32>(ref dd); + } + + //[Benchmark] + public void BitOps_Group2() + { + ref uint sBase = ref Unsafe.As(ref this.source[0]); + ref uint dBase = ref Unsafe.As(ref this.dest[0]); + + for (int i = 0; i < this.Count; i++) + { + ref uint s0 = ref Unsafe.Add(ref sBase, i); + uint s1 = Unsafe.Add(ref s0, 1); + + ref uint d0 = ref Unsafe.Add(ref dBase, i); + d0 = FromRgba32.ToBgra32(s0); + Unsafe.Add(ref d0, 1) = FromRgba32.ToBgra32(s1); + } + } + + [Benchmark] + public void BitOps_GroupAsULong() + { + ref ulong sBase = ref Unsafe.As(ref this.source[0]); + ref ulong dBase = ref Unsafe.As(ref this.dest[0]); + + for (int i = 0; i < this.Count / 2; i++) + { + ulong s = Unsafe.Add(ref sBase, i); + uint lo = (uint)s; + uint hi = (uint)(s >> 32); + lo = FromRgba32.ToBgra32(lo); + hi = FromRgba32.ToBgra32(hi); + + s = (ulong)(hi << 32) | lo; + + Unsafe.Add(ref dBase, i) = s; + } + } + + //[Benchmark] + public void BitOps_GroupAsULong_V2() + { + ref ulong sBase = ref Unsafe.As(ref this.source[0]); + ref ulong dBase = ref Unsafe.As(ref this.dest[0]); + + for (int i = 0; i < this.Count / 2; i++) + { + ulong s = Unsafe.Add(ref sBase, i); + uint lo = (uint)s; + uint hi = (uint)(s >> 32); + + uint tmp1 = lo & 0xFF00FF00; + uint tmp4 = hi & 0xFF00FF00; + + uint tmp2 = lo & 0x00FF00FF; + uint tmp5 = hi & 0x00FF00FF; + + uint tmp3 = (tmp2 << 16) | (tmp2 >> 16); + uint tmp6 = (tmp5 << 16) | (tmp5 >> 16); + + lo = tmp1 + tmp3; + hi = tmp4 + tmp6; + + s = (ulong)(hi << 32) | lo; + + Unsafe.Add(ref dBase, i) = s; + } + } + + public static class FromRgba32 + { + /// + /// Converts a packed to . + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static uint ToArgb32(uint packedRgba) + { + // packedRgba = [aa bb gg rr] + // ROL(8, packedRgba) = [bb gg rr aa] + return (packedRgba << 8) | (packedRgba >> 24); + } + + /// + /// Converts a packed to . + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static uint ToBgra32(uint packedRgba) + { + // packedRgba = [aa bb gg rr] + // tmp1 = [aa 00 gg 00] + // tmp2 = [00 bb 00 rr] + // tmp3=ROL(16, tmp2) = [00 rr 00 bb] + // tmp1 + tmp3 = [aa rr gg bb] + uint tmp1 = packedRgba & 0xFF00FF00; + uint tmp2 = packedRgba & 0x00FF00FF; + uint tmp3 = (tmp2 << 16) | (tmp2 >> 16); + return tmp1 + tmp3; + } + } + + + // RESULTS: + // Method | Count | Mean | Error | StdDev | Scaled | ScaledSD | + // -------------------- |------ |---------:|----------:|----------:|-------:|---------:| + // Default | 64 | 82.67 ns | 0.6737 ns | 0.5625 ns | 1.00 | 0.00 | + // Default_Generic | 64 | 88.73 ns | 1.7959 ns | 1.7638 ns | 1.07 | 0.02 | + // Default_Group2 | 64 | 91.03 ns | 1.5237 ns | 1.3508 ns | 1.10 | 0.02 | + // Default_Group4 | 64 | 86.62 ns | 1.5737 ns | 1.4720 ns | 1.05 | 0.02 | + // BitOps | 64 | 57.45 ns | 0.6067 ns | 0.5066 ns | 0.69 | 0.01 | + // Bitops_Tuple | 64 | 75.47 ns | 1.1824 ns | 1.1060 ns | 0.91 | 0.01 | + // BitOps_GroupAsULong | 64 | 65.42 ns | 0.7157 ns | 0.6695 ns | 0.79 | 0.01 | + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs new file mode 100644 index 0000000000..76de794eca --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestArgb.cs @@ -0,0 +1,89 @@ +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +{ + [StructLayout(LayoutKind.Sequential)] + struct TestArgb : ITestPixel + { + public 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; + } + + [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; + } + + [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; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToVector4() + { + return new Vector4(this.R, this.G, this.B, this.A); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyToVector4(ref Vector4 dest) + { + dest.X = this.R; + dest.Y = this.G; + dest.Z = this.B; + dest.W = this.A; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs new file mode 100644 index 0000000000..36d5f3e5b9 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/PixelConversion/TestRgba.cs @@ -0,0 +1,71 @@ +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Benchmarks.General.PixelConversion +{ + [StructLayout(LayoutKind.Sequential)] + struct TestRgba : ITestPixel + { + public 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; + } + + public void FromVector4(Vector4 source) + { + throw new System.NotImplementedException(); + } + + public void FromVector4(ref Vector4 source) + { + throw new System.NotImplementedException(); + } + + [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); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToVector4() + { + return new Vector4(this.R, this.G, this.B, this.A) * new Vector4(1f / 255f); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyToVector4(ref Vector4 dest) + { + var tmp = new Vector4(this.R, this.G, this.B, this.A); + tmp *= new Vector4(1f / 255f); + dest = tmp; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromRgba32.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromRgba32.cs deleted file mode 100644 index f5bd135e12..0000000000 --- a/tests/ImageSharp.Benchmarks/General/PixelConversion_ConvertFromRgba32.cs +++ /dev/null @@ -1,209 +0,0 @@ -// ReSharper disable InconsistentNaming - -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.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; } - - [GlobalSetup] - 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/Vectorization/Premultiply.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/Premultiply.cs new file mode 100644 index 0000000000..23f13c89b7 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/Premultiply.cs @@ -0,0 +1,59 @@ +using System.Numerics; +using System.Runtime.CompilerServices; +using BenchmarkDotNet.Attributes; + +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +{ + public class Premultiply + { + [Benchmark(Baseline = true)] + public Vector4 PremultiplyByVal() + { + var input = new Vector4(.5F); + return Vector4Utils.Premultiply(input); + } + + [Benchmark] + public Vector4 PremultiplyByRef() + { + var input = new Vector4(.5F); + Vector4Utils.PremultiplyRef(ref input); + return input; + } + + [Benchmark] + public Vector4 PremultiplyRefWithPropertyAssign() + { + var input = new Vector4(.5F); + Vector4Utils.PremultiplyRefWithPropertyAssign(ref input); + return input; + } + } + + internal static class Vector4Utils + { + [MethodImpl(InliningOptions.ShortMethod)] + public static Vector4 Premultiply(Vector4 source) + { + float w = source.W; + Vector4 premultiplied = source * w; + premultiplied.W = w; + return premultiplied; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public static void PremultiplyRef(ref Vector4 source) + { + float w = source.W; + source *= w; + source.W = w; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public static void PremultiplyRefWithPropertyAssign(ref Vector4 source) + { + float w = source.W; + source *= new Vector4(w) { W = 1 }; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs new file mode 100644 index 0000000000..ca85a350cc --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/UInt32ToSingle.cs @@ -0,0 +1,113 @@ +using System.Numerics; +using System.Runtime.CompilerServices; + +using BenchmarkDotNet.Attributes; + +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +{ + [Config(typeof(Config.ShortClr))] + public class UInt32ToSingle + { + private float[] data; + + private const int Count = 32; + + [GlobalSetup] + public void Setup() + { + this.data = new float[Count]; + } + + [Benchmark(Baseline = true)] + public void MagicMethod() + { + ref Vector b = ref Unsafe.As>(ref this.data[0]); + + int n = Count / Vector.Count; + + var bVec = new Vector(256.0f / 255.0f); + var magicFloat = new Vector(32768.0f); + var magicInt = new Vector(1191182336); // reinterpreded value of 32768.0f + var mask = new Vector(255); + + for (int i = 0; i < n; i++) + { + // union { float f; uint32_t i; } u; + // u.f = 32768.0f + x * (255.0f / 256.0f); + // return (uint8_t)u.i; + + ref Vector df = ref Unsafe.Add(ref b, i); + + var vi = Vector.AsVectorUInt32(df); + vi &= mask; + vi |= magicInt; + + var vf = Vector.AsVectorSingle(vi); + vf = (vf - magicFloat) * bVec; + + df = vf; + } + } + + [Benchmark] + public void StandardSimd() + { + int n = Count / Vector.Count; + + ref Vector bf = ref Unsafe.As>(ref this.data[0]); + ref Vector bu = ref Unsafe.As, Vector>(ref bf); + + var scale = new Vector(1f / 255f); + + for (int i = 0; i < n; i++) + { + Vector u = Unsafe.Add(ref bu, i); + Vector v = Vector.ConvertToSingle(u); + v *= scale; + Unsafe.Add(ref bf, i) = v; + } + } + + [Benchmark] + public void StandardSimdFromInt() + { + int n = Count / Vector.Count; + + ref Vector bf = ref Unsafe.As>(ref this.data[0]); + ref Vector bu = ref Unsafe.As, Vector>(ref bf); + + var scale = new Vector(1f / 255f); + + for (int i = 0; i < n; i++) + { + Vector u = Unsafe.Add(ref bu, i); + Vector v = Vector.ConvertToSingle(u); + v *= scale; + Unsafe.Add(ref bf, i) = v; + } + } + + + [Benchmark] + public void StandardSimdFromInt_RefCast() + { + int n = Count / Vector.Count; + + ref Vector bf = ref Unsafe.As>(ref this.data[0]); + ref Vector bu = ref Unsafe.As, Vector>(ref bf); + + var scale = new Vector(1f / 255f); + + for (int i = 0; i < n; i++) + { + ref Vector fRef = ref Unsafe.Add(ref bf, i); + + Vector du = Vector.AsVectorInt32(fRef); + Vector v = Vector.ConvertToSingle(du); + v *= scale; + + fRef = v; + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs new file mode 100644 index 0000000000..2bc3af4c98 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/General/Vectorization/WidenBytesToUInt32.cs @@ -0,0 +1,64 @@ +using System.Numerics; +using System.Runtime.CompilerServices; + +using BenchmarkDotNet.Attributes; + +using SixLabors.ImageSharp.Tuples; + +namespace SixLabors.ImageSharp.Benchmarks.General.Vectorization +{ + [Config(typeof(Config.ShortClr))] + public class WidenBytesToUInt32 + { + private byte[] source; + + private uint[] dest; + + private const int Count = 64; + + [GlobalSetup] + public void Setup() + { + this.source = new byte[Count]; + this.dest = new uint[Count]; + } + + [Benchmark(Baseline = true)] + public void Standard() + { + const int N = Count / 8; + + ref Octet.OfByte sBase = ref Unsafe.As(ref this.source[0]); + ref Octet.OfUInt32 dBase = ref Unsafe.As(ref this.dest[0]); + + for (int i = 0; i < N; i++) + { + Unsafe.Add(ref dBase, i).LoadFrom(ref Unsafe.Add(ref sBase, i)); + } + } + + [Benchmark] + public void Simd() + { + int n = Count / Vector.Count; + + ref Vector sBase = ref Unsafe.As>(ref this.source[0]); + ref Vector dBase = ref Unsafe.As>(ref this.dest[0]); + + for (int i = 0; i < n; i++) + { + Vector b = Unsafe.Add(ref sBase, i); + + Vector.Widen(b, out Vector s0, out Vector s1); + Vector.Widen(s0, out Vector w0, out Vector w1); + Vector.Widen(s1, out Vector w2, out Vector w3); + + ref Vector d = ref Unsafe.Add(ref dBase, i * 4); + d = w0; + Unsafe.Add(ref d, 1) = w1; + Unsafe.Add(ref d, 2) = w2; + Unsafe.Add(ref d, 3) = w3; + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 35a3a67e9e..a705c9bacb 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -1,11 +1,11 @@  - netcoreapp2.0;net461 + netcoreapp2.1;net461 Exe True SixLabors.ImageSharp.Benchmarks ImageSharp.Benchmarks - 7.2 + 7.3 win7-x64 @@ -15,13 +15,12 @@ - + - diff --git a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs index d9cf65d4be..cdd56fa074 100644 --- a/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs +++ b/tests/ImageSharp.Benchmarks/PixelBlenders/PorterDuffBulkVsPixel.cs @@ -17,6 +17,8 @@ namespace SixLabors.ImageSharp.Benchmarks public class PorterDuffBulkVsPixel : BenchmarkBase { + private Configuration Configuration => Configuration.Default; + private void BulkVectorConvert( Span destination, Span background, @@ -35,15 +37,15 @@ namespace SixLabors.ImageSharp.Benchmarks Span backgroundSpan = buffer.Slice(destination.Length, destination.Length); Span sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); - PixelOperations.Instance.ToVector4(background, backgroundSpan, destination.Length); - PixelOperations.Instance.ToVector4(source, sourceSpan, destination.Length); + PixelOperations.Instance.ToVector4(this.Configuration, background, backgroundSpan); + PixelOperations.Instance.ToVector4(this.Configuration, source, sourceSpan); for (int i = 0; i < destination.Length; i++) { destinationSpan[i] = PorterDuffFunctions.NormalSrcOver(backgroundSpan[i], sourceSpan[i], amount[i]); } - PixelOperations.Instance.PackFromVector4(destinationSpan, destination, destination.Length); + PixelOperations.Instance.FromVector4(this.Configuration, destinationSpan, destination); } } diff --git a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs index fe1d4221d1..729971548c 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Glow.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Glow.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Overlays; @@ -112,26 +113,29 @@ namespace SixLabors.ImageSharp.Benchmarks Buffer2D sourcePixels = source.PixelBuffer; rowColors.GetSpan().Fill(glowColor); - ParallelFor.WithConfiguration( - minY, - maxY, + var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); + ParallelHelper.IterateRows( + workingRect, configuration, - y => + rows => { - int offsetY = y - startY; - - for (int x = minX; x < maxX; x++) + for (int y = rows.Min; y < rows.Max; y++) { - int offsetX = x - startX; - float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY)); - Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4(); - TPixel packed = default(TPixel); - packed.PackFromVector4( - PremultipliedLerp( - sourceColor, - glowColor.ToVector4(), - 1 - (.95F * (distance / maxDistance)))); - sourcePixels[offsetX, offsetY] = packed; + int offsetY = y - startY; + + for (int x = minX; x < maxX; x++) + { + int offsetX = x - startX; + float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY)); + Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4(); + TPixel packed = default(TPixel); + packed.FromVector4( + PremultipliedLerp( + sourceColor, + glowColor.ToVector4(), + 1 - (.95F * (distance / maxDistance)))); + sourcePixels[offsetX, offsetY] = packed; + } } }); } diff --git a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs index 86dc13e91e..148b253281 100644 --- a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs +++ b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs @@ -1,7 +1,5 @@ -// -// Copyright (c) James Jackson-South and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -// using System; using System.Drawing; @@ -9,90 +7,141 @@ using System.Drawing.Drawing2D; using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using CoreSize = SixLabors.Primitives.Size; - namespace SixLabors.ImageSharp.Benchmarks { - using System.Threading.Tasks; - - using SixLabors.ImageSharp.Formats.Jpeg; - [Config(typeof(Config.ShortClr))] - public class Resize : BenchmarkBase + public abstract class ResizeBenchmarkBase { - private readonly Configuration configuration = new Configuration(new JpegConfigurationModule()); + protected readonly Configuration Configuration = new Configuration(new JpegConfigurationModule()); + + private Image sourceImage; - [Params(false, true)] - public bool EnableParallelExecution { get; set; } + private Bitmap sourceBitmap; + + [Params(3032)] + public int SourceSize { get; set; } + + [Params(400)] + public int DestSize { get; set; } [GlobalSetup] public void Setup() { - this.configuration.MaxDegreeOfParallelism = - this.EnableParallelExecution ? Environment.ProcessorCount : 1; + this.sourceImage = new Image(this.Configuration, this.SourceSize, this.SourceSize); + this.sourceBitmap = new Bitmap(this.SourceSize, this.SourceSize); + } + + [GlobalCleanup] + public void Cleanup() + { + this.sourceImage.Dispose(); + this.sourceBitmap.Dispose(); } - [Benchmark(Baseline = true, Description = "System.Drawing Resize")] - public Size ResizeSystemDrawing() + [Benchmark(Baseline = true)] + public int SystemDrawing() { - using (Bitmap source = new Bitmap(2000, 2000)) + using (var destination = new Bitmap(this.DestSize, this.DestSize)) { - using (Bitmap destination = new Bitmap(400, 400)) + using (var g = Graphics.FromImage(destination)) { - using (Graphics graphics = Graphics.FromImage(destination)) - { - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.DrawImage(source, 0, 0, 400, 400); - } - - return destination.Size; + g.CompositingMode = CompositingMode.SourceCopy; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.PixelOffsetMode = PixelOffsetMode.HighQuality; + g.CompositingQuality = CompositingQuality.HighQuality; + g.SmoothingMode = SmoothingMode.HighQuality; + + g.DrawImage(this.sourceBitmap, 0, 0, this.DestSize, this.DestSize); } + + return destination.Width; } } - [Benchmark(Description = "ImageSharp Resize")] - public CoreSize ResizeCore() + [Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 1")] + public int ImageSharp_P1() => this.RunImageSharpResize(1); + + [Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 4")] + public int ImageSharp_P4() => this.RunImageSharpResize(4); + + [Benchmark(Description = "ImageSharp, MaxDegreeOfParallelism = 8")] + public int ImageSharp_P8() => this.RunImageSharpResize(8); + + protected int RunImageSharpResize(int maxDegreeOfParallelism) { - using (var image = new Image(this.configuration, 2000, 2000)) + this.Configuration.MaxDegreeOfParallelism = maxDegreeOfParallelism; + + using (Image clone = this.sourceImage.Clone(this.ExecuteResizeOperation)) { - image.Mutate(x => x.Resize(400, 400)); - return new CoreSize(image.Width, image.Height); + return clone.Width; } } - //[Benchmark(Description = "ImageSharp Vector Resize")] - //public CoreSize ResizeCoreVector() - //{ - // using (Image image = new Image(2000, 2000)) - // { - // image.Resize(400, 400); - // return new CoreSize(image.Width, image.Height); - // } - //} - - //[Benchmark(Description = "ImageSharp Compand Resize")] - //public CoreSize ResizeCoreCompand() - //{ - // using (Image image = new Image(2000, 2000)) - // { - // image.Resize(400, 400, true); - // return new CoreSize(image.Width, image.Height); - // } - //} - - //[Benchmark(Description = "ImageSharp Vector Compand Resize")] - //public CoreSize ResizeCoreVectorCompand() - //{ - // using (Image image = new Image(2000, 2000)) - // { - // image.Resize(400, 400, true); - // return new CoreSize(image.Width, image.Height); - // } - //} + protected abstract void ExecuteResizeOperation(IImageProcessingContext ctx); + } + + public class Resize_Bicubic : ResizeBenchmarkBase + { + protected override void ExecuteResizeOperation(IImageProcessingContext ctx) + { + ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic); + } + + // RESULTS (2018 October): + // + // BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134 + // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores + // Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC + // .NET Core SDK=2.1.403 + // [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT + // Job-IGUFBA : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0 + // Job-DZFERG : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT + // + // Method | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Scaled | ScaledSD | Allocated | + // ----------------------------------------- |-------- |----------- |--------- |----------:|----------:|----------:|-------:|---------:|----------:| + // SystemDrawing | Clr | 3032 | 400 | 101.13 ms | 18.659 ms | 1.0542 ms | 1.00 | 0.00 | 0 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | 3032 | 400 | 122.05 ms | 19.622 ms | 1.1087 ms | 1.21 | 0.01 | 21856 B | + // 'ImageSharp, MaxDegreeOfParallelism = 4' | Clr | 3032 | 400 | 41.34 ms | 54.841 ms | 3.0986 ms | 0.41 | 0.03 | 28000 B | + // 'ImageSharp, MaxDegreeOfParallelism = 8' | Clr | 3032 | 400 | 31.68 ms | 12.782 ms | 0.7222 ms | 0.31 | 0.01 | 28256 B | + // | | | | | | | | | | + // SystemDrawing | Core | 3032 | 400 | 100.37 ms | 18.479 ms | 1.0441 ms | 1.00 | 0.00 | 0 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | 3032 | 400 | 73.03 ms | 10.540 ms | 0.5955 ms | 0.73 | 0.01 | 21368 B | + // 'ImageSharp, MaxDegreeOfParallelism = 4' | Core | 3032 | 400 | 22.59 ms | 4.863 ms | 0.2748 ms | 0.23 | 0.00 | 25220 B | + // 'ImageSharp, MaxDegreeOfParallelism = 8' | Core | 3032 | 400 | 21.10 ms | 23.362 ms | 1.3200 ms | 0.21 | 0.01 | 25539 B | + + } + + public class Resize_BicubicCompand : ResizeBenchmarkBase + { + protected override void ExecuteResizeOperation(IImageProcessingContext ctx) + { + ctx.Resize(this.DestSize, this.DestSize, KnownResamplers.Bicubic, true); + } + + // RESULTS (2018 October): + // + // BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134 + // Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores + // Frequency=2742191 Hz, Resolution=364.6719 ns, Timer=TSC + // .NET Core SDK=2.1.403 + // [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT + // Job-IGUFBA : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0 + // Job-DZFERG : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT + // + // Method | Runtime | SourceSize | DestSize | Mean | Error | StdDev | Scaled | ScaledSD | Allocated | + // ----------------------------------------- |-------- |----------- |--------- |----------:|----------:|----------:|-------:|---------:|----------:| + // SystemDrawing | Clr | 3032 | 400 | 100.63 ms | 13.864 ms | 0.7833 ms | 1.00 | 0.00 | 0 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Clr | 3032 | 400 | 156.83 ms | 28.631 ms | 1.6177 ms | 1.56 | 0.02 | 21856 B | + // 'ImageSharp, MaxDegreeOfParallelism = 4' | Clr | 3032 | 400 | 53.43 ms | 38.493 ms | 2.1749 ms | 0.53 | 0.02 | 28512 B | + // 'ImageSharp, MaxDegreeOfParallelism = 8' | Clr | 3032 | 400 | 38.47 ms | 11.969 ms | 0.6763 ms | 0.38 | 0.01 | 28000 B | + // | | | | | | | | | | + // SystemDrawing | Core | 3032 | 400 | 99.87 ms | 23.459 ms | 1.3255 ms | 1.00 | 0.00 | 0 B | + // 'ImageSharp, MaxDegreeOfParallelism = 1' | Core | 3032 | 400 | 108.19 ms | 38.562 ms | 2.1788 ms | 1.08 | 0.02 | 21368 B | + // 'ImageSharp, MaxDegreeOfParallelism = 4' | Core | 3032 | 400 | 36.21 ms | 53.802 ms | 3.0399 ms | 0.36 | 0.03 | 25300 B | + // 'ImageSharp, MaxDegreeOfParallelism = 8' | Core | 3032 | 400 | 26.52 ms | 2.173 ms | 0.1228 ms | 0.27 | 0.00 | 25589 B | } } diff --git a/tests/ImageSharp.Benchmarks/Samplers/Rotate.cs b/tests/ImageSharp.Benchmarks/Samplers/Rotate.cs new file mode 100644 index 0000000000..f898576af0 --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Samplers/Rotate.cs @@ -0,0 +1,45 @@ +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Benchmarks.Samplers +{ + [Config(typeof(Config.ShortClr))] + public class Rotate + { + [Benchmark] + public Size DoRotate() + { + using (var image = new Image(Configuration.Default, 400, 400, Rgba32.BlanchedAlmond)) + { + image.Mutate(x => x.Rotate(37.5F)); + + return image.Size(); + } + } + } +} + +// Nov 7 2018 +//BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17763 +//Intel Core i7-6600U CPU 2.60GHz(Skylake), 1 CPU, 4 logical and 2 physical cores +//.NET Core SDK = 2.1.403 + +// [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT +// Job-KKDIMW : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0 +// Job-IUZRFA : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT + +//LaunchCount=1 TargetCount=3 WarmupCount=3 + +// #### BEFORE ####: +// Method | Runtime | Mean | Error | StdDev | Allocated | +//--------- |-------- |---------:|----------:|----------:|----------:| +// DoRotate | Clr | 85.19 ms | 13.379 ms | 0.7560 ms | 6 KB | +// DoRotate | Core | 53.51 ms | 9.512 ms | 0.5375 ms | 4.29 KB | + +// #### AFTER ####: +//Method | Runtime | Mean | Error | StdDev | Allocated | +//--------- |-------- |---------:|---------:|---------:|----------:| +// DoRotate | Clr | 77.08 ms | 23.97 ms | 1.354 ms | 6 KB | +// DoRotate | Core | 40.36 ms | 47.43 ms | 2.680 ms | 4.36 KB | \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/Samplers/Skew.cs b/tests/ImageSharp.Benchmarks/Samplers/Skew.cs new file mode 100644 index 0000000000..84819750af --- /dev/null +++ b/tests/ImageSharp.Benchmarks/Samplers/Skew.cs @@ -0,0 +1,45 @@ +using BenchmarkDotNet.Attributes; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Benchmarks.Samplers +{ + [Config(typeof(Config.ShortClr))] + public class Skew + { + [Benchmark] + public Size DoSkew() + { + using (var image = new Image(Configuration.Default, 400, 400, Rgba32.BlanchedAlmond)) + { + image.Mutate(x => x.Skew(20, 10)); + + return image.Size(); + } + } + } +} + +// Nov 7 2018 +//BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17763 +//Intel Core i7-6600U CPU 2.60GHz(Skylake), 1 CPU, 4 logical and 2 physical cores +//.NET Core SDK = 2.1.403 + +// [Host] : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT +// Job-KKDIMW : .NET Framework 4.7.1 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3190.0 +// Job-IUZRFA : .NET Core 2.1.5 (CoreCLR 4.6.26919.02, CoreFX 4.6.26919.02), 64bit RyuJIT + +//LaunchCount=1 TargetCount=3 WarmupCount=3 + +// #### BEFORE ####: +//Method | Runtime | Mean | Error | StdDev | Allocated | +//------- |-------- |---------:|---------:|----------:|----------:| +// DoSkew | Clr | 78.14 ms | 8.383 ms | 0.4736 ms | 6 KB | +// DoSkew | Core | 44.22 ms | 4.109 ms | 0.2322 ms | 4.28 KB | + +// #### AFTER ####: +//Method | Runtime | Mean | Error | StdDev | Allocated | +//------- |-------- |---------:|----------:|----------:|----------:| +// DoSkew | Clr | 71.63 ms | 25.589 ms | 1.4458 ms | 6 KB | +// DoSkew | Core | 38.99 ms | 8.640 ms | 0.4882 ms | 4.36 KB | \ No newline at end of file diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index 0ca3cffa18..4d7b7de759 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -11,7 +11,7 @@ James Jackson-South and contributors James Jackson-South SixLabors.ImageSharp.Sandbox46 - 7.2 + 7.3 @@ -19,7 +19,7 @@ - + diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs index fa1d63878a..02d4f80c55 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Sandbox46/Program.cs @@ -3,17 +3,15 @@ // Licensed under the Apache License, Version 2.0. // +using SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations; +using SixLabors.ImageSharp.Tests.ProfilingBenchmarks; + namespace SixLabors.ImageSharp.Sandbox46 { using System; - using System.Runtime.DesignerServices; - - using SixLabors.ImageSharp.Tests; - using SixLabors.ImageSharp.Tests.Colors; using SixLabors.ImageSharp.Tests.Formats.Jpg; using SixLabors.ImageSharp.Tests.PixelFormats; using SixLabors.ImageSharp.Tests.Processing.Processors.Transforms; - using SixLabors.ImageSharp.Tests.Processing.Transforms; using Xunit.Abstractions; @@ -21,15 +19,9 @@ namespace SixLabors.ImageSharp.Sandbox46 { private class ConsoleOutput : ITestOutputHelper { - public void WriteLine(string message) - { - Console.WriteLine(message); - } + public void WriteLine(string message) => Console.WriteLine(message); - public void WriteLine(string format, params object[] args) - { - Console.WriteLine(format, args); - } + public void WriteLine(string format, params object[] args) => Console.WriteLine(format, args); } /// @@ -58,20 +50,20 @@ namespace SixLabors.ImageSharp.Sandbox46 private static void RunResizeProfilingTest() { - ResizeProfilingBenchmarks test = new ResizeProfilingBenchmarks(new ConsoleOutput()); + var test = new ResizeProfilingBenchmarks(new ConsoleOutput()); test.ResizeBicubic(2000, 2000); } private static void RunToVector4ProfilingTest() { - PixelOperationsTests.Rgba32 tests = new PixelOperationsTests.Rgba32(new ConsoleOutput()); + var tests = new PixelOperationsTests.Rgba32OperationsTests(new ConsoleOutput()); tests.Benchmark_ToVector4(); } private static void RunDecodeJpegProfilingTests() { Console.WriteLine("RunDecodeJpegProfilingTests..."); - JpegProfilingBenchmarks benchmarks = new JpegProfilingBenchmarks(new ConsoleOutput()); + var benchmarks = new JpegProfilingBenchmarks(new ConsoleOutput()); foreach (object[] data in JpegProfilingBenchmarks.DecodeJpegData) { string fileName = (string)data[0]; diff --git a/tests/ImageSharp.Tests/Advanced/AotCompilerTests.cs b/tests/ImageSharp.Tests/Advanced/AotCompilerTests.cs new file mode 100644 index 0000000000..f6397dbd09 --- /dev/null +++ b/tests/ImageSharp.Tests/Advanced/AotCompilerTests.cs @@ -0,0 +1,15 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Advanced +{ + public class AotCompilerTests + { + [Fact] + public void AotCompiler_NoExceptions() => AotCompilerTools.Seed(); + } +} diff --git a/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs b/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs index 34b2f718ee..7adbefb346 100644 --- a/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs +++ b/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs @@ -14,7 +14,9 @@ namespace SixLabors.ImageSharp.Tests private readonly FakeImageOperationsProvider.FakeImageOperations internalOperations; protected readonly Rectangle rect; protected readonly GraphicsOptions options; - private Image source; + private readonly Image source; + + public Rectangle SourceBounds() => this.source.Bounds(); public BaseImageOperationsExtensionTest() { @@ -29,7 +31,7 @@ namespace SixLabors.ImageSharp.Tests { Assert.InRange(index, 0, this.internalOperations.Applied.Count - 1); - var operation = this.internalOperations.Applied[index]; + FakeImageOperationsProvider.FakeImageOperations.AppliedOperation operation = this.internalOperations.Applied[index]; return Assert.IsType(operation.Processor); } @@ -38,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests { Assert.InRange(index, 0, this.internalOperations.Applied.Count - 1); - var operation = this.internalOperations.Applied[index]; + FakeImageOperationsProvider.FakeImageOperations.AppliedOperation operation = this.internalOperations.Applied[index]; Assert.Equal(rect, operation.Rectangle); return Assert.IsType(operation.Processor); diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLabTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLabTests.cs new file mode 100644 index 0000000000..dbc07b916e --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/CieLabTests.cs @@ -0,0 +1,46 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ + /// + /// Tests the struct. + /// + public class CieLabTests + { + [Fact] + public void CieLabConstructorAssignsFields() + { + const float l = 75F; + const float a = -64F; + const float b = 87F; + var cieLab = new CieLab(l, a, b); + + Assert.Equal(l, cieLab.L); + Assert.Equal(a, cieLab.A); + Assert.Equal(b, cieLab.B); + } + + [Fact] + public void CieLabEquality() + { + var x = default(CieLab); + var y = new CieLab(Vector3.One); + + Assert.True(default(CieLab) == default(CieLab)); + Assert.True(default(CieLab) != new CieLab(1, 0, 1)); + Assert.False(default(CieLab) == new CieLab(1, 0, 1)); + Assert.Equal(default(CieLab), default(CieLab)); + Assert.Equal(new CieLab(1, 0, 1), new CieLab(1, 0, 1)); + Assert.Equal(new CieLab(Vector3.One), new CieLab(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(default(CieLab) == new CieLab(1, 0, 1)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLchTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLchTests.cs new file mode 100644 index 0000000000..90c2c22446 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/CieLchTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ + /// + /// Tests the struct. + /// + public class CieLchTests + { + [Fact] + public void CieLchConstructorAssignsFields() + { + const float l = 75F; + const float c = 64F; + const float h = 287F; + var cieLch = new CieLch(l, c, h); + + Assert.Equal(l, cieLch.L); + Assert.Equal(c, cieLch.C); + Assert.Equal(h, cieLch.H); + } + + [Fact] + public void CieLchEquality() + { + var x = default(CieLch); + var y = new CieLch(Vector3.One); + + Assert.True(default(CieLch) == default(CieLch)); + Assert.False(default(CieLch) != default(CieLch)); + Assert.Equal(default(CieLch), default(CieLch)); + Assert.Equal(new CieLch(1, 0, 1), new CieLch(1, 0, 1)); + Assert.Equal(new CieLch(Vector3.One), new CieLch(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + } +} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLchuvTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLchuvTests.cs new file mode 100644 index 0000000000..a6a5fa32ad --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/CieLchuvTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ + /// + /// Tests the struct. + /// + public class CieLchuvTests + { + [Fact] + public void CieLchuvConstructorAssignsFields() + { + const float l = 75F; + const float c = 64F; + const float h = 287F; + var cieLchuv = new CieLchuv(l, c, h); + + Assert.Equal(l, cieLchuv.L); + Assert.Equal(c, cieLchuv.C); + Assert.Equal(h, cieLchuv.H); + } + + [Fact] + public void CieLchuvEquality() + { + var x = default(CieLchuv); + var y = new CieLchuv(Vector3.One); + + Assert.True(default(CieLchuv) == default(CieLchuv)); + Assert.False(default(CieLchuv) != default(CieLchuv)); + Assert.Equal(default(CieLchuv), default(CieLchuv)); + Assert.Equal(new CieLchuv(1, 0, 1), new CieLchuv(1, 0, 1)); + Assert.Equal(new CieLchuv(Vector3.One), new CieLchuv(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + } +} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLuvTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLuvTests.cs new file mode 100644 index 0000000000..dbf64cb1d0 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/CieLuvTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ + /// + /// Tests the struct. + /// + public class CieLuvTests + { + [Fact] + public void CieLuvConstructorAssignsFields() + { + const float l = 75F; + const float c = -64F; + const float h = 87F; + var cieLuv = new CieLuv(l, c, h); + + Assert.Equal(l, cieLuv.L); + Assert.Equal(c, cieLuv.U); + Assert.Equal(h, cieLuv.V); + } + + [Fact] + public void CieLuvEquality() + { + var x = default(CieLuv); + var y = new CieLuv(Vector3.One); + + Assert.True(default(CieLuv) == default(CieLuv)); + Assert.False(default(CieLuv) != default(CieLuv)); + Assert.Equal(default(CieLuv), default(CieLuv)); + Assert.Equal(new CieLuv(1, 0, 1), new CieLuv(1, 0, 1)); + Assert.Equal(new CieLuv(Vector3.One), new CieLuv(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + } +} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs new file mode 100644 index 0000000000..42ace9dbed --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ + /// + /// Tests the struct. + /// + public class CieXyChromaticityCoordinatesTests + { + [Fact] + public void CieXyChromaticityCoordinatesConstructorAssignsFields() + { + const float x = .75F; + const float y = .64F; + var coordinates = new CieXyChromaticityCoordinates(x, y); + + Assert.Equal(x, coordinates.X); + Assert.Equal(y, coordinates.Y); + } + + [Fact] + public void CieXyChromaticityCoordinatesEquality() + { + var x = default(CieXyChromaticityCoordinates); + var y = new CieXyChromaticityCoordinates(1, 1); + + Assert.True(default(CieXyChromaticityCoordinates) == default(CieXyChromaticityCoordinates)); + Assert.True(default(CieXyChromaticityCoordinates) != new CieXyChromaticityCoordinates(1, 0)); + Assert.False(default(CieXyChromaticityCoordinates) == new CieXyChromaticityCoordinates(1, 0)); + Assert.Equal(default(CieXyChromaticityCoordinates), default(CieXyChromaticityCoordinates)); + Assert.Equal(new CieXyChromaticityCoordinates(1, 0), new CieXyChromaticityCoordinates(1, 0)); + Assert.Equal(new CieXyChromaticityCoordinates(1, 1), new CieXyChromaticityCoordinates(1, 1)); + Assert.False(x.Equals(y)); + Assert.False(default(CieXyChromaticityCoordinates) == new CieXyChromaticityCoordinates(1, 0)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyyTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyyTests.cs new file mode 100644 index 0000000000..88196034bf --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyyTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ + /// + /// Tests the struct. + /// + public class CieXyyTests + { + [Fact] + public void CieXyyConstructorAssignsFields() + { + const float x = 75F; + const float y = 64F; + const float yl = 287F; + var cieXyy = new CieXyy(x, y, yl); + + Assert.Equal(x, cieXyy.X); + Assert.Equal(y, cieXyy.Y); + Assert.Equal(y, cieXyy.Y); + } + + [Fact] + public void CieXyyEquality() + { + var x = default(CieXyy); + var y = new CieXyy(Vector3.One); + + Assert.True(default(CieXyy) == default(CieXyy)); + Assert.False(default(CieXyy) != default(CieXyy)); + Assert.Equal(default(CieXyy), default(CieXyy)); + Assert.Equal(new CieXyy(1, 0, 1), new CieXyy(1, 0, 1)); + Assert.Equal(new CieXyy(Vector3.One), new CieXyy(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + } +} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndHunterLabConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzAndHunterLabConversionTest.cs deleted file mode 100644 index bea392c167..0000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndHunterLabConversionTest.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Collections.Generic; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Colorspaces -{ - /// - /// Tests - conversions. - /// - /// - /// Test data generated using: - /// - /// - public class CieXyzAndHunterLabConversionTest - { - private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(4); - - /// - /// Tests conversion from to (). - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(100, 0, 0, 0.98074, 1, 1.18232)] // C white point is HunterLab 100, 0, 0 - public void Convert_HunterLab_to_Xyz(float l, float a, float b, float x, float y, float z) - { - // Arrange - var input = new HunterLab(l, a, b); - var converter = new ColorSpaceConverter { WhitePoint = Illuminants.C }; - - // Act - CieXyz output = converter.ToCieXyz(input); - - // Assert - Assert.Equal(x, output.X, FloatRoundingComparer); - Assert.Equal(y, output.Y, FloatRoundingComparer); - Assert.Equal(z, output.Z, FloatRoundingComparer); - } - - /// - /// Tests conversion from to (). - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(100, 0, 0, 0.95047, 1, 1.08883)] // D65 white point is HunerLab 100, 0, 0 (adaptation to C performed) - public void Convert_HunterLab_to_Xyz_D65(float l, float a, float b, float x, float y, float z) - { - // Arrange - var input = new HunterLab(l, a, b); - var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65 }; - - // Act - CieXyz output = converter.ToCieXyz(input); - - // Assert - Assert.Equal(x, output.X, FloatRoundingComparer); - Assert.Equal(y, output.Y, FloatRoundingComparer); - Assert.Equal(z, output.Z, FloatRoundingComparer); - } - - /// - /// Tests conversion from () to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.95047, 1, 1.08883, 100, 0, 0)] // D65 white point is HunterLab 100, 0, 0 (adaptation to C performed) - public void Convert_Xyz_D65_to_HunterLab(float x, float y, float z, float l, float a, float b) - { - // Arrange - var input = new CieXyz(x, y, z); - var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65 }; - - // Act - HunterLab output = converter.ToHunterLab(input); - - // Assert - Assert.Equal(l, output.L, FloatRoundingComparer); - Assert.Equal(a, output.A, FloatRoundingComparer); - Assert.Equal(b, output.B, FloatRoundingComparer); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzTests.cs new file mode 100644 index 0000000000..3c77f132e3 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/CieXyzTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ + /// + /// Tests the struct. + /// + public class CieXyzTests + { + [Fact] + public void CieXyzConstructorAssignsFields() + { + const float x = 75F; + const float y = 64F; + const float z = 287F; + var cieXyz = new CieXyz(x, y, z); + + Assert.Equal(x, cieXyz.X); + Assert.Equal(y, cieXyz.Y); + Assert.Equal(z, cieXyz.Z); + } + + [Fact] + public void CieXyzEquality() + { + var x = default(CieXyz); + var y = new CieXyz(Vector3.One); + + Assert.True(default(CieXyz) == default(CieXyz)); + Assert.False(default(CieXyz) != default(CieXyz)); + Assert.Equal(default(CieXyz), default(CieXyz)); + Assert.Equal(new CieXyz(1, 0, 1), new CieXyz(1, 0, 1)); + Assert.Equal(new CieXyz(Vector3.One), new CieXyz(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + } +} diff --git a/tests/ImageSharp.Tests/Colorspaces/CmykTests.cs b/tests/ImageSharp.Tests/Colorspaces/CmykTests.cs new file mode 100644 index 0000000000..dbf3fe6d8a --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/CmykTests.cs @@ -0,0 +1,46 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ + /// + /// Tests the struct. + /// + public class CmykTests + { + [Fact] + public void CmykConstructorAssignsFields() + { + const float c = .75F; + const float m = .64F; + const float y = .87F; + const float k = .334F; + var cmyk = new Cmyk(c, m, y, k); + + Assert.Equal(c, cmyk.C); + Assert.Equal(m, cmyk.M); + Assert.Equal(y, cmyk.Y); + Assert.Equal(k, cmyk.K); + } + + [Fact] + public void CmykEquality() + { + var x = default(Cmyk); + var y = new Cmyk(Vector4.One); + + Assert.True(default(Cmyk) == default(Cmyk)); + Assert.False(default(Cmyk) != default(Cmyk)); + Assert.Equal(default(Cmyk), default(Cmyk)); + Assert.Equal(new Cmyk(1, 0, 1, 0), new Cmyk(1, 0, 1, 0)); + Assert.Equal(new Cmyk(Vector4.One), new Cmyk(Vector4.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs b/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs deleted file mode 100644 index b393c51b73..0000000000 --- a/tests/ImageSharp.Tests/Colorspaces/ColorConverterAdaptTest.cs +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Collections.Generic; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.LmsColorSapce; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Colorspaces -{ - /// - /// Tests methods. - /// Test data generated using: - /// - /// - /// - public class ColorConverterAdaptTest - { - private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(3); - - private static readonly ApproximateFloatComparer ApproximateComparer = new ApproximateFloatComparer(0.0001F); - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(1, 1, 1, 1, 1, 1)] - [InlineData(0.206162, 0.260277, 0.746717, 0.220000, 0.130000, 0.780000)] - public void Adapt_RGB_WideGamutRGB_To_sRGB(float r1, float g1, float b1, float r2, float g2, float b2) - { - // Arrange - var input = new Rgb(r1, g1, b1, RgbWorkingSpaces.WideGamutRgb); - var expectedOutput = new Rgb(r2, g2, b2, RgbWorkingSpaces.SRgb); - var converter = new ColorSpaceConverter { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; - - // Action - Rgb output = converter.Adapt(input); - - // Assert - Assert.Equal(expectedOutput.WorkingSpace, output.WorkingSpace, ApproximateComparer); - Assert.Equal(expectedOutput.R, output.R, FloatRoundingComparer); - Assert.Equal(expectedOutput.G, output.G, FloatRoundingComparer); - Assert.Equal(expectedOutput.B, output.B, FloatRoundingComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(1, 1, 1, 1, 1, 1)] - [InlineData(0.220000, 0.130000, 0.780000, 0.206162, 0.260277, 0.746717)] - public void Adapt_RGB_SRGB_To_WideGamutRGB(float r1, float g1, float b1, float r2, float g2, float b2) - { - // Arrange - var input = new Rgb(r1, g1, b1, RgbWorkingSpaces.SRgb); - var expectedOutput = new Rgb(r2, g2, b2, RgbWorkingSpaces.WideGamutRgb); - var converter = new ColorSpaceConverter { TargetRgbWorkingSpace = RgbWorkingSpaces.WideGamutRgb }; - - // Action - Rgb output = converter.Adapt(input); - - // Assert - Assert.Equal(expectedOutput.WorkingSpace, output.WorkingSpace, ApproximateComparer); - Assert.Equal(expectedOutput.R, output.R, FloatRoundingComparer); - Assert.Equal(expectedOutput.G, output.G, FloatRoundingComparer); - Assert.Equal(expectedOutput.B, output.B, FloatRoundingComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(22, 33, 1, 22.269869, 32.841164, 1.633926)] - public void Adapt_Lab_D50_To_D65(float l1, float a1, float b1, float l2, float a2, float b2) - { - // Arrange - var input = new CieLab(l1, a1, b1, Illuminants.D65); - var expectedOutput = new CieLab(l2, a2, b2); - var converter = new ColorSpaceConverter { TargetLabWhitePoint = Illuminants.D50 }; - - // Action - CieLab output = converter.Adapt(input); - - // Assert - Assert.Equal(expectedOutput.L, output.L, FloatRoundingComparer); - Assert.Equal(expectedOutput.A, output.A, FloatRoundingComparer); - Assert.Equal(expectedOutput.B, output.B, FloatRoundingComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.5, 0.5, 0.5, 0.510286, 0.501489, 0.378970)] - public void Adapt_Xyz_D65_To_D50_Bradford(float x1, float y1, float z1, float x2, float y2, float z2) - { - // Arrange - CieXyz input = new CieXyz(x1, y1, z1); - CieXyz expectedOutput = new CieXyz(x2, y2, z2); - ColorSpaceConverter converter = new ColorSpaceConverter - { - WhitePoint = Illuminants.D50 - }; - - // Action - CieXyz output = converter.Adapt(input, Illuminants.D65); - - // Assert - Assert.Equal(expectedOutput.X, output.X, FloatRoundingComparer); - Assert.Equal(expectedOutput.Y, output.Y, FloatRoundingComparer); - Assert.Equal(expectedOutput.Z, output.Z, FloatRoundingComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.5, 0.5, 0.5, 0.507233, 0.500000, 0.378943)] - public void Adapt_CieXyz_D65_To_D50_XyzScaling(float x1, float y1, float z1, float x2, float y2, float z2) - { - // Arrange - var input = new CieXyz(x1, y1, z1); - var expectedOutput = new CieXyz(x2, y2, z2); - var converter = new ColorSpaceConverter - { - ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling), - WhitePoint = Illuminants.D50 - }; - - // Action - CieXyz output = converter.Adapt(input, Illuminants.D65); - - // Assert - Assert.Equal(expectedOutput.X, output.X, FloatRoundingComparer); - Assert.Equal(expectedOutput.Y, output.Y, FloatRoundingComparer); - Assert.Equal(expectedOutput.Z, output.Z, FloatRoundingComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.5, 0.5, 0.5, 0.507233, 0.500000, 0.378943)] - public void Adapt_Xyz_D65_To_D50_XyzScaling(float x1, float y1, float z1, float x2, float y2, float z2) - { - // Arrange - var input = new CieXyz(x1, y1, z1); - var expectedOutput = new CieXyz(x2, y2, z2); - var converter = new ColorSpaceConverter - { - ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling), - WhitePoint = Illuminants.D50 - }; - - // Action - CieXyz output = converter.Adapt(input, Illuminants.D65); - - // Assert - Assert.Equal(expectedOutput.X, output.X, FloatRoundingComparer); - Assert.Equal(expectedOutput.Y, output.Y, FloatRoundingComparer); - Assert.Equal(expectedOutput.Z, output.Z, FloatRoundingComparer); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs b/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs deleted file mode 100644 index 759d0f12f6..0000000000 --- a/tests/ImageSharp.Tests/Colorspaces/ColorSpaceEqualityTests.cs +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; -using Xunit; - -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Tests.Colorspaces -{ - /// - /// Test implementations of IEquatable and IAlmostEquatable in our colorspaces - /// - public class ColorSpaceEqualityTests - { - internal static readonly Dictionary EmptyDataLookup = - new Dictionary - { - {nameof( CieLab), default(CieLab) }, - {nameof( CieLch), default(CieLch) }, - {nameof( CieLchuv), default(CieLchuv) }, - {nameof( CieLuv), default(CieLuv) }, - {nameof( CieXyz), default(CieXyz) }, - {nameof( CieXyy), default(CieXyy) }, - {nameof( Hsl), default(Hsl) }, - {nameof( HunterLab), default(HunterLab) }, - {nameof( Lms), default(Lms) }, - {nameof( LinearRgb), default(LinearRgb) }, - {nameof( Rgb), default(Rgb) }, - {nameof( YCbCr), default(YCbCr) } - }; - - public static readonly IEnumerable EmptyData = EmptyDataLookup.Select(x => new [] { x.Key }); - - public static readonly TheoryData EqualityData = - new TheoryData - { - { new CieLab(Vector3.One), new CieLab(Vector3.One), typeof(CieLab) }, - { new CieLch(Vector3.One), new CieLch(Vector3.One), typeof(CieLch) }, - { new CieLchuv(Vector3.One), new CieLchuv(Vector3.One), typeof(CieLchuv) }, - { new CieLuv(Vector3.One), new CieLuv(Vector3.One), typeof(CieLuv) }, - { new CieXyz(Vector3.One), new CieXyz(Vector3.One), typeof(CieXyz) }, - { new CieXyy(Vector3.One), new CieXyy(Vector3.One), typeof(CieXyy) }, - { new HunterLab(Vector3.One), new HunterLab(Vector3.One), typeof(HunterLab) }, - { new Lms(Vector3.One), new Lms(Vector3.One), typeof(Lms) }, - { new LinearRgb(Vector3.One), new LinearRgb(Vector3.One), typeof(LinearRgb) }, - { new Rgb(Vector3.One), new Rgb(Vector3.One), typeof(Rgb) }, - { new Hsl(Vector3.One), new Hsl(Vector3.One), typeof(Hsl) }, - { new Hsv(Vector3.One), new Hsv(Vector3.One), typeof(Hsv) }, - { new YCbCr(Vector3.One), new YCbCr(Vector3.One), typeof(YCbCr) }, - }; - - public static readonly TheoryData NotEqualityDataNulls = - new TheoryData - { - // Valid object against null - { new CieLab(Vector3.One), null, typeof(CieLab) }, - { new CieLch(Vector3.One), null, typeof(CieLch) }, - { new CieLchuv(Vector3.One), null, typeof(CieLchuv) }, - { new CieLuv(Vector3.One), null, typeof(CieLuv) }, - { new CieXyz(Vector3.One), null, typeof(CieXyz) }, - { new CieXyy(Vector3.One), null, typeof(CieXyy) }, - { new HunterLab(Vector3.One), null, typeof(HunterLab) }, - { new Lms(Vector3.One), null, typeof(Lms) }, - { new LinearRgb(Vector3.One), null, typeof(LinearRgb) }, - { new Rgb(Vector3.One), null, typeof(Rgb) }, - { new Hsl(Vector3.One), null, typeof(Hsl) }, - { new Hsv(Vector3.One), null, typeof(Hsv) }, - { new YCbCr(Vector3.One), null, typeof(YCbCr) }, - }; - - public static readonly TheoryData NotEqualityDataDifferentObjects = - new TheoryData - { - // Valid objects of different types but not equal - { new CieLab(Vector3.One), new CieLch(Vector3.Zero), null }, - { new CieLuv(Vector3.One), new CieLchuv(Vector3.Zero), null }, - { new CieXyz(Vector3.One), new HunterLab(Vector3.Zero), null }, - { new Rgb(Vector3.One), new LinearRgb(Vector3.Zero), null }, - { new Rgb(Vector3.One), new Lms(Vector3.Zero), null }, - { new Cmyk(Vector4.One), new Hsl(Vector3.Zero), null }, - { new YCbCr(Vector3.One), new CieXyy(Vector3.Zero), null }, - { new Hsv(Vector3.One), new Hsl(Vector3.Zero), null }, - }; - - public static readonly TheoryData NotEqualityData = - new TheoryData - { - // Valid objects of the same type but not equal - { new CieLab(Vector3.One), new CieLab(Vector3.Zero), typeof(CieLab) }, - { new CieLch(Vector3.One), new CieLch(Vector3.Zero), typeof(CieLch) }, - { new CieLchuv(Vector3.One), new CieLchuv(Vector3.Zero), typeof(CieLchuv) }, - { new CieLuv(Vector3.One), new CieLuv(Vector3.Zero), typeof(CieLuv) }, - { new CieXyz(Vector3.One), new CieXyz(Vector3.Zero), typeof(CieXyz) }, - { new CieXyy(Vector3.One), new CieXyy(Vector3.Zero), typeof(CieXyy) }, - { new HunterLab(Vector3.One), new HunterLab(Vector3.Zero), typeof(HunterLab) }, - { new Lms(Vector3.One), new Lms(Vector3.Zero), typeof(Lms) }, - { new LinearRgb(Vector3.One), new LinearRgb(Vector3.Zero), typeof(LinearRgb) }, - { new Rgb(Vector3.One), new Rgb(Vector3.Zero), typeof(Rgb) }, - { new Cmyk(Vector4.One), new Cmyk(Vector4.Zero), typeof(Cmyk) }, - { new Hsl(Vector3.One), new Hsl(Vector3.Zero), typeof(Hsl) }, - { new Hsv(Vector3.One), new Hsv(Vector3.Zero), typeof(Hsv) }, - { new YCbCr(Vector3.One), new YCbCr(Vector3.Zero), typeof(YCbCr) }, - }; - - public static readonly TheoryData AlmostEqualsData = - new TheoryData - { - { new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, 0F), typeof(CieLab), 0F }, - { new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, 0F), typeof(CieLab), .001F }, - { new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, 0F), typeof(CieLab), .0001F }, - { new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, 0F), typeof(CieLab), .0005F }, - { new CieLab(0F, 0F, 0F), new CieLab(0F, .001F, 0F), typeof(CieLab), .001F }, - { new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, .0001F), typeof(CieLab), .0001F }, - { new CieLab(0F, 0F, 0F), new CieLab(.0005F, 0F, 0F), typeof(CieLab), .0005F }, - { new CieLch(0F, 0F, 0F), new CieLch(0F, .001F, 0F), typeof(CieLch), .001F }, - { new CieLchuv(0F, 0F, 0F), new CieLchuv(0F, .001F, 0F), typeof(CieLchuv), .001F }, - { new CieLuv(0F, 0F, 0F), new CieLuv(0F, .001F, 0F), typeof(CieLuv), .001F }, - { new CieXyz(380F, 380F, 380F), new CieXyz(380F, 380F, 380F), typeof(CieXyz), 0F }, - { new CieXyz(380F, 380F, 380F), new CieXyz(380.001F, 380F, 380F), typeof(CieXyz), .01F }, - { new CieXyz(380F, 380F, 380F), new CieXyz(380F, 380.001F, 380F), typeof(CieXyz), .01F }, - { new CieXyz(380F, 380F, 380F), new CieXyz(380F, 380F, 380.001F), typeof(CieXyz), .01F }, - { new Cmyk(1, 1, 1, 1), new Cmyk(1, 1, 1, .99F), typeof(Cmyk), .01F }, - { new YCbCr(255F, 128F, 128F), new YCbCr(255F, 128F, 128.001F), typeof(YCbCr), .01F }, - { new Hsv(0F, 0F, 0F), new Hsv(0F, 0F, 0F), typeof(Hsv), 0F }, - { new Hsl(0F, 0F, 0F), new Hsl(0F, 0F, 0F), typeof(Hsl), 0F }, - }; - - public static readonly TheoryData AlmostNotEqualsData = - new TheoryData - { - { new CieLab(0F, 0F, 0F), new CieLab(0.1F, 0F, 0F), typeof(CieLab), .001F }, - { new CieLab(0F, 0F, 0F), new CieLab(0F, 0.1F, 0F), typeof(CieLab), .001F }, - { new CieLab(0F, 0F, 0F), new CieLab(0F, 0F, 0.1F), typeof(CieLab), .001F }, - { new CieXyz(380F, 380F, 380F), new CieXyz(380.1F, 380F, 380F), typeof(CieXyz), .001F }, - { new CieXyz(380F, 380F, 380F), new CieXyz(380F, 380.1F, 380F), typeof(CieXyz), .001F }, - { new CieXyz(380F, 380F, 380F), new CieXyz(380F, 380F, 380.1F), typeof(CieXyz), .001F }, - }; - - [Theory] - [MemberData(nameof(EmptyData))] - public void Vector_Equals_WhenTrue(string color) - { - IColorVector colorVector = EmptyDataLookup[color]; - // Act - bool equal = colorVector.Vector.Equals(Vector3.Zero); - - // Assert - Assert.True(equal); - } - - [Theory] - [MemberData(nameof(EqualityData))] - public void Equals_WhenTrue(object first, object second, Type type) - { - // Act - bool equal = first.Equals(second); - - // Assert - Assert.True(equal); - } - - [Theory] - [MemberData(nameof(NotEqualityDataNulls))] - [MemberData(nameof(NotEqualityDataDifferentObjects))] - [MemberData(nameof(NotEqualityData))] - public void Equals_WhenFalse(object first, object second, Type type) - { - // Act - bool equal = first.Equals(second); - - // Assert - Assert.False(equal); - } - - [Theory] - [MemberData(nameof(EqualityData))] - public void GetHashCode_WhenEqual(object first, object second, Type type) - { - // Act - bool equal = first.GetHashCode() == second.GetHashCode(); - - // Assert - Assert.True(equal); - } - - [Theory] - [MemberData(nameof(NotEqualityDataDifferentObjects))] - public void GetHashCode_WhenNotEqual(object first, object second, Type type) - { - // Act - bool equal = first.GetHashCode() == second.GetHashCode(); - - // Assert - Assert.False(equal); - } - - [Theory] - [MemberData(nameof(EqualityData))] - public void GenericEquals_WhenTrue(object first, object second, Type type) - { - // Arrange - // Cast to the known object types, this is so that we can hit the - // equality operator on the concrete type, otherwise it goes to the - // default "object" one :) - dynamic firstObject = Convert.ChangeType(first, type); - dynamic secondObject = Convert.ChangeType(second, type); - - // Act - dynamic equal = firstObject.Equals(secondObject); - - // Assert - Assert.True(equal); - } - - [Theory] - [MemberData(nameof(NotEqualityData))] - public void GenericEquals_WhenFalse(object first, object second, Type type) - { - // Arrange - // Cast to the known object types, this is so that we can hit the - // equality operator on the concrete type, otherwise it goes to the - // default "object" one :) - dynamic firstObject = Convert.ChangeType(first, type); - dynamic secondObject = Convert.ChangeType(second, type); - - // Act - dynamic equal = firstObject.Equals(secondObject); - - // Assert - Assert.False(equal); - } - - // TODO:Disabled due to RuntypeBinder errors while structs are internal - //[Theory] - //[MemberData(nameof(EqualityData))] - //public void EqualityOperator(object first, object second, Type type) - //{ - // // Arrange - // // Cast to the known object types, this is so that we can hit the - // // equality operator on the concrete type, otherwise it goes to the - // // default "object" one :) - // dynamic firstObject = Convert.ChangeType(first, type); - // dynamic secondObject = Convert.ChangeType(second, type); - - // // Act - // dynamic equal = firstObject == secondObject; - - // // Assert - // Assert.True(equal); - //} - - [Theory] - [MemberData(nameof(NotEqualityData))] - public void Operator_WhenTrue(object first, object second, Type type) - { - // Arrange - // Cast to the known object types, this is so that we can hit the - // equality operator on the concrete type, otherwise it goes to the - // default "object" one :) - dynamic firstObject = Convert.ChangeType(first, type); - dynamic secondObject = Convert.ChangeType(second, type); - - // Act - dynamic notEqual = firstObject != secondObject; - - // Assert - Assert.True(notEqual); - } - - // TODO:Disabled due to RuntypeBinder errors while structs are internal - //[Theory] - //[MemberData(nameof(AlmostEqualsData))] - //public void AlmostEquals(object first, object second, Type type, float precision) - //{ - // // Arrange - // // Cast to the known object types, this is so that we can hit the - // // equality operator on the concrete type, otherwise it goes to the - // // default "object" one :) - // dynamic firstObject = Convert.ChangeType(first, type); - // dynamic secondObject = Convert.ChangeType(second, type); - - // // Act - // dynamic almostEqual = firstObject.AlmostEquals(secondObject, precision); - - // // Assert - // Assert.True(almostEqual); - //} - - // TODO:Disabled due to RuntypeBinder errors while structs are internal - //[Theory] - //[MemberData(nameof(AlmostNotEqualsData))] - //public void AlmostNotEquals(object first, object second, Type type, float precision) - //{ - // // Arrange - // // Cast to the known object types, this is so that we can hit the - // // equality operator on the concrete type, otherwise it goes to the - // // default "object" one :) - // dynamic firstObject = Convert.ChangeType(first, type); - // dynamic secondObject = Convert.ChangeType(second, type); - - // // Act - // dynamic almostEqual = firstObject.AlmostEquals(secondObject, precision); - - // // Assert - // Assert.False(almostEqual); - //} - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs b/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs new file mode 100644 index 0000000000..91cacfe3f0 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs @@ -0,0 +1,103 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Linq; +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces.Companding; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Companding +{ + /// + /// Tests various companding algorithms. Numbers are hand calculated from formulas online. + /// TODO: Oddly the formula for converting to/from Rec 2020 and 709 from Wikipedia seems to cause the value to + /// fail a round trip. They're large spaces so this is a surprise. More reading required!! + /// + public class CompandingTests + { + private static readonly ApproximateFloatComparer FloatComparer = new ApproximateFloatComparer(.00001F); + + [Fact] + public void Rec2020Companding_IsCorrect() + { + const float input = .667F; + float e = Rec2020Companding.Expand(input); + float c = Rec2020Companding.Compress(e); + CompandingIsCorrectImpl(e, c, .4484759F, .3937096F); + } + + [Fact] + public void Rec709Companding_IsCorrect() + { + const float input = .667F; + float e = Rec709Companding.Expand(input); + float c = Rec709Companding.Compress(e); + CompandingIsCorrectImpl(e, c, .4483577F, .3937451F); + } + + [Fact] + public void SRgbCompanding_IsCorrect() + { + const float input = .667F; + float e = SRgbCompanding.Expand(input); + float c = SRgbCompanding.Compress(e); + CompandingIsCorrectImpl(e, c, .40242353F, .667F); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(30)] + public void SRgbCompanding_Expand_VectorSpan(int length) + { + var rnd = new Random(42); + Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); + Vector4[] expected = source.Select(v => SRgbCompanding.Expand(v)).ToArray(); + + SRgbCompanding.Expand(source); + + Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(30)] + public void SRgbCompanding_Compress_VectorSpan(int length) + { + var rnd = new Random(42); + Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); + Vector4[] expected = source.Select(v => SRgbCompanding.Compress(v)).ToArray(); + + SRgbCompanding.Compress(source); + + Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); + } + + [Fact] + public void GammaCompanding_IsCorrect() + { + const float gamma = 2.2F; + const float input = .667F; + float e = GammaCompanding.Expand(input, gamma); + float c = GammaCompanding.Compress(e, gamma); + CompandingIsCorrectImpl(e, c, .41027668F, .667F); + } + + [Fact] + public void LCompanding_IsCorrect() + { + const float input = .667F; + float e = LCompanding.Expand(input); + float c = LCompanding.Compress(e); + CompandingIsCorrectImpl(e, c, .36236193F, .58908917F); + } + + private static void CompandingIsCorrectImpl(float e, float c, float expanded, float compressed) + { + Assert.Equal(expanded, e, FloatComparer); + Assert.Equal(compressed, c, FloatComparer); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs new file mode 100644 index 0000000000..57da2ff170 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs @@ -0,0 +1,240 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Allows the approximate comparison of colorspace component values. + /// + internal readonly struct ApproximateColorSpaceComparer : + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer + { + private readonly float Epsilon; + + /// + /// Initializes a new instance of the class. + /// + /// The comparison error difference epsilon to use. + public ApproximateColorSpaceComparer(float epsilon = 1F) => this.Epsilon = epsilon; + + /// + public bool Equals(Rgb x, Rgb y) + { + return this.Equals(x.R, y.R) + && this.Equals(x.G, y.G) + && this.Equals(x.B, y.B); + } + + /// + public int GetHashCode(Rgb obj) => obj.GetHashCode(); + + /// + public bool Equals(LinearRgb x, LinearRgb y) + { + return this.Equals(x.R, y.R) + && this.Equals(x.G, y.G) + && this.Equals(x.B, y.B); + } + + /// + public int GetHashCode(LinearRgb obj) => obj.GetHashCode(); + + /// + public bool Equals(CieLab x, CieLab y) + { + return this.Equals(x.L, y.L) + && this.Equals(x.A, y.A) + && this.Equals(x.B, y.B); + } + + /// + public int GetHashCode(CieLab obj) => obj.GetHashCode(); + + /// + public bool Equals(CieLch x, CieLch y) + { + return this.Equals(x.L, y.L) + && this.Equals(x.C, y.C) + && this.Equals(x.H, y.H); + } + + /// + public int GetHashCode(CieLch obj) => obj.GetHashCode(); + + /// + public bool Equals(CieLchuv x, CieLchuv y) + { + return this.Equals(x.L, y.L) + && this.Equals(x.C, y.C) + && this.Equals(x.H, y.H); + } + + /// + public int GetHashCode(CieLchuv obj) => obj.GetHashCode(); + + /// + public bool Equals(CieLuv x, CieLuv y) + { + return this.Equals(x.L, y.L) + && this.Equals(x.U, y.U) + && this.Equals(x.V, y.V); + } + + /// + public int GetHashCode(CieLuv obj) => obj.GetHashCode(); + + /// + public bool Equals(CieXyz x, CieXyz y) + { + return this.Equals(x.X, y.X) + && this.Equals(x.Y, y.Y) + && this.Equals(x.Z, y.Z); + } + + /// + public int GetHashCode(CieXyz obj) => obj.GetHashCode(); + + /// + public bool Equals(CieXyy x, CieXyy y) + { + return this.Equals(x.X, y.X) + && this.Equals(x.Y, y.Y) + && this.Equals(x.Yl, y.Yl); + } + + /// + public int GetHashCode(CieXyy obj) => obj.GetHashCode(); + + /// + public bool Equals(Cmyk x, Cmyk y) + { + return this.Equals(x.C, y.C) + && this.Equals(x.M, y.M) + && this.Equals(x.Y, y.Y) + && this.Equals(x.K, y.K); + } + + /// + public int GetHashCode(Cmyk obj) => obj.GetHashCode(); + + /// + public bool Equals(HunterLab x, HunterLab y) + { + return this.Equals(x.L, y.L) + && this.Equals(x.A, y.A) + && this.Equals(x.B, y.B); + } + + /// + public int GetHashCode(HunterLab obj) => obj.GetHashCode(); + + /// + public bool Equals(Hsl x, Hsl y) + { + return this.Equals(x.H, y.H) + && this.Equals(x.S, y.S) + && this.Equals(x.L, y.L); + } + + /// + public int GetHashCode(Hsl obj) => obj.GetHashCode(); + + /// + public bool Equals(Hsv x, Hsv y) + { + return this.Equals(x.H, y.H) + && this.Equals(x.S, y.S) + && this.Equals(x.V, y.V); + } + + /// + public int GetHashCode(Hsv obj) => obj.GetHashCode(); + + /// + public bool Equals(Lms x, Lms y) + { + return this.Equals(x.L, y.L) + && this.Equals(x.M, y.M) + && this.Equals(x.S, y.S); + } + + /// + public int GetHashCode(Lms obj) => obj.GetHashCode(); + + /// + public bool Equals(YCbCr x, YCbCr y) + { + return this.Equals(x.Y, y.Y) + && this.Equals(x.Cb, y.Cb) + && this.Equals(x.Cr, y.Cr); + } + + /// + public int GetHashCode(YCbCr obj) => obj.GetHashCode(); + + /// + public bool Equals(CieXyChromaticityCoordinates x, CieXyChromaticityCoordinates y) => this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y); + + /// + public int GetHashCode(CieXyChromaticityCoordinates obj) => obj.GetHashCode(); + + /// + public bool Equals(RgbPrimariesChromaticityCoordinates x, RgbPrimariesChromaticityCoordinates y) => this.Equals(x.R, y.R) && this.Equals(x.G, y.G) && this.Equals(x.B, y.B); + + /// + public int GetHashCode(RgbPrimariesChromaticityCoordinates obj) => obj.GetHashCode(); + + /// + public bool Equals(GammaWorkingSpace x, GammaWorkingSpace y) + { + if (x is GammaWorkingSpace g1 && y is GammaWorkingSpace g2) + { + return this.Equals(g1.Gamma, g2.Gamma) + && this.Equals(g1.WhitePoint, g2.WhitePoint) + && this.Equals(g1.ChromaticityCoordinates, g2.ChromaticityCoordinates); + } + + return false; + } + + /// + public int GetHashCode(GammaWorkingSpace obj) => obj.GetHashCode(); + + /// + public bool Equals(RgbWorkingSpaceBase x, RgbWorkingSpaceBase y) + { + return this.Equals(x.WhitePoint, y.WhitePoint) + && this.Equals(x.ChromaticityCoordinates, y.ChromaticityCoordinates); + } + + /// + public int GetHashCode(RgbWorkingSpaceBase obj) => obj.GetHashCode(); + + private bool Equals(float x, float y) + { + float d = x - y; + return d >= -this.Epsilon && d <= this.Epsilon; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLabAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchConversionTests.cs similarity index 61% rename from tests/ImageSharp.Tests/Colorspaces/CieLabAndCieLchConversionTests.cs rename to tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchConversionTests.cs index 299b9e9e5b..38c0c21bc9 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieLabAndCieLchConversionTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchConversionTests.cs @@ -1,12 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; +using System; using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; using Xunit; -namespace SixLabors.ImageSharp.Tests.Colorspaces +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion { /// /// Tests - conversions. @@ -17,8 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces /// public class CieLabAndCieLchConversionTests { - private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(4); - + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); /// @@ -37,14 +36,24 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { // Arrange var input = new CieLch(l, c, h); + var expected = new CieLab(l2, a, b); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; // Act - var output = Converter.ToCieLab(input); + var actual = Converter.ToCieLab(input); + Converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(l2, output.L, FloatRoundingComparer); - Assert.Equal(a, output.A, FloatRoundingComparer); - Assert.Equal(b, output.B, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } /// @@ -59,18 +68,28 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces [InlineData(10, -20, 30, 10, 36.0555, 123.6901)] [InlineData(10, 20, -30, 10, 36.0555, 303.6901)] [InlineData(10, -20, -30, 10, 36.0555, 236.3099)] - public void Convert_Lab_to_LCHab(float l, float a, float b, float l2, float c, float h) + public void Convert_Lab_to_Lch(float l, float a, float b, float l2, float c, float h) { // Arrange var input = new CieLab(l, a, b); + var expected = new CieLch(l2, c, h); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; // Act - var output = Converter.ToCieLch(input); + var actual = Converter.ToCieLch(input); + Converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(l2, output.L, FloatRoundingComparer); - Assert.Equal(c, output.C, FloatRoundingComparer); - Assert.Equal(h, output.H, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchuvConversionTests.cs new file mode 100644 index 0000000000..96628977fe --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchuvConversionTests.cs @@ -0,0 +1,83 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + /// + /// Test data generated using: + /// + /// + public class CieLabAndCieLchuvConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(30.66194, 200, 352.7564, 31.95653, 116.8745, 2.388602)] + public void Convert_Lchuv_to_Lab(float l, float c, float h, float l2, float a, float b) + { + // Arrange + var input = new CieLchuv(l, c, h); + var expected = new CieLab(l2, a, b); + + Span inputSpan = new CieLchuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + var actual = Converter.ToCieLab(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 303.6901, 10.01514, 30.66194, 200, 352.7564)] + public void Convert_Lab_to_Lchuv(float l, float a, float b, float l2, float c, float h) + { + // Arrange + var input = new CieLab(l, a, b); + var expected = new CieLchuv(l2, c, h); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLchuv[5]; + + // Act + var actual = Converter.ToCieLchuv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLuvConversionTests.cs new file mode 100644 index 0000000000..39011bb292 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLuvConversionTests.cs @@ -0,0 +1,83 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + /// + /// Test data generated using: + /// + /// + public class CieLabAndCieLuvConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(10, 36.0555, 303.6901, 10.0151367, -23.9644356, 17.0226)] + public void Convert_CieLuv_to_CieLab(float l, float u, float v, float l2, float a, float b) + { + // Arrange + var input = new CieLuv(l, u, v); + var expected = new CieLab(l2, a, b); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + var actual = Converter.ToCieLab(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(10.0151367, -23.9644356, 17.0226, 10.0000038, -12.830183, 15.1829338)] + public void Convert_CieLab_to_CieLuv(float l, float a, float b, float l2, float u, float v) + { + // Arrange + var input = new CieLab(l, a, b); + var expected = new CieLuv(l2, u, v); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + var actual = Converter.ToCieLuv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieXyyConversionTests.cs new file mode 100644 index 0000000000..f7dc365b81 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieXyyConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLabAndCieXyyConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.8644734, 0.06098868, 0.06509002, 36.05552, 275.6228, 10.01517)] + public void Convert_CieXyy_to_CieLab(float x, float y, float yl, float l, float a, float b) + { + // Arrange + var input = new CieXyy(x, y, yl); + var expected = new CieLab(l, a, b); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + var actual = Converter.ToCieLab(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 303.6901, 10.01514, 0.8644734, 0.06098868, 0.06509002)] + public void Convert_CieLab_to_CieXyy(float l, float a, float b, float x, float y, float yl) + { + // Arrange + var input = new CieLab(l, a, b); + var expected = new CieXyy(x, y, yl); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + var actual = Converter.ToCieXyy(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCmykConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCmykConversionTests.cs new file mode 100644 index 0000000000..43300ab88c --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCmykConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLabAndCmykConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 1, 0, 0, 0)] + [InlineData(0, 1, 0.6156551, 5.960464E-08, 55.063, 82.54871, 23.16506)] + public void Convert_Cmyk_to_CieLab(float c, float m, float y, float k, float l, float a, float b) + { + // Arrange + var input = new Cmyk(c, m, y, k); + var expected = new CieLab(l, a, b); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + var actual = Converter.ToCieLab(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0, 1)] + [InlineData(36.0555, 303.6901, 10.01514, 0, 1, 0.6156551, 5.960464E-08)] + public void Convert_CieLab_to_Cmyk(float l, float a, float b, float c, float m, float y, float k) + { + // Arrange + var input = new CieLab(l, a, b); + var expected = new Cmyk(c, m, y, k); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + var actual = Converter.ToCmyk(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHslConversionTests.cs new file mode 100644 index 0000000000..4ab309fe14 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHslConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLabAndHslConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(336.9393, 1, 0.5, 55.063, 82.54868, 23.16508)] + public void Convert_Hsl_to_CieLab(float h, float s, float ll, float l, float a, float b) + { + // Arrange + var input = new Hsl(h, s, ll); + var expected = new CieLab(l, a, b); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + var actual = Converter.ToCieLab(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 303.6901, 10.01514, 336.9393, 1, 0.5)] + public void Convert_CieLab_to_Hsl(float l, float a, float b, float h, float s, float ll) + { + // Arrange + var input = new CieLab(l, a, b); + var expected = new Hsl(h, s, ll); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; + + // Act + var actual = Converter.ToHsl(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHsvConversionTests.cs new file mode 100644 index 0000000000..e7ff34f494 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHsvConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLabAndHsvConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(336.9393, 1, 0.9999999, 55.063, 82.54871, 23.16504)] + public void Convert_Hsv_to_CieLab(float h, float s, float v, float l, float a, float b) + { + // Arrange + var input = new Hsv(h, s, v); + var expected = new CieLab(l, a, b); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + var actual = Converter.ToCieLab(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 303.6901, 10.01514, 336.9393, 1, 0.9999999)] + public void Convert_CieLab_to_Hsv(float l, float a, float b, float h, float s, float v) + { + // Arrange + var input = new CieLab(l, a, b); + var expected = new Hsv(h, s, v); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; + + // Act + var actual = Converter.ToHsv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHunterLabConversionTests.cs new file mode 100644 index 0000000000..844cda4760 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHunterLabConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLabAndHunterLabConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(27.51646, 556.9392, -0.03974226, 36.05554, 275.6227, 10.01519)] + public void Convert_HunterLab_to_CieLab(float l2, float a2, float b2, float l, float a, float b) + { + // Arrange + var input = new HunterLab(l2, a2, b2); + var expected = new CieLab(l, a, b); + + Span inputSpan = new HunterLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + var actual = Converter.ToCieLab(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 303.6901, 10.01514, 27.51646, 556.9392, -0.03974226)] + public void Convert_CieLab_to_HunterLab(float l, float a, float b, float l2, float a2, float b2) + { + // Arrange + var input = new CieLab(l, a, b); + var expected = new HunterLab(l2, a2, b2); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new HunterLab[5]; + + // Act + var actual = Converter.ToHunterLab(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLinearRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLinearRgbConversionTests.cs new file mode 100644 index 0000000000..74ed180f38 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLinearRgbConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLabAndLinearRgbConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(1, 0, 0.1221596, 55.063, 82.54871, 23.16505)] + public void Convert_LinearRgb_to_CieLab(float r, float g, float b2, float l, float a, float b) + { + // Arrange + var input = new LinearRgb(r, g, b2); + var expected = new CieLab(l, a, b); + + Span inputSpan = new LinearRgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + var actual = Converter.ToCieLab(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 303.6901, 10.01514, 1, 0, 0.1221596)] + public void Convert_CieLab_to_LinearRgb(float l, float a, float b, float r, float g, float b2) + { + // Arrange + var input = new CieLab(l, a, b); + var expected = new LinearRgb(r, g, b2); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new LinearRgb[5]; + + // Act + var actual = Converter.ToLinearRgb(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLmsConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLmsConversionTests.cs new file mode 100644 index 0000000000..a3db00e804 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLmsConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLabAndLmsConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.8303261, -0.5776886, 0.1133359, 36.05553, 275.6228, 10.01518)] + public void Convert_Lms_to_CieLab(float l2, float m, float s, float l, float a, float b) + { + // Arrange + var input = new Lms(l2, m, s); + var expected = new CieLab(l, a, b); + + Span inputSpan = new Lms[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + var actual = Converter.ToCieLab(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 303.6901, 10.01514, 0.8303261, -0.5776886, 0.1133359)] + public void Convert_CieLab_to_Lms(float l, float a, float b, float l2, float m, float s) + { + // Arrange + var input = new CieLab(l, a, b); + var expected = new Lms(l2, m, s); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new Lms[5]; + + // Act + var actual = Converter.ToLms(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndRgbConversionTests.cs new file mode 100644 index 0000000000..fc202ccc96 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndRgbConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLabAndRgbConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.9999999, 0, 0.384345, 55.063, 82.54871, 23.16505)] + public void Convert_Rgb_to_CieLab(float r, float g, float b2, float l, float a, float b) + { + // Arrange + var input = new Rgb(r, g, b2); + var expected = new CieLab(l, a, b); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + var actual = Converter.ToCieLab(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 303.6901, 10.01514, 0.9999999, 0, 0.384345)] + public void Convert_CieLab_to_Rgb(float l, float a, float b, float r, float g, float b2) + { + // Arrange + var input = new CieLab(l, a, b); + var expected = new Rgb(r, g, b2); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + var actual = Converter.ToRgb(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndYCbCrConversionTests.cs new file mode 100644 index 0000000000..3e481d4f64 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndYCbCrConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLabAndYCbCrConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 128, 128, 0, 0, 0)] + [InlineData(87.4179, 133.9763, 247.5308, 55.06287, 82.54838, 23.1697)] + public void Convert_YCbCr_to_CieLab(float y, float cb, float cr, float l, float a, float b) + { + // Arrange + var input = new YCbCr(y, cb, cr); + var expected = new CieLab(l, a, b); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + var actual = Converter.ToCieLab(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 128, 128)] + [InlineData(36.0555, 303.6901, 10.01514, 87.4179, 133.9763, 247.5308)] + public void Convert_CieLab_to_YCbCr(float l, float a, float b, float y, float cb, float cr) + { + // Arrange + var input = new CieLab(l, a, b); + var expected = new YCbCr(y, cb, cr); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + var actual = Converter.ToYCbCr(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieLuvConversionTests.cs new file mode 100644 index 0000000000..078ba44daf --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieLuvConversionTests.cs @@ -0,0 +1,78 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLchAndCieLuvConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 103.6901, 10.01514, 34.89777, 187.6642, -7.181467)] + public void Convert_CieLch_to_CieLuv(float l, float c, float h, float l2, float u, float v) + { + // Arrange + var input = new CieLch(l, c, h); + var expected = new CieLuv(l2, u, v); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + var actual = Converter.ToCieLuv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(34.89777, 187.6642, -7.181467, 36.05552, 103.6901, 10.01514)] + public void Convert_CieLuv_to_CieLch(float l2, float u, float v, float l, float c, float h) + { + // Arrange + var input = new CieLuv(l2, u, v); + var expected = new CieLch(l, c, h); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + var actual = Converter.ToCieLch(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieXyyConversionTests.cs new file mode 100644 index 0000000000..a65f618835 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieXyyConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLchAndCieXyyConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 103.6901, 10.01514, 0.6529307, 0.2147411, 0.08447381)] + public void Convert_CieLch_to_CieXyy(float l, float c, float h, float x, float y, float yl) + { + // Arrange + var input = new CieLch(l, c, h); + var expected = new CieXyy(x, y, yl); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + var actual = Converter.ToCieXyy(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0.6529307, 0.2147411, 0.08447381, 36.05552, 103.6901, 10.01515)] + public void Convert_CieXyy_to_CieLch(float x, float y, float yl, float l, float c, float h) + { + // Arrange + var input = new CieXyy(x, y, yl); + var expected = new CieLch(l, c, h); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + var actual = Converter.ToCieLch(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHslConversionTests.cs new file mode 100644 index 0000000000..49990fb908 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHslConversionTests.cs @@ -0,0 +1,78 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLchAndHslConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 103.6901, 10.01514, 341.959, 1, 0.4207301)] + public void Convert_CieLch_to_Hsl(float l, float c, float h, float h2, float s, float l2) + { + // Arrange + var input = new CieLch(l, c, h); + var expected = new Hsl(h2, s, l2); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; + + // Act + var actual = Converter.ToHsl(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(341.959, 1, 0.4207301, 46.13444, 78.0637, 22.90503)] + public void Convert_Hsl_to_CieLch(float h2, float s, float l2, float l, float c, float h) + { + // Arrange + var input = new Hsl(h2, s, l2); + var expected = new CieLch(l, c, h); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + var actual = Converter.ToCieLch(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHsvConversionTests.cs new file mode 100644 index 0000000000..924b45b4a0 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHsvConversionTests.cs @@ -0,0 +1,78 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLchAndHsvConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 103.6901, 10.01514, 341.959, 1, 0.8414602)] + public void Convert_CieLch_to_Hsv(float l, float c, float h, float h2, float s, float v) + { + // Arrange + var input = new CieLch(l, c, h); + var expected = new Hsv(h2, s, v); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; + + // Act + var actual = Converter.ToHsv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(341.959, 1, 0.8414602, 46.13444, 78.0637, 22.90501)] + public void Convert_Hsv_to_CieLch(float h2, float s, float v, float l, float c, float h) + { + // Arrange + var input = new Hsv(h2, s, v); + var expected = new CieLch(l, c, h); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + var actual = Converter.ToCieLch(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHunterLabConversionTests.cs new file mode 100644 index 0000000000..0991657310 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHunterLabConversionTests.cs @@ -0,0 +1,78 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLchAndHunterLabConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 103.6901, 10.01514, 29.41358, 106.6302, 9.102425)] + public void Convert_CieLch_to_HunterLab(float l, float c, float h, float l2, float a, float b) + { + // Arrange + var input = new CieLch(l, c, h); + var expected = new HunterLab(l2, a, b); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new HunterLab[5]; + + // Act + var actual = Converter.ToHunterLab(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(29.41358, 106.6302, 9.102425, 36.05551, 103.6901, 10.01515)] + public void Convert_HunterLab_to_CieLch(float l2, float a, float b, float l, float c, float h) + { + // Arrange + var input = new HunterLab(l2, a, b); + var expected = new CieLch(l, c, h); + + Span inputSpan = new HunterLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + var actual = Converter.ToCieLch(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLinearRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLinearRgbConversionTests.cs new file mode 100644 index 0000000000..a7a819d1f7 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLinearRgbConversionTests.cs @@ -0,0 +1,78 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLchAndLinearRgbConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 103.6901, 10.01514, 0.6765013, 0, 0.05209038)] + public void Convert_CieLch_to_LinearRgb(float l, float c, float h, float r, float g, float b) + { + // Arrange + var input = new CieLch(l, c, h); + var expected = new LinearRgb(r, g, b); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new LinearRgb[5]; + + // Act + var actual = Converter.ToLinearRgb(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0.6765013, 0, 0.05209038, 46.13445, 78.06367, 22.90504)] + public void Convert_LinearRgb_to_CieLch(float r, float g, float b, float l, float c, float h) + { + // Arrange + var input = new LinearRgb(r, g, b); + var expected = new CieLch(l, c, h); + + Span inputSpan = new LinearRgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + var actual = Converter.ToCieLch(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLmsConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLmsConversionTests.cs new file mode 100644 index 0000000000..b83b861be8 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLmsConversionTests.cs @@ -0,0 +1,78 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLchAndLmsConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 103.6901, 10.01514, 0.2440057, -0.04603009, 0.05780027)] + public void Convert_CieLch_to_Lms(float l, float c, float h, float l2, float m, float s) + { + // Arrange + var input = new CieLch(l, c, h); + var expected = new Lms(l2, m, s); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new Lms[5]; + + // Act + var actual = Converter.ToLms(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0.2440057, -0.04603009, 0.05780027, 36.05552, 103.6901, 10.01515)] + public void Convert_Lms_to_CieLch(float l2, float m, float s, float l, float c, float h) + { + // Arrange + var input = new Lms(l2, m, s); + var expected = new CieLch(l, c, h); + + Span inputSpan = new Lms[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + var actual = Converter.ToCieLch(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndRgbConversionTests.cs new file mode 100644 index 0000000000..932fdc4105 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndRgbConversionTests.cs @@ -0,0 +1,78 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLchAndRgbConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 103.6901, 10.01514, 0.8414602, 0, 0.2530123)] + public void Convert_CieLch_to_Rgb(float l, float c, float h, float r, float g, float b) + { + // Arrange + var input = new CieLch(l, c, h); + var expected = new Rgb(r, g, b); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + var actual = Converter.ToRgb(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0.8414602, 0, 0.2530123, 46.13444, 78.0637, 22.90503)] + public void Convert_Rgb_to_CieLch(float r, float g, float b, float l, float c, float h) + { + // Arrange + var input = new Rgb(r, g, b); + var expected = new CieLch(l, c, h); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + var actual = Converter.ToCieLch(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndYCbCrConversionTests.cs new file mode 100644 index 0000000000..4d04418d99 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndYCbCrConversionTests.cs @@ -0,0 +1,78 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLchAndYCbCrConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 128, 128)] + [InlineData(36.0555, 103.6901, 10.01514, 71.5122, 124.053, 230.0401)] + public void Convert_CieLch_to_YCbCr(float l, float c, float h, float y, float cb, float cr) + { + // Arrange + var input = new CieLch(l, c, h); + var expected = new YCbCr(y, cb, cr); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + var actual = Converter.ToYCbCr(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(71.5122, 124.053, 230.0401, 46.23178, 78.1114, 22.7662)] + public void Convert_YCbCr_to_CieLch(float y, float cb, float cr, float l, float c, float h) + { + // Arrange + var input = new YCbCr(y, cb, cr); + var expected = new CieLch(l, c, h); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + var actual = Converter.ToCieLch(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLchConversionTests.cs new file mode 100644 index 0000000000..3cdaa42792 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLchConversionTests.cs @@ -0,0 +1,78 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLchuvAndCieLchConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.73742, 64.79149, 30.1786, 36.0555, 103.6901, 10.01513)] + public void Convert_CieLch_to_CieLchuv(float l2, float c2, float h2, float l, float c, float h) + { + // Arrange + var input = new CieLch(l2, c2, h2); + var expected = new CieLchuv(l, c, h); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLchuv[5]; + + // Act + var actual = Converter.ToCieLchuv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(36.0555, 103.6901, 10.01514, 36.73742, 64.79149, 30.1786)] + public void Convert_CieLchuv_to_CieLch(float l, float c, float h, float l2, float c2, float h2) + { + // Arrange + var input = new CieLchuv(l, c, h); + var expected = new CieLch(l2, c2, h2); + + Span inputSpan = new CieLchuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + var actual = Converter.ToCieLch(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLuvConversionTests.cs similarity index 57% rename from tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs rename to tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLuvConversionTests.cs index cbcddcfe50..6829c62b50 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieLuvAndCieLchuvConversionTests.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLuvConversionTests.cs @@ -1,12 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; +using System; using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; using Xunit; -namespace SixLabors.ImageSharp.Tests.Colorspaces +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion { /// /// Tests - conversions. @@ -15,10 +15,9 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces /// Test data generated using: /// /// - public class CieLuvAndCieLchuvuvConversionTests + public class CieLchuvAndCieLuvConversionTests { - private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(4); - + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); /// @@ -33,18 +32,28 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces [InlineData(10, 36.0555, 123.6901, 10, -20, 30)] [InlineData(10, 36.0555, 303.6901, 10, 20, -30)] [InlineData(10, 36.0555, 236.3099, 10, -20, -30)] - public void Convert_Lchuv_to_Luv(float l, float c, float h, float l2, float u, float v) + public void Convert_CieLchuv_to_CieLuv(float l, float c, float h, float l2, float u, float v) { // Arrange var input = new CieLchuv(l, c, h); + var expected = new CieLuv(l2, u, v); + + Span inputSpan = new CieLchuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; // Act - CieLuv output = Converter.ToCieLuv(input); + var actual = Converter.ToCieLuv(input); + Converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(l2, output.L, FloatRoundingComparer); - Assert.Equal(u, output.U, FloatRoundingComparer); - Assert.Equal(v, output.V, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } /// @@ -60,18 +69,29 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces [InlineData(10, 20, -30, 10, 36.0555, 303.6901)] [InlineData(10, -20, -30, 10, 36.0555, 236.3099)] [InlineData(37.3511, 24.1720, 16.0684, 37.3511, 29.0255, 33.6141)] - public void Convert_Luv_to_LCHuv(float l, float u, float v, float l2, float c, float h) + public void Convert_CieLuv_to_CieLchuv(float l, float u, float v, float l2, float c, float h) { // Arrange var input = new CieLuv(l, u, v); + var expected = new CieLchuv(l2, c, h); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLchuv[5]; // Act - CieLchuv output = Converter.ToCieLchuv(input); + var actual = Converter.ToCieLchuv(input); + Converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(l2, output.L, FloatRoundingComparer); - Assert.Equal(c, output.C, FloatRoundingComparer); - Assert.Equal(h, output.H, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCmykConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCmykConversionTests.cs new file mode 100644 index 0000000000..0c62ffcc31 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCmykConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLchuvAndCmykConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 1, 0, 0, 0)] + [InlineData(0, 0.8576171, 0.7693201, 0.3440427, 36.0555, 103.6901, 10.01514)] + public void Convert_Cmyk_to_CieLchuv(float c2, float m, float y, float k, float l, float c, float h) + { + // Arrange + var input = new Cmyk(c2, m, y, k); + var expected = new CieLchuv(l, c, h); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLchuv[5]; + + // Act + var actual = Converter.ToCieLchuv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0, 1)] + [InlineData(36.0555, 103.6901, 10.01514, 0, 0.8576171, 0.7693201, 0.3440427)] + public void Convert_CieLchuv_to_Cmyk(float l, float c, float h, float c2, float m, float y, float k) + { + // Arrange + var input = new CieLchuv(l, c, h); + var expected = new Cmyk(c2, m, y, k); + + Span inputSpan = new CieLchuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + var actual = Converter.ToCmyk(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndCieXyyConversionTests.cs new file mode 100644 index 0000000000..3b41204f7c --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndCieXyyConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLuvAndCieXyyConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 103.6901, 10.01514, 0.5646762, 0.2932749, 0.09037033)] + public void Convert_CieLuv_to_CieXyy(float l, float u, float v, float x, float y, float yl) + { + // Arrange + var input = new CieLuv(l, u, v); + var expected = new CieXyy(x, y, yl); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + var actual = Converter.ToCieXyy(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.5646762, 0.2932749, 0.09037033, 36.0555, 103.6901, 10.01514)] + public void Convert_CieXyy_to_CieLuv(float x, float y, float yl, float l, float u, float v) + { + // Arrange + var input = new CieXyy(x, y, yl); + var expected = new CieLuv(l, u, v); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + var actual = Converter.ToCieLuv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHslConversionTests.cs new file mode 100644 index 0000000000..bfc0d2ecf1 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHslConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLuvAndHslConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 93.6901, 10.01514, 347.3767, 0.7115612, 0.3765343)] + public void Convert_CieLuv_to_Hsl(float l, float u, float v, float h, float s, float l2) + { + // Arrange + var input = new CieLuv(l, u, v); + var expected = new Hsl(h, s, l2); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; + + // Act + var actual = Converter.ToHsl(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(347.3767, 0.7115612, 0.3765343, 36.0555, 93.69012, 10.01514)] + public void Convert_Hsl_to_CieLuv(float h, float s, float l2, float l, float u, float v) + { + // Arrange + var input = new Hsl(h, s, l2); + var expected = new CieLuv(l, u, v); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + var actual = Converter.ToCieLuv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHsvConversionTests.cs new file mode 100644 index 0000000000..f11b17fff3 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHsvConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLuvAndHsvConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 93.6901, 10.01514, 347.3767, 0.8314762, 0.6444615)] + public void Convert_CieLuv_to_Hsv(float l, float u, float v, float h, float s, float v2) + { + // Arrange + var input = new CieLuv(l, u, v); + var expected = new Hsv(h, s, v2); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; + + // Act + var actual = Converter.ToHsv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(347.3767, 0.8314762, 0.6444615, 36.0555, 93.69012, 10.01514)] + public void Convert_Hsv_to_CieLuv(float h, float s, float v2, float l, float u, float v) + { + // Arrange + var input = new Hsv(h, s, v2); + var expected = new CieLuv(l, u, v); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + var actual = Converter.ToCieLuv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHunterLabConversionTests.cs new file mode 100644 index 0000000000..de2329c2ec --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHunterLabConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLuvAndHunterLabConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 93.6901, 10.01514, 30.19531, 46.4312, 11.16259)] + public void Convert_CieLuv_to_HunterLab(float l, float u, float v, float l2, float a, float b) + { + // Arrange + var input = new CieLuv(l, u, v); + var expected = new HunterLab(l2, a, b); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new HunterLab[5]; + + // Act + var actual = Converter.ToHunterLab(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(30.19531, 46.4312, 11.16259, 36.0555, 93.6901, 10.01514)] + public void Convert_HunterLab_to_CieLuv(float l2, float a, float b, float l, float u, float v) + { + // Arrange + var input = new HunterLab(l2, a, b); + var expected = new CieLuv(l, u, v); + + Span inputSpan = new HunterLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + var actual = Converter.ToCieLuv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLinearRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLinearRgbConversionTests.cs new file mode 100644 index 0000000000..3a1bd10c41 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLinearRgbConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLuvAndLinearRgbConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 93.6901, 10.01514, 0.3729299, 0.01141088, 0.04014909)] + public void Convert_CieLuv_to_LinearRgb(float l, float u, float v, float r, float g, float b) + { + // Arrange + var input = new CieLuv(l, u, v); + var expected = new LinearRgb(r, g, b); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new LinearRgb[5]; + + // Act + var actual = Converter.ToLinearRgb(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.3729299, 0.01141088, 0.04014909, 36.0555, 93.6901, 10.01511)] + public void Convert_LinearRgb_to_CieLuv(float r, float g, float b, float l, float u, float v) + { + // Arrange + var input = new LinearRgb(r, g, b); + var expected = new CieLuv(l, u, v); + + Span inputSpan = new LinearRgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + var actual = Converter.ToCieLuv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLmsConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLmsConversionTests.cs new file mode 100644 index 0000000000..f3881f10f7 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLmsConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLuvAndLmsConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 93.6901, 10.01514, 0.164352, 0.03267485, 0.0483408)] + public void Convert_CieLuv_to_Lms(float l, float u, float v, float l2, float m, float s) + { + // Arrange + var input = new CieLuv(l, u, v); + var expected = new Lms(l2, m, s); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Lms[5]; + + // Act + var actual = Converter.ToLms(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.164352, 0.03267485, 0.0483408, 36.0555, 93.69009, 10.01514)] + public void Convert_Lms_to_CieLuv(float l2, float m, float s, float l, float u, float v) + { + // Arrange + var input = new Lms(l2, m, s); + var expected = new CieLuv(l, u, v); + + Span inputSpan = new Lms[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + var actual = Converter.ToCieLuv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndRgbConversionTests.cs new file mode 100644 index 0000000000..644f4577bf --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndRgbConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLuvAndRgbConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 93.6901, 10.01514, 0.6444615, 0.1086071, 0.2213444)] + public void Convert_CieLuv_to_Rgb(float l, float u, float v, float r, float g, float b) + { + // Arrange + var input = new CieLuv(l, u, v); + var expected = new Rgb(r, g, b); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + var actual = Converter.ToRgb(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.6444615, 0.1086071, 0.2213444, 36.0555, 93.69012, 10.01514)] + public void Convert_Rgb_to_CieLuv(float r, float g, float b, float l, float u, float v) + { + // Arrange + var input = new Rgb(r, g, b); + var expected = new CieLuv(l, u, v); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + var actual = Converter.ToCieLuv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndYCbCrConversionTests.cs new file mode 100644 index 0000000000..41b9dba091 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndYCbCrConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieLuvAndYCbCrConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 128, 128)] + [InlineData(36.0555, 93.6901, 10.01514, 71.8283, 119.3174, 193.9839)] + public void Convert_CieLuv_to_YCbCr(float l, float u, float v, float y, float cb, float cr) + { + // Arrange + var input = new CieLuv(l, u, v); + var expected = new YCbCr(y, cb, cr); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + var actual = Converter.ToYCbCr(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 128, 128, 0, 0, 0)] + [InlineData(71.8283, 119.3174, 193.9839, 36.00565, 93.44593, 10.2234)] + public void Convert_YCbCr_to_CieLuv(float y, float cb, float cr, float l, float u, float v) + { + // Arrange + var input = new YCbCr(y, cb, cr); + var expected = new CieLuv(l, u, v); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + var actual = Converter.ToCieLuv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHslConversionTests.cs new file mode 100644 index 0000000000..5b36beaab9 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHslConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieXyyAndHslConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.211263)] + public void Convert_CieXyy_to_Hsl(float x, float y, float yl, float h, float s, float l) + { + // Arrange + var input = new CieXyy(x, y, yl); + var expected = new Hsl(h, s, l); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; + + // Act + var actual = Converter.ToHsl(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(120, 1, 0.211263, 0.3, 0.6, 0.1067051)] + public void Convert_Hsl_to_CieXyy(float h, float s, float l, float x, float y, float yl) + { + // Arrange + var input = new Hsl(h, s, l); + var expected = new CieXyy(x, y, yl); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + var actual = Converter.ToCieXyy(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHsvConversionTests.cs new file mode 100644 index 0000000000..da77378759 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHsvConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieXyyAndHsvConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.4225259)] + public void Convert_CieXyy_to_Hsv(float x, float y, float yl, float h, float s, float v) + { + // Arrange + var input = new CieXyy(x, y, yl); + var expected = new Hsv(h, s, v); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; + + // Act + var actual = Converter.ToHsv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(120, 1, 0.4225259, 0.3, 0.6, 0.1067051)] + public void Convert_Hsv_to_CieXyy(float h, float s, float v, float x, float y, float yl) + { + // Arrange + var input = new Hsv(h, s, v); + var expected = new CieXyy(x, y, yl); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + var actual = Converter.ToCieXyy(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHunterLabConversionTests.cs new file mode 100644 index 0000000000..96d14c98a6 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHunterLabConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieXyyAndHunterLabConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 31.46263, -32.81796, 28.64938)] + public void Convert_CieXyy_to_HunterLab(float x, float y, float yl, float l, float a, float b) + { + // Arrange + var input = new CieXyy(x, y, yl); + var expected = new HunterLab(l, a, b); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new HunterLab[5]; + + // Act + var actual = Converter.ToHunterLab(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(31.46263, -32.81796, 28.64938, 0.3605552, 0.9369011, 0.1001514)] + public void Convert_HunterLab_to_CieXyy(float l, float a, float b, float x, float y, float yl) + { + // Arrange + var input = new HunterLab(l, a, b); + var expected = new CieXyy(x, y, yl); + + Span inputSpan = new HunterLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + var actual = Converter.ToCieXyy(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLinearRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLinearRgbConversionTests.cs new file mode 100644 index 0000000000..0339730945 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLinearRgbConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieXyyAndLinearRgbConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 0, 0.1492062, 0)] + public void Convert_CieXyy_to_LinearRgb(float x, float y, float yl, float r, float g, float b) + { + // Arrange + var input = new CieXyy(x, y, yl); + var expected = new LinearRgb(r, g, b); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new LinearRgb[5]; + + // Act + var actual = Converter.ToLinearRgb(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0, 0.1492062, 0, 0.3, 0.6, 0.1067051)] + public void Convert_LinearRgb_to_CieXyy(float r, float g, float b, float x, float y, float yl) + { + // Arrange + var input = new LinearRgb(r, g, b); + var expected = new CieXyy(x, y, yl); + + Span inputSpan = new LinearRgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + var actual = Converter.ToCieXyy(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLmsConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLmsConversionTests.cs new file mode 100644 index 0000000000..fb0e06e6bb --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLmsConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieXyyAndLmsConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 0.06631134, 0.1415282, -0.03809926)] + public void Convert_CieXyy_to_Lms(float x, float y, float yl, float l, float m, float s) + { + // Arrange + var input = new CieXyy(x, y, yl); + var expected = new Lms(l, m, s); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new Lms[5]; + + // Act + var actual = Converter.ToLms(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.06631134, 0.1415282, -0.03809926, 0.360555, 0.9369009, 0.1001514)] + public void Convert_Lms_to_CieXyy(float l, float m, float s, float x, float y, float yl) + { + // Arrange + var input = new Lms(l, m, s); + var expected = new CieXyy(x, y, yl); + + Span inputSpan = new Lms[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + var actual = Converter.ToCieXyy(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndRgbConversionTests.cs new file mode 100644 index 0000000000..5bbcd90875 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndRgbConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieXyyAndRgbConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 0, 0.4225259, 0)] + public void Convert_CieXyy_to_Rgb(float x, float y, float yl, float r, float g, float b) + { + // Arrange + var input = new CieXyy(x, y, yl); + var expected = new Rgb(r, g, b); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + var actual = Converter.ToRgb(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0, 0.4225259, 0, 0.3, 0.6, 0.1067051)] + public void Convert_Rgb_to_CieXyy(float r, float g, float b, float x, float y, float yl) + { + // Arrange + var input = new Rgb(r, g, b); + var expected = new CieXyy(x, y, yl); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + var actual = Converter.ToCieXyy(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndYCbCrConversionTests.cs new file mode 100644 index 0000000000..1ee84ef2e5 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndYCbCrConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieXyyAndYCbCrConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 128, 128)] + [InlineData(0.360555, 0.936901, 0.1001514, 63.24579, 92.30826, 82.88884)] + public void Convert_CieXyy_to_YCbCr(float x, float y, float yl, float y2, float cb, float cr) + { + // Arrange + var input = new CieXyy(x, y, yl); + var expected = new YCbCr(y2, cb, cr); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + var actual = Converter.ToYCbCr(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 128, 128, 0, 0, 0)] + [InlineData(63.24579, 92.30826, 82.88884, 0.3, 0.6, 0.1072441)] + public void Convert_YCbCr_to_CieXyy(float y2, float cb, float cr, float x, float y, float yl) + { + // Arrange + var input = new YCbCr(y2, cb, cr); + var expected = new CieXyy(x, y, yl); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + var actual = Converter.ToCieXyy(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLabConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLabConversionTest.cs similarity index 56% rename from tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLabConversionTest.cs rename to tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLabConversionTest.cs index 186f976188..49b99b7052 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLabConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLabConversionTest.cs @@ -1,12 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; +using System; using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; using Xunit; -namespace SixLabors.ImageSharp.Tests.Colorspaces +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion { /// /// Tests - conversions. @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces /// public class CieXyzAndCieLabConversionTest { - private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(4); + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); /// /// Tests conversion from to (). @@ -35,15 +35,26 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { // Arrange var input = new CieLab(l, a, b, Illuminants.D65); - var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; + var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; + var converter = new ColorSpaceConverter(options); + var expected = new CieXyz(x, y, z); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; // Act - CieXyz output = converter.ToCieXyz(input); + var actual = converter.ToCieXyz(input); + converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(x, output.X, FloatRoundingComparer); - Assert.Equal(y, output.Y, FloatRoundingComparer); - Assert.Equal(z, output.Z, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } /// @@ -60,15 +71,26 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { // Arrange var input = new CieXyz(x, y, z); - var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; + var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; + var converter = new ColorSpaceConverter(options); + var expected = new CieLab(l, a, b); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; // Act - var output = converter.ToCieLab(input); + var actual = converter.ToCieLab(input); + converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(l, output.L, FloatRoundingComparer); - Assert.Equal(a, output.A, FloatRoundingComparer); - Assert.Equal(b, output.B, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchConversionTests.cs new file mode 100644 index 0000000000..77f0c69699 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieXyzAndCieLchConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0.360555, 0.936901, 0.1001514, 97.50815, 155.8035, 139.323)] + public void Convert_CieXyz_to_CieLch(float x, float y, float yl, float l, float c, float h) + { + // Arrange + var input = new CieXyz(x, y, yl); + var expected = new CieLch(l, c, h); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + var actual = Converter.ToCieLch(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(97.50815, 155.8035, 139.323, 0.3605551, 0.936901, 0.1001514)] + public void Convert_CieLch_to_CieXyz(float l, float c, float h, float x, float y, float yl) + { + // Arrange + var input = new CieLch(l, c, h); + var expected = new CieXyz(x, y, yl); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + var actual = Converter.ToCieXyz(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchuvConversionTests.cs new file mode 100644 index 0000000000..24e134d732 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchuvConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieXyzAndCieLchuvConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0.360555, 0.936901, 0.1001514, 97.50697, 183.3831, 133.6321)] + public void Convert_CieXyz_to_CieLchuv(float x, float y, float yl, float l, float c, float h) + { + // Arrange + var input = new CieXyz(x, y, yl); + var expected = new CieLchuv(l, c, h); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLchuv[5]; + + // Act + var actual = Converter.ToCieLchuv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(97.50697, 183.3831, 133.6321, 0.360555, 0.936901, 0.1001515)] + public void Convert_CieLchuv_to_CieXyz(float l, float c, float h, float x, float y, float yl) + { + // Arrange + var input = new CieLchuv(l, c, h); + var expected = new CieXyz(x, y, yl); + + Span inputSpan = new CieLchuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + var actual = Converter.ToCieXyz(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLuvConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLuvConversionTest.cs similarity index 57% rename from tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLuvConversionTest.cs rename to tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLuvConversionTest.cs index 46f4f15b8a..761b9851e3 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieLuvConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLuvConversionTest.cs @@ -1,12 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; +using System; using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; using Xunit; -namespace SixLabors.ImageSharp.Tests.Colorspaces +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion { /// /// Tests - conversions. @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces /// public class CieXyzAndCieLuvConversionTest { - private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(4); + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); /// /// Tests conversion from to (). @@ -34,15 +34,26 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { // Arrange var input = new CieLuv(l, u, v, Illuminants.D65); - var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; + var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; + var converter = new ColorSpaceConverter(options); + var expected = new CieXyz(x, y, z); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; // Act - CieXyz output = converter.ToCieXyz(input); + var actual = converter.ToCieXyz(input); + converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(x, output.X, FloatRoundingComparer); - Assert.Equal(y, output.Y, FloatRoundingComparer); - Assert.Equal(z, output.Z, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } /// @@ -59,15 +70,26 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { // Arrange var input = new CieXyz(x, y, z); - var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; + var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; + var converter = new ColorSpaceConverter(options); + var expected = new CieLuv(l, u, v); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; // Act - CieLuv output = converter.ToCieLuv(input); + var actual = converter.ToCieLuv(input); + converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(l, output.L, FloatRoundingComparer); - Assert.Equal(u, output.U, FloatRoundingComparer); - Assert.Equal(v, output.V, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieXyyConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieXyyConversionTest.cs similarity index 57% rename from tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieXyyConversionTest.cs rename to tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieXyyConversionTest.cs index d461acd56c..2b0350cea1 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndCieXyyConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieXyyConversionTest.cs @@ -1,12 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; +using System; using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; using Xunit; -namespace SixLabors.ImageSharp.Tests.Colorspaces +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion { /// /// Tests - conversions. @@ -17,8 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces /// public class CieXyzAndCieXyyConversionTest { - private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(4); - + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); [Theory] @@ -29,14 +28,24 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces public void Convert_xyY_to_XYZ(float xyzX, float xyzY, float xyzZ, float x, float y, float yl) { var input = new CieXyy(x, y, yl); + var expected = new CieXyz(xyzX, xyzY, xyzZ); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; // Act - CieXyz output = Converter.ToCieXyz(input); + var actual = Converter.ToCieXyz(input); + Converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(xyzX, output.X, FloatRoundingComparer); - Assert.Equal(xyzY, output.Y, FloatRoundingComparer); - Assert.Equal(xyzZ, output.Z, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } [Theory] @@ -47,14 +56,24 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces public void Convert_XYZ_to_xyY(float xyzX, float xyzY, float xyzZ, float x, float y, float yl) { var input = new CieXyz(xyzX, xyzY, xyzZ); + var expected = new CieXyy(x, y, yl); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; // Act - CieXyy output = Converter.ToCieXyy(input); + var actual = Converter.ToCieXyy(input); + Converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(x, output.X, FloatRoundingComparer); - Assert.Equal(y, output.Y, FloatRoundingComparer); - Assert.Equal(yl, output.Yl, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } } } diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHslConversionTests.cs new file mode 100644 index 0000000000..cd1c9f2c3e --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHslConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieXyzAndHslConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.5)] + public void Convert_CieXyz_to_Hsl(float x, float y, float yl, float h, float s, float l) + { + // Arrange + var input = new CieXyz(x, y, yl); + var expected = new Hsl(h, s, l); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; + + // Act + var actual = Converter.ToHsl(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(120, 1, 0.5, 0.3575761, 0.7151522, 0.119192)] + public void Convert_Hsl_to_CieXyz(float h, float s, float l, float x, float y, float yl) + { + // Arrange + var input = new Hsl(h, s, l); + var expected = new CieXyz(x, y, yl); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + var actual = Converter.ToCieXyz(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHsvConversionTests.cs new file mode 100644 index 0000000000..8112f6a198 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHsvConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieXyzAndHsvConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.9999999)] + public void Convert_CieXyz_to_Hsv(float x, float y, float yl, float h, float s, float v) + { + // Arrange + var input = new CieXyz(x, y, yl); + var expected = new Hsv(h, s, v); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; + + // Act + var actual = Converter.ToHsv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(120, 1, 0.9999999, 0.3575761, 0.7151522, 0.119192)] + public void Convert_Hsv_to_CieXyz(float h, float s, float v, float x, float y, float yl) + { + // Arrange + var input = new Hsv(h, s, v); + var expected = new CieXyz(x, y, yl); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + var actual = Converter.ToCieXyz(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHunterLabConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHunterLabConversionTest.cs new file mode 100644 index 0000000000..2fed3e9c55 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHunterLabConversionTest.cs @@ -0,0 +1,118 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + /// + /// Test data generated using: + /// + /// + public class CieXyzAndHunterLabConversionTest + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); + + /// + /// Tests conversion from to (). + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(100, 0, 0, 0.98074, 1, 1.18232)] // C white point is HunterLab 100, 0, 0 + public void Convert_HunterLab_to_Xyz(float l, float a, float b, float x, float y, float z) + { + // Arrange + var input = new HunterLab(l, a, b); + var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.C }; + var converter = new ColorSpaceConverter(options); + var expected = new CieXyz(x, y, z); + + Span inputSpan = new HunterLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + var actual = converter.ToCieXyz(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to (). + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(100, 0, 0, 0.95047, 1, 1.08883)] // D65 white point is HunerLab 100, 0, 0 (adaptation to C performed) + public void Convert_HunterLab_to_Xyz_D65(float l, float a, float b, float x, float y, float z) + { + // Arrange + var input = new HunterLab(l, a, b); + var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65 }; + var converter = new ColorSpaceConverter(options); + var expected = new CieXyz(x, y, z); + + Span inputSpan = new HunterLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + var actual = converter.ToCieXyz(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from () to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.95047, 1, 1.08883, 100, 0, 0)] // D65 white point is HunterLab 100, 0, 0 (adaptation to C performed) + public void Convert_Xyz_D65_to_HunterLab(float x, float y, float z, float l, float a, float b) + { + // Arrange + var input = new CieXyz(x, y, z); + var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65 }; + var converter = new ColorSpaceConverter(options); + var expected = new HunterLab(l, a, b); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new HunterLab[5]; + + // Act + var actual = converter.ToHunterLab(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndLmsConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndLmsConversionTest.cs similarity index 62% rename from tests/ImageSharp.Tests/Colorspaces/CieXyzAndLmsConversionTest.cs rename to tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndLmsConversionTest.cs index 45ca9049ab..75634eb51e 100644 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzAndLmsConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndLmsConversionTest.cs @@ -1,22 +1,22 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; +using System; using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; using Xunit; -namespace SixLabors.ImageSharp.Tests.Colorspaces +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion { /// - /// Tests - conversions. + /// Tests - conversions. /// /// /// Test data generated using original colorful library. /// public class CieXyzAndLmsConversionTest { - private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(5); + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); /// /// Tests conversion from () to . @@ -33,14 +33,24 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces // Arrange var input = new Lms(l, m, s); var converter = new ColorSpaceConverter(); + var expected = new CieXyz(x, y, z); + + Span inputSpan = new Lms[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; // Act - CieXyz output = converter.ToCieXyz(input); + var actual = converter.ToCieXyz(input); + converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(x, output.X, FloatRoundingComparer); - Assert.Equal(y, output.Y, FloatRoundingComparer); - Assert.Equal(z, output.Z, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } /// @@ -58,14 +68,24 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces // Arrange var input = new CieXyz(x, y, z); var converter = new ColorSpaceConverter(); + var expected = new Lms(l, m, s); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new Lms[5]; // Act - Lms output = converter.ToLms(input); + var actual = converter.ToLms(input); + converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(l, output.L, FloatRoundingComparer); - Assert.Equal(m, output.M, FloatRoundingComparer); - Assert.Equal(s, output.S, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndYCbCrConversionTests.cs new file mode 100644 index 0000000000..9ea890f101 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndYCbCrConversionTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CieXyzAndYCbCrConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 128, 128)] + [InlineData(0.360555, 0.936901, 0.1001514, 149.685, 43.52769, 21.23457)] + public void Convert_CieXyz_to_YCbCr(float x, float y, float z, float y2, float cb, float cr) + { + // Arrange + var input = new CieXyz(x, y, z); + var expected = new YCbCr(y2, cb, cr); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + var actual = Converter.ToYCbCr(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 128, 128, 0, 0, 0)] + [InlineData(149.685, 43.52769, 21.23457, 0.3575761, 0.7151522, 0.119192)] + public void Convert_YCbCr_to_CieXyz(float y2, float cb, float cr, float x, float y, float z) + { + // Arrange + var input = new YCbCr(y2, cb, cr); + var expected = new CieXyz(x, y, z); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + var actual = Converter.ToCieXyz(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLchConversionTests.cs new file mode 100644 index 0000000000..dbb0c6e200 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLchConversionTests.cs @@ -0,0 +1,78 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CmykAndCieLchConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 62.85025, 64.77041, 118.2425)] + public void Convert_Cmyk_to_CieLch(float c, float m, float y, float k, float l, float c2, float h) + { + // Arrange + var input = new Cmyk(c, m, y, k); + var expected = new CieLch(l, c2, h); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + var actual = Converter.ToCieLch(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(100, 3.81656E-05, 218.6598, 0, 1.192093E-07, 0, 5.960464E-08)] + [InlineData(62.85025, 64.77041, 118.2425, 0.286581, 0, 0.7975187, 0.34983)] + public void Convert_CieLch_to_Cmyk(float l, float c2, float h, float c, float m, float y, float k) + { + // Arrange + var input = new CieLch(l, c2, h); + var expected = new Cmyk(c, m, y, k); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + var actual = Converter.ToCmyk(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLuvConversionTests.cs new file mode 100644 index 0000000000..5fcc59090b --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLuvConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CmykAndCieLuvConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 100, -1.937151E-05, 0)] + [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 62.66017, -24.01712, 68.29556)] + public void Convert_Cmyk_to_CieLuv(float c, float m, float y, float k, float l, float u, float v) + { + // Arrange + var input = new Cmyk(c, m, y, k); + var expected = new CieLuv(l, u, v); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + var actual = Converter.ToCieLuv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(100, -1.937151E-05, 0, 3.576279E-07, 0, 0, 5.960464E-08)] + [InlineData(62.66017, -24.01712, 68.29556, 0.2865804, 0, 0.7975189, 0.3498302)] + public void Convert_CieLuv_to_Cmyk(float l, float u, float v, float c, float m, float y, float k) + { + // Arrange + var input = new CieLuv(l, u, v); + var expected = new Cmyk(c, m, y, k); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + var actual = Converter.ToCmyk(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyyConversionTests.cs new file mode 100644 index 0000000000..7ff80c170b --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyyConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CmykAndCieXyyConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0.3127266, 0.3290231, 1)] + [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 0.3628971, 0.5289949, 0.3118104)] + public void Convert_Cmyk_to_CieXyy(float c, float m, float y, float k, float x, float y2, float yl) + { + // Arrange + var input = new Cmyk(c, m, y, k); + var expected = new CieXyy(x, y2, yl); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + var actual = Converter.ToCieXyy(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0.3127266, 0.3290231, 1, 0, 0, 0, 5.960464E-08)] + [InlineData(0.3628971, 0.5289949, 0.3118104, 0.2865805, 0, 0.7975187, 0.3498302)] + public void Convert_CieXyy_to_Cmyk(float x, float y2, float yl, float c, float m, float y, float k) + { + // Arrange + var input = new CieXyy(x, y2, yl); + var expected = new Cmyk(c, m, y, k); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + var actual = Converter.ToCmyk(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyzConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyzConversionTests.cs new file mode 100644 index 0000000000..8017302059 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyzConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CmykAndCieXyzConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0.9504699, 1, 1.08883)] + [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 0.2139058, 0.3118104, 0.0637231)] + public void Convert_Cmyk_to_CieXyz(float c, float m, float y, float k, float x, float y2, float z) + { + // Arrange + var input = new Cmyk(c, m, y, k); + var expected = new CieXyz(x, y2, z); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + var actual = Converter.ToCieXyz(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0.9504699, 1, 1.08883, 1.192093E-07, 0, 0, 5.960464E-08)] + [InlineData(0.2139058, 0.3118104, 0.0637231, 0.2865805, 0, 0.7975187, 0.3498302)] + public void Convert_CieXyz_to_Cmyk(float x, float y2, float z, float c, float m, float y, float k) + { + // Arrange + var input = new CieXyz(x, y2, z); + var expected = new Cmyk(c, m, y, k); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + var actual = Converter.ToCmyk(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHslConversionTests.cs new file mode 100644 index 0000000000..3464fdbbde --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHslConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CmykAndHslConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0, 1)] + [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 81.56041, 0.6632275, 0.3909085)] + public void Convert_Cmyk_to_Hsl(float c, float m, float y, float k, float h, float s, float l) + { + // Arrange + var input = new Cmyk(c, m, y, k); + var expected = new Hsl(h, s, l); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; + + // Act + var actual = Converter.ToHsl(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 1, 0, 0, 0, 0)] + [InlineData(81.56041, 0.6632275, 0.3909085, 0.2865805, 0, 0.7975187, 0.3498302)] + public void Convert_Hsl_to_Cmyk(float h, float s, float l, float c, float m, float y, float k) + { + // Arrange + var input = new Hsl(h, s, l); + var expected = new Cmyk(c, m, y, k); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + var actual = Converter.ToCmyk(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHsvConversionTests.cs new file mode 100644 index 0000000000..26af5ddd30 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHsvConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CmykAndHsvConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 0, 0, 1)] + [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 81.56041, 0.7975187, 0.6501698)] + public void Convert_Cmyk_to_Hsv(float c, float m, float y, float k, float h, float s, float v) + { + // Arrange + var input = new Cmyk(c, m, y, k); + var expected = new Hsv(h, s, v); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; + + // Act + var actual = Converter.ToHsv(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 1, 0, 0, 0, 0)] + [InlineData(81.56041, 0.7975187, 0.6501698, 0.2865805, 0, 0.7975187, 0.3498302)] + public void Convert_Hsv_to_Cmyk(float h, float s, float v, float c, float m, float y, float k) + { + // Arrange + var input = new Hsv(h, s, v); + var expected = new Cmyk(c, m, y, k); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + var actual = Converter.ToCmyk(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHunterLabConversionTests.cs new file mode 100644 index 0000000000..dc40ee518e --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHunterLabConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CmykAndHunterLabConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 99.99999, 0, -1.66893E-05)] + [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 55.66742, -27.21679, 31.73834)] + public void Convert_Cmyk_to_HunterLab(float c, float m, float y, float k, float l, float a, float b) + { + // Arrange + var input = new Cmyk(c, m, y, k); + var expected = new HunterLab(l, a, b); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new HunterLab[5]; + + // Act + var actual = Converter.ToHunterLab(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(99.99999, 0, -1.66893E-05, 1.192093E-07, 1.192093E-07, 0, 5.960464E-08)] + [InlineData(55.66742, -27.21679, 31.73834, 0.2865806, 0, 0.7975186, 0.3498301)] + public void Convert_HunterLab_to_Cmyk(float l, float a, float b, float c, float m, float y, float k) + { + // Arrange + var input = new HunterLab(l, a, b); + var expected = new Cmyk(c, m, y, k); + + Span inputSpan = new HunterLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + var actual = Converter.ToCmyk(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndYCbCrConversionTests.cs new file mode 100644 index 0000000000..00569ced2e --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndYCbCrConversionTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests - conversions. + /// + public class CmykAndYCbCrConversionTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); + private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(0, 0, 0, 0, 255, 128, 128)] + [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 136.5134, 69.90555, 114.9948)] + public void Convert_Cmyk_to_YCbCr(float c, float m, float y, float k, float y2, float cb, float cr) + { + // Arrange + var input = new Cmyk(c, m, y, k); + var expected = new YCbCr(y2, cb, cr); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + var actual = Converter.ToYCbCr(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + + /// + /// Tests conversion from to . + /// + [Theory] + [InlineData(255, 128, 128, 0, 0, 0, 5.960464E-08)] + [InlineData(136.5134, 69.90555, 114.9948, 0.2891567, 0, 0.7951807, 0.3490196)] + public void Convert_YCbCr_to_Cmyk(float y2, float cb, float cr, float c, float m, float y, float k) + { + // Arrange + var input = new YCbCr(y2, cb, cr); + var expected = new Cmyk(c, m, y, k); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + var actual = Converter.ToCmyk(input); + Converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs new file mode 100644 index 0000000000..326777f3c6 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs @@ -0,0 +1,181 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + /// + /// Tests methods. + /// Test data generated using: + /// + /// + /// + public class ColorConverterAdaptTest + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(1, 1, 1, 1, 1, 1)] + [InlineData(0.206162, 0.260277, 0.746717, 0.220000, 0.130000, 0.780000)] + public void Adapt_RGB_WideGamutRGB_To_sRGB(float r1, float g1, float b1, float r2, float g2, float b2) + { + // Arrange + var input = new Rgb(r1, g1, b1, RgbWorkingSpaces.WideGamutRgb); + var expected = new Rgb(r2, g2, b2, RgbWorkingSpaces.SRgb); + var options = new ColorSpaceConverterOptions { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; + var converter = new ColorSpaceConverter(options); + + // Action + Rgb actual = converter.Adapt(input); + + // Assert + Assert.Equal(expected.WorkingSpace, actual.WorkingSpace, ColorSpaceComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(1, 1, 1, 1, 1, 1)] + [InlineData(0.220000, 0.130000, 0.780000, 0.206162, 0.260277, 0.746717)] + public void Adapt_RGB_SRGB_To_WideGamutRGB(float r1, float g1, float b1, float r2, float g2, float b2) + { + // Arrange + var input = new Rgb(r1, g1, b1, RgbWorkingSpaces.SRgb); + var expected = new Rgb(r2, g2, b2, RgbWorkingSpaces.WideGamutRgb); + var options = new ColorSpaceConverterOptions { TargetRgbWorkingSpace = RgbWorkingSpaces.WideGamutRgb }; + var converter = new ColorSpaceConverter(options); + + // Action + Rgb actual = converter.Adapt(input); + + // Assert + Assert.Equal(expected.WorkingSpace, actual.WorkingSpace, ColorSpaceComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(22, 33, 1, 22.269869, 32.841164, 1.633926)] + public void Adapt_Lab_D65_To_D50(float l1, float a1, float b1, float l2, float a2, float b2) + { + // Arrange + var input = new CieLab(l1, a1, b1, Illuminants.D65); + var expected = new CieLab(l2, a2, b2); + var options = new ColorSpaceConverterOptions { TargetLabWhitePoint = Illuminants.D50 }; + var converter = new ColorSpaceConverter(options); + + // Action + CieLab actual = converter.Adapt(input); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.5, 0.5, 0.5, 0.510286, 0.501489, 0.378970)] + public void Adapt_Xyz_D65_To_D50_Bradford(float x1, float y1, float z1, float x2, float y2, float z2) + { + // Arrange + var input = new CieXyz(x1, y1, z1); + var expected = new CieXyz(x2, y2, z2); + var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D50 }; + var converter = new ColorSpaceConverter(options); + + // Action + CieXyz actual = converter.Adapt(input, Illuminants.D65); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.5, 0.5, 0.5, 0.507233, 0.500000, 0.378943)] + public void Adapt_Xyz_D65_To_D50_XyzScaling(float x1, float y1, float z1, float x2, float y2, float z2) + { + // Arrange + var input = new CieXyz(x1, y1, z1); + var expected = new CieXyz(x2, y2, z2); + var options = new ColorSpaceConverterOptions + { + ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling), + WhitePoint = Illuminants.D50 + }; + + var converter = new ColorSpaceConverter(options); + + // Action + CieXyz actual = converter.Adapt(input, Illuminants.D65); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(22, 33, 1, 22.1090755, 32.2102661, 1.153463)] + public void Adapt_HunterLab_D65_To_D50(float l1, float a1, float b1, float l2, float a2, float b2) + { + // Arrange + var input = new HunterLab(l1, a1, b1, Illuminants.D65); + var expected = new HunterLab(l2, a2, b2); + var options = new ColorSpaceConverterOptions { TargetLabWhitePoint = Illuminants.D50 }; + var converter = new ColorSpaceConverter(options); + + // Action + HunterLab actual = converter.Adapt(input); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(22, 33, 1, 22, 33, 0.9999999)] + public void Adapt_CieLchuv_D65_To_D50_XyzScaling(float l1, float c1, float h1, float l2, float c2, float h2) + { + // Arrange + var input = new CieLchuv(l1, c1, h1, Illuminants.D65); + var expected = new CieLchuv(l2, c2, h2); + var options = new ColorSpaceConverterOptions + { + ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling), + TargetLabWhitePoint = Illuminants.D50 + }; + var converter = new ColorSpaceConverter(options); + + // Action + CieLchuv actual = converter.Adapt(input); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + } + + [Theory] + [InlineData(22, 33, 1, 22, 33, 0.9999999)] + public void Adapt_CieLch_D65_To_D50_XyzScaling(float l1, float c1, float h1, float l2, float c2, float h2) + { + // Arrange + var input = new CieLch(l1, c1, h1, Illuminants.D65); + var expected = new CieLch(l2, c2, h2); + var options = new ColorSpaceConverterOptions + { + ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling), + TargetLabWhitePoint = Illuminants.D50 + }; + var converter = new ColorSpaceConverter(options); + + // Action + CieLch actual = converter.Adapt(input); + + // Assert + Assert.Equal(expected, actual, ColorSpaceComparer); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCieXyzConversionTest.cs similarity index 54% rename from tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs rename to tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCieXyzConversionTest.cs index 929c35ee90..8a2cd1159e 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCieXyzConversionTest.cs @@ -1,12 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; +using System; using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; using Xunit; -namespace SixLabors.ImageSharp.Tests.Colorspaces +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion { /// /// Tests - conversions. @@ -17,9 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces /// public class RgbAndCieXyzConversionTest { - private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(5); - - private static readonly ApproximateFloatComparer ApproximateComparer = new ApproximateFloatComparer(0.0001F); + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); /// /// Tests conversion from () @@ -36,18 +34,27 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { // Arrange var input = new CieXyz(x, y, z); - var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D50, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; + var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D50, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; + var converter = new ColorSpaceConverter(options); + var expected = new Rgb(r, g, b); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; // Act - Rgb output = converter.ToRgb(input); + var actual = converter.ToRgb(input); + converter.Convert(inputSpan, actualSpan); // Assert - IEqualityComparer comparer = new ApproximateFloatComparer(0.001f); + Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace, ApproximateComparer); - Assert.Equal(r, output.R, comparer); - Assert.Equal(g, output.G, comparer); - Assert.Equal(b, output.B, comparer); + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } /// @@ -65,17 +72,28 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces public void Convert_XYZ_D65_to_SRGB(float x, float y, float z, float r, float g, float b) { // Arrange - CieXyz input = new CieXyz(x, y, z); - ColorSpaceConverter converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; + var input = new CieXyz(x, y, z); + var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; + var converter = new ColorSpaceConverter(options); + var expected = new Rgb(r, g, b); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; // Act - Rgb output = converter.ToRgb(input); + var actual = converter.ToRgb(input); + converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace, ApproximateComparer); - Assert.Equal(r, output.R, FloatRoundingComparer); - Assert.Equal(g, output.G, FloatRoundingComparer); - Assert.Equal(b, output.B, FloatRoundingComparer); + Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } /// @@ -93,16 +111,26 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { // Arrange var input = new Rgb(r, g, b); - var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D50 }; + var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D50 }; + var converter = new ColorSpaceConverter(options); + var expected = new CieXyz(x, y, z); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; // Act - CieXyz output = converter.ToCieXyz(input); + var actual = converter.ToCieXyz(input); + converter.Convert(inputSpan, actualSpan); // Assert - IEqualityComparer comparer = new ApproximateFloatComparer(0.001f); - Assert.Equal(x, output.X, comparer); - Assert.Equal(y, output.Y, comparer); - Assert.Equal(z, output.Z, comparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } /// @@ -120,15 +148,26 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { // Arrange var input = new Rgb(r, g, b); - var converter = new ColorSpaceConverter { WhitePoint = Illuminants.D65 }; + var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65 }; + var converter = new ColorSpaceConverter(options); + var expected = new CieXyz(x, y, z); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; // Act - CieXyz output = converter.ToCieXyz(input); + var actual = converter.ToCieXyz(input); + converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(x, output.X, FloatRoundingComparer); - Assert.Equal(y, output.Y, FloatRoundingComparer); - Assert.Equal(z, output.Z, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCmykConversionTest.cs similarity index 56% rename from tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs rename to tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCmykConversionTest.cs index 9a6ff7b491..b01e3a854c 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndCmykConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCmykConversionTest.cs @@ -1,12 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; +using System; using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; using Xunit; -namespace SixLabors.ImageSharp.Tests.Colorspaces +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion { /// /// Tests - conversions. @@ -18,11 +18,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces /// public class RgbAndCmykConversionTest { - private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(4); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - private static readonly ApproximateFloatComparer ApproximateComparer = new ApproximateFloatComparer(0.0001F); + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); /// /// Tests conversion from to . @@ -35,15 +32,25 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { // Arrange var input = new Cmyk(c, m, y, k); + var expected = new Rgb(r, g, b); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; // Act - Rgb output = Converter.ToRgb(input); + var actual = Converter.ToRgb(input); + Converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace, ApproximateComparer); - Assert.Equal(r, output.R, FloatRoundingComparer); - Assert.Equal(g, output.G, FloatRoundingComparer); - Assert.Equal(b, output.B, FloatRoundingComparer); + Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } /// @@ -57,15 +64,24 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { // Arrange var input = new Rgb(r, g, b); + var expected = new Cmyk(c, m, y, k); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; // Act - Cmyk output = Converter.ToCmyk(input); + var actual = Converter.ToCmyk(input); + Converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(c, output.C, FloatRoundingComparer); - Assert.Equal(m, output.M, FloatRoundingComparer); - Assert.Equal(y, output.Y, FloatRoundingComparer); - Assert.Equal(k, output.K, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } } } diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHslConversionTest.cs similarity index 58% rename from tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs rename to tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHslConversionTest.cs index 4f15379329..502df84133 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndHslConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHslConversionTest.cs @@ -1,12 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; +using System; using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; using Xunit; -namespace SixLabors.ImageSharp.Tests.Colorspaces +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion { /// /// Tests - conversions. @@ -18,11 +18,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces /// public class RgbAndHslConversionTest { - private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(4); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - private static readonly ApproximateFloatComparer ApproximateComparer = new ApproximateFloatComparer(0.0001F); + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); /// /// Tests conversion from to . @@ -38,15 +35,25 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { // Arrange var input = new Hsl(h, s, l); + var expected = new Rgb(r, g, b); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; // Act - Rgb output = Converter.ToRgb(input); + var actual = Converter.ToRgb(input); + Converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace, ApproximateComparer); - Assert.Equal(r, output.R, FloatRoundingComparer); - Assert.Equal(g, output.G, FloatRoundingComparer); - Assert.Equal(b, output.B, FloatRoundingComparer); + Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } /// @@ -62,14 +69,25 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { // Arrange var input = new Rgb(r, g, b); + var expected = new Hsl(h, s, l); + + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; // Act - Hsl output = Converter.ToHsl(input); + var actual = Converter.ToHsl(input); + Converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(h, output.H, FloatRoundingComparer); - Assert.Equal(s, output.S, FloatRoundingComparer); - Assert.Equal(l, output.L, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } } } diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHsvConversionTest.cs similarity index 57% rename from tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs rename to tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHsvConversionTest.cs index 7f46ff1fc9..9adc94af7c 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndHsvConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHsvConversionTest.cs @@ -1,12 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; +using System; using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; using Xunit; -namespace SixLabors.ImageSharp.Tests.Colorspaces +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion { /// /// Tests - conversions. @@ -17,11 +17,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces /// public class RgbAndHsvConversionTest { - private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(4); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - private static readonly ApproximateFloatComparer ApproximateComparer = new ApproximateFloatComparer(0.0001F); + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); /// /// Tests conversion from to . @@ -37,15 +34,25 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { // Arrange var input = new Hsv(h, s, v); + var expected = new Rgb(r, g, b); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; // Act - Rgb output = Converter.ToRgb(input); + var actual = Converter.ToRgb(input); + Converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace, ApproximateComparer); - Assert.Equal(r, output.R, FloatRoundingComparer); - Assert.Equal(g, output.G, FloatRoundingComparer); - Assert.Equal(b, output.B, FloatRoundingComparer); + Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } /// @@ -61,14 +68,24 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { // Arrange var input = new Rgb(r, g, b); + var expected = new Hsv(h, s, v); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; // Act - Hsv output = Converter.ToHsv(input); + var actual = Converter.ToHsv(input); + Converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(h, output.H, FloatRoundingComparer); - Assert.Equal(s, output.S, FloatRoundingComparer); - Assert.Equal(v, output.V, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } } } diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndYCbCrConversionTest.cs similarity index 55% rename from tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs rename to tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndYCbCrConversionTest.cs index 46c12e3a55..94879eee7a 100644 --- a/tests/ImageSharp.Tests/Colorspaces/RgbAndYCbCrConversionTest.cs +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndYCbCrConversionTest.cs @@ -1,12 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; +using System; using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; using Xunit; -namespace SixLabors.ImageSharp.Tests.Colorspaces +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion { /// /// Tests - conversions. @@ -16,11 +16,8 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces /// public class RgbAndYCbCrConversionTest { - private static readonly IEqualityComparer FloatRoundingComparer = new FloatRoundingComparer(3); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - private static readonly ApproximateFloatComparer ApproximateComparer = new ApproximateFloatComparer(0.0001F); + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.001F); /// /// Tests conversion from to . @@ -33,15 +30,25 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { // Arrange var input = new YCbCr(y, cb, cr); + var expected = new Rgb(r, g, b); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; // Act - Rgb output = Converter.ToRgb(input); + var actual = Converter.ToRgb(input); + Converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, output.WorkingSpace, ApproximateComparer); - Assert.Equal(r, output.R, FloatRoundingComparer); - Assert.Equal(g, output.G, FloatRoundingComparer); - Assert.Equal(b, output.B, FloatRoundingComparer); + Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } /// @@ -56,14 +63,24 @@ namespace SixLabors.ImageSharp.Tests.Colorspaces { // Arrange var input = new Rgb(r, g, b); + var expected = new YCbCr(y, cb, cr); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; // Act - YCbCr output = Converter.ToYCbCr(input); + var actual = Converter.ToYCbCr(input); + Converter.Convert(inputSpan, actualSpan); // Assert - Assert.Equal(y, output.Y, FloatRoundingComparer); - Assert.Equal(cb, output.Cb, FloatRoundingComparer); - Assert.Equal(cr, output.Cr, FloatRoundingComparer); + Assert.Equal(expected, actual, ColorSpaceComparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/VonKriesChromaticAdaptationTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/VonKriesChromaticAdaptationTests.cs new file mode 100644 index 0000000000..b1427f4d5f --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/Conversion/VonKriesChromaticAdaptationTests.cs @@ -0,0 +1,41 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorSpaces.Conversion; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion +{ + public class VonKriesChromaticAdaptationTests + { + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); + public static readonly TheoryData WhitePoints = new TheoryData + { + {CieLuv.DefaultWhitePoint, CieLab.DefaultWhitePoint}, + {CieLuv.DefaultWhitePoint, CieLuv.DefaultWhitePoint} + }; + + [Theory] + [MemberData(nameof(WhitePoints))] + public void SingleAndBulkTransformYieldIdenticalResults(CieXyz sourceWhitePoint, CieXyz destinationWhitePoint) + { + var adaptation = new VonKriesChromaticAdaptation(); + var input = new CieXyz(1, 0, 1); + CieXyz expected = adaptation.Transform(input, sourceWhitePoint, destinationWhitePoint); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + adaptation.Transform(inputSpan, actualSpan, sourceWhitePoint, destinationWhitePoint); + + for (int i = 0; i < inputSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Colorspaces/HslTests.cs b/tests/ImageSharp.Tests/Colorspaces/HslTests.cs new file mode 100644 index 0000000000..60cfa9761a --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/HslTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ + /// + /// Tests the struct. + /// + public class HslTests + { + [Fact] + public void HslConstructorAssignsFields() + { + const float h = 275F; + const float s = .64F; + const float l = .87F; + var hsl = new Hsl(h, s, l); + + Assert.Equal(h, hsl.H); + Assert.Equal(s, hsl.S); + Assert.Equal(l, hsl.L); + } + + [Fact] + public void HslEquality() + { + var x = default(Hsl); + var y = new Hsl(Vector3.One); + + Assert.True(default(Hsl) == default(Hsl)); + Assert.False(default(Hsl) != default(Hsl)); + Assert.Equal(default(Hsl), default(Hsl)); + Assert.Equal(new Hsl(1, 0, 1), new Hsl(1, 0, 1)); + Assert.Equal(new Hsl(Vector3.One), new Hsl(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/HsvTests.cs b/tests/ImageSharp.Tests/Colorspaces/HsvTests.cs new file mode 100644 index 0000000000..d1d1d15c8a --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/HsvTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ + /// + /// Tests the struct. + /// + public class HsvTests + { + [Fact] + public void HsvConstructorAssignsFields() + { + const float h = 275F; + const float s = .64F; + const float v = .87F; + var hsv = new Hsv(h, s, v); + + Assert.Equal(h, hsv.H); + Assert.Equal(s, hsv.S); + Assert.Equal(v, hsv.V); + } + + [Fact] + public void HsvEquality() + { + var x = default(Hsv); + var y = new Hsv(Vector3.One); + + Assert.True(default(Hsv) == default(Hsv)); + Assert.False(default(Hsv) != default(Hsv)); + Assert.Equal(default(Hsv), default(Hsv)); + Assert.Equal(new Hsv(1, 0, 1), new Hsv(1, 0, 1)); + Assert.Equal(new Hsv(Vector3.One), new Hsv(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/HunterLabTests.cs b/tests/ImageSharp.Tests/Colorspaces/HunterLabTests.cs new file mode 100644 index 0000000000..95261e1d98 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/HunterLabTests.cs @@ -0,0 +1,45 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ + /// + /// Tests the struct. + /// + public class HunterLabTests + { + [Fact] + public void HunterLabConstructorAssignsFields() + { + const float l = 75F; + const float a = -64F; + const float b = 87F; + var hunterLab = new HunterLab(l, a, b); + + Assert.Equal(l, hunterLab.L); + Assert.Equal(a, hunterLab.A); + Assert.Equal(b, hunterLab.B); + } + + [Fact] + public void HunterLabEquality() + { + var x = default(HunterLab); + var y = new HunterLab(Vector3.One); + + Assert.True(default(HunterLab) == default(HunterLab)); + Assert.True(default(HunterLab) != new HunterLab(1, 0, 1)); + Assert.False(default(HunterLab) == new HunterLab(1, 0, 1)); + Assert.Equal(default(HunterLab), default(HunterLab)); + Assert.Equal(new HunterLab(1, 0, 1), new HunterLab(1, 0, 1)); + Assert.Equal(new HunterLab(Vector3.One), new HunterLab(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/LinearRgbTests.cs b/tests/ImageSharp.Tests/Colorspaces/LinearRgbTests.cs new file mode 100644 index 0000000000..ef42e68bcc --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/LinearRgbTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ + /// + /// Tests the struct. + /// + public class LinearRgbTests + { + [Fact] + public void LinearRgbConstructorAssignsFields() + { + const float r = .75F; + const float g = .64F; + const float b = .87F; + var rgb = new LinearRgb(r, g, b); + + Assert.Equal(r, rgb.R); + Assert.Equal(g, rgb.G); + Assert.Equal(b, rgb.B); + } + + [Fact] + public void LinearRgbEquality() + { + var x = default(LinearRgb); + var y = new LinearRgb(Vector3.One); + + Assert.True(default(LinearRgb) == default(LinearRgb)); + Assert.False(default(LinearRgb) != default(LinearRgb)); + Assert.Equal(default(LinearRgb), default(LinearRgb)); + Assert.Equal(new LinearRgb(1, 0, 1), new LinearRgb(1, 0, 1)); + Assert.Equal(new LinearRgb(Vector3.One), new LinearRgb(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/LmsTests.cs b/tests/ImageSharp.Tests/Colorspaces/LmsTests.cs new file mode 100644 index 0000000000..1b0939dc5c --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/LmsTests.cs @@ -0,0 +1,45 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ + /// + /// Tests the struct. + /// + public class LmsTests + { + [Fact] + public void LmsConstructorAssignsFields() + { + const float l = 75F; + const float m = -64F; + const float s = 87F; + var Lms = new Lms(l, m, s); + + Assert.Equal(l, Lms.L); + Assert.Equal(m, Lms.M); + Assert.Equal(s, Lms.S); + } + + [Fact] + public void LmsEquality() + { + var x = default(Lms); + var y = new Lms(Vector3.One); + + Assert.True(default(Lms) == default(Lms)); + Assert.True(default(Lms) != new Lms(1, 0, 1)); + Assert.False(default(Lms) == new Lms(1, 0, 1)); + Assert.Equal(default(Lms), default(Lms)); + Assert.Equal(new Lms(1, 0, 1), new Lms(1, 0, 1)); + Assert.Equal(new Lms(Vector3.One), new Lms(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbTests.cs b/tests/ImageSharp.Tests/Colorspaces/RgbTests.cs new file mode 100644 index 0000000000..7987fbe9f2 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/RgbTests.cs @@ -0,0 +1,83 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ + /// + /// Tests the struct. + /// + public class RgbTests + { + [Fact] + public void RgbConstructorAssignsFields() + { + const float r = .75F; + const float g = .64F; + const float b = .87F; + var rgb = new Rgb(r, g, b); + + Assert.Equal(r, rgb.R); + Assert.Equal(g, rgb.G); + Assert.Equal(b, rgb.B); + } + + [Fact] + public void RgbEquality() + { + var x = default(Rgb); + var y = new Rgb(Vector3.One); + + Assert.True(default(Rgb) == default(Rgb)); + Assert.False(default(Rgb) != default(Rgb)); + Assert.Equal(default(Rgb), default(Rgb)); + Assert.Equal(new Rgb(1, 0, 1), new Rgb(1, 0, 1)); + Assert.Equal(new Rgb(Vector3.One), new Rgb(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + + [Fact] + public void RgbAndRgb24Operators() + { + const byte r = 64; + const byte g = 128; + const byte b = 255; + + Rgb24 rgb24 = new Rgb(r / 255F, g / 255F, b / 255F); + Rgb rgb2 = rgb24; + + Assert.Equal(r, rgb24.R); + Assert.Equal(g, rgb24.G); + Assert.Equal(b, rgb24.B); + + Assert.Equal(r / 255F, rgb2.R); + Assert.Equal(g / 255F, rgb2.G); + Assert.Equal(b / 255F, rgb2.B); + } + + [Fact] + public void RgbAndRgba32Operators() + { + const byte r = 64; + const byte g = 128; + const byte b = 255; + + Rgba32 rgba32 = new Rgb(r / 255F, g / 255F, b / 255F); + Rgb rgb2 = rgba32; + + Assert.Equal(r, rgba32.R); + Assert.Equal(g, rgba32.G); + Assert.Equal(b, rgba32.B); + + Assert.Equal(r / 255F, rgb2.R); + Assert.Equal(g / 255F, rgb2.G); + Assert.Equal(b / 255F, rgb2.B); + } + } +} diff --git a/tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs b/tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs new file mode 100644 index 0000000000..5249b709b1 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs @@ -0,0 +1,66 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ + public class StringRepresentationTests + { + private static readonly Vector3 one = new Vector3(1); + private static readonly Vector3 zero = new Vector3(0); + private static readonly Vector3 random = new Vector3(42.4F, 94.5F, 83.4F); + + public static readonly TheoryData TestData = new TheoryData + { + { new CieLab(zero), "CieLab(0, 0, 0)" }, + { new CieLch(zero), "CieLch(0, 0, 0)" }, + { new CieLchuv(zero), "CieLchuv(0, 0, 0)" }, + { new CieLuv(zero), "CieLuv(0, 0, 0)" }, + { new CieXyz(zero), "CieXyz(0, 0, 0)" }, + { new CieXyy(zero), "CieXyy(0, 0, 0)" }, + { new HunterLab(zero), "HunterLab(0, 0, 0)" }, + { new Lms(zero), "Lms(0, 0, 0)" }, + { new LinearRgb(zero), "LinearRgb(0, 0, 0)" }, + { new Rgb(zero), "Rgb(0, 0, 0)" }, + { new Hsl(zero), "Hsl(0, 0, 0)" }, + { new Hsv(zero), "Hsv(0, 0, 0)" }, + { new YCbCr(zero), "YCbCr(0, 0, 0)" }, + + { new CieLab(one), "CieLab(1, 1, 1)" }, + { new CieLch(one), "CieLch(1, 1, 1)" }, + { new CieLchuv(one), "CieLchuv(1, 1, 1)" }, + { new CieLuv(one), "CieLuv(1, 1, 1)" }, + { new CieXyz(one), "CieXyz(1, 1, 1)" }, + { new CieXyy(one), "CieXyy(1, 1, 1)" }, + { new HunterLab(one), "HunterLab(1, 1, 1)" }, + { new Lms(one), "Lms(1, 1, 1)" }, + { new LinearRgb(one), "LinearRgb(1, 1, 1)" }, + { new Rgb(one), "Rgb(1, 1, 1)" }, + { new Hsl(one), "Hsl(1, 1, 1)" }, + { new Hsv(one), "Hsv(1, 1, 1)" }, + { new YCbCr(one), "YCbCr(1, 1, 1)" }, + { new CieXyChromaticityCoordinates(1, 1), "CieXyChromaticityCoordinates(1, 1)"}, + + { new CieLab(random), "CieLab(42.4, 94.5, 83.4)" }, + { new CieLch(random), "CieLch(42.4, 94.5, 83.4)" }, + { new CieLchuv(random), "CieLchuv(42.4, 94.5, 83.4)" }, + { new CieLuv(random), "CieLuv(42.4, 94.5, 83.4)" }, + { new CieXyz(random), "CieXyz(42.4, 94.5, 83.4)" }, + { new CieXyy(random), "CieXyy(42.4, 94.5, 83.4)" }, + { new HunterLab(random), "HunterLab(42.4, 94.5, 83.4)" }, + { new Lms(random), "Lms(42.4, 94.5, 83.4)" }, + { new LinearRgb(random), "LinearRgb(1, 1, 1)" }, // clamping to 1 is expected + { new Rgb(random), "Rgb(1, 1, 1)" }, // clamping to 1 is expected + { new Hsl(random), "Hsl(42.4, 1, 1)" }, // clamping to 1 is expected + { new Hsv(random), "Hsv(42.4, 1, 1)" }, // clamping to 1 is expected + { new YCbCr(random), "YCbCr(42.4, 94.5, 83.4)" }, + }; + + [Theory] + [MemberData(nameof(TestData))] + public void StringRepresentationsAreCorrect(object color, string text) => Assert.Equal(text, color.ToString()); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colorspaces/YCbCrTests.cs b/tests/ImageSharp.Tests/Colorspaces/YCbCrTests.cs new file mode 100644 index 0000000000..f3e6f88f49 --- /dev/null +++ b/tests/ImageSharp.Tests/Colorspaces/YCbCrTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Colorspaces +{ + /// + /// Tests the struct. + /// + public class YCbCrTests + { + [Fact] + public void YCbCrConstructorAssignsFields() + { + const float y = 75F; + const float cb = 64F; + const float cr = 87F; + var yCbCr = new YCbCr(y, cb, cr); + + Assert.Equal(y, yCbCr.Y); + Assert.Equal(cb, yCbCr.Cb); + Assert.Equal(cr, yCbCr.Cr); + } + + [Fact] + public void YCbCrEquality() + { + var x = default(YCbCr); + var y = new YCbCr(Vector3.One); + + Assert.True(default(YCbCr) == default(YCbCr)); + Assert.False(default(YCbCr) != default(YCbCr)); + Assert.Equal(default(YCbCr), default(YCbCr)); + Assert.Equal(new YCbCr(1, 0, 1), new YCbCr(1, 0, 1)); + Assert.Equal(new YCbCr(Vector3.One), new YCbCr(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + } +} diff --git a/tests/ImageSharp.Tests/Common/EncoderExtensionsTests.cs b/tests/ImageSharp.Tests/Common/EncoderExtensionsTests.cs new file mode 100644 index 0000000000..e1b4fc790c --- /dev/null +++ b/tests/ImageSharp.Tests/Common/EncoderExtensionsTests.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Text; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Common +{ + public class EncoderExtensionsTests + { + [Fact] + public void GetString_EmptyBuffer_ReturnsEmptyString() + { + var buffer = new ReadOnlySpan(); + + string result = Encoding.UTF8.GetString(buffer); + + Assert.Equal(string.Empty, result); + } + + [Fact] + public void GetString_Buffer_ReturnsString() + { + var buffer = new ReadOnlySpan(new byte[] { 73, 109, 97, 103, 101, 83, 104, 97, 114, 112 }); + + string result = Encoding.UTF8.GetString(buffer); + + Assert.Equal("ImageSharp", result); + } + } +} diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs index c6c3b68f33..4f8a2cdaf7 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Common { float[] data = new float[Vector.Count]; - var rnd = new Random(); + var rnd = new Random(seed); for (int i = 0; i < Vector.Count; i++) { @@ -118,7 +118,7 @@ namespace SixLabors.ImageSharp.Tests.Common [InlineData(1, 8)] [InlineData(2, 16)] [InlineData(3, 128)] - public void BulkConvertNormalizedFloatToByte_WithRoundedData(int seed, int count) + public void BasicIntrinsics256_BulkConvertNormalizedFloatToByte_WithRoundedData(int seed, int count) { if (this.SkipOnNonAvx2()) { @@ -130,7 +130,7 @@ namespace SixLabors.ImageSharp.Tests.Common byte[] dest = new byte[count]; - SimdUtils.BulkConvertNormalizedFloatToByte(normalized, dest); + SimdUtils.BasicIntrinsics256.BulkConvertNormalizedFloatToByte(normalized, dest); byte[] expected = orig.Select(f => (byte)(f)).ToArray(); @@ -142,7 +142,7 @@ namespace SixLabors.ImageSharp.Tests.Common [InlineData(1, 8)] [InlineData(2, 16)] [InlineData(3, 128)] - public void BulkConvertNormalizedFloatToByte_WithNonRoundedData(int seed, int count) + public void BasicIntrinsics256_BulkConvertNormalizedFloatToByte_WithNonRoundedData(int seed, int count) { if (this.SkipOnNonAvx2()) { @@ -153,39 +153,168 @@ namespace SixLabors.ImageSharp.Tests.Common byte[] dest = new byte[count]; - SimdUtils.BulkConvertNormalizedFloatToByte(source, dest); + SimdUtils.BasicIntrinsics256.BulkConvertNormalizedFloatToByte(source, dest); byte[] expected = source.Select(f => (byte)Math.Round(f * 255f)).ToArray(); Assert.Equal(expected, dest); } - private static float Clamp255(float x) => Math.Min(255f, Math.Max(0f, x)); + public static readonly TheoryData ArraySizesDivisibleBy8 = new TheoryData { 0, 8, 16, 1024 }; + public static readonly TheoryData ArraySizesDivisibleBy4 = new TheoryData { 0, 4, 8, 28, 1020 }; + + public static readonly TheoryData ArraySizesDivisibleBy32 = new TheoryData { 0, 32, 512 }; + + public static readonly TheoryData ArbitraryArraySizes = + new TheoryData + { + 0, 1, 2, 3, 4, 7, 8, 9, 15, 16, 17, 63, 64, 255, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 520, + }; [Theory] - [InlineData(1, 0)] - [InlineData(1, 8)] - [InlineData(2, 16)] - [InlineData(3, 128)] - public void BulkConvertNormalizedFloatToByteClampOverflows(int seed, int count) + [MemberData(nameof(ArraySizesDivisibleBy4))] + public void FallbackIntrinsics128_BulkConvertByteToNormalizedFloat(int count) + { + TestImpl_BulkConvertByteToNormalizedFloat( + count, + (s, d) => SimdUtils.FallbackIntrinsics128.BulkConvertByteToNormalizedFloat(s.Span, d.Span)); + } + + [Theory] + [MemberData(nameof(ArraySizesDivisibleBy8))] + public void BasicIntrinsics256_BulkConvertByteToNormalizedFloat(int count) { if (this.SkipOnNonAvx2()) { return; } - float[] orig = new Random(seed).GenerateRandomRoundedFloatArray(count, -50, 444); - float[] normalized = orig.Select(f => f / 255f).ToArray(); + TestImpl_BulkConvertByteToNormalizedFloat( + count, + (s, d) => SimdUtils.BasicIntrinsics256.BulkConvertByteToNormalizedFloat(s.Span, d.Span)); + } + + [Theory] + [MemberData(nameof(ArraySizesDivisibleBy32))] + public void ExtendedIntrinsics_BulkConvertByteToNormalizedFloat(int count) + { + TestImpl_BulkConvertByteToNormalizedFloat( + count, + (s, d) => SimdUtils.ExtendedIntrinsics.BulkConvertByteToNormalizedFloat(s.Span, d.Span)); + } - byte[] dest = new byte[count]; + [Theory] + [MemberData(nameof(ArbitraryArraySizes))] + public void BulkConvertByteToNormalizedFloat(int count) + { + TestImpl_BulkConvertByteToNormalizedFloat( + count, + (s, d) => SimdUtils.BulkConvertByteToNormalizedFloat(s.Span, d.Span)); + } - SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows(normalized, dest); + private static void TestImpl_BulkConvertByteToNormalizedFloat( + int count, + Action, Memory> convert) + { + byte[] source = new Random(count).GenerateRandomByteArray(count); + float[] result = new float[count]; + float[] expected = source.Select(b => (float)b / 255f).ToArray(); - byte[] expected = orig.Select(f => (byte)Clamp255(f)).ToArray(); + convert(source, result); - Assert.Equal(expected, dest); + Assert.Equal(expected, result, new ApproximateFloatComparer(1e-5f)); } + [Theory] + [MemberData(nameof(ArraySizesDivisibleBy4))] + public void FallbackIntrinsics128_BulkConvertNormalizedFloatToByteClampOverflows(int count) + { + TestImpl_BulkConvertNormalizedFloatToByteClampOverflows(count, + (s, d) => SimdUtils.FallbackIntrinsics128.BulkConvertNormalizedFloatToByteClampOverflows(s.Span, d.Span) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesDivisibleBy8))] + public void BasicIntrinsics256_BulkConvertNormalizedFloatToByteClampOverflows(int count) + { + if (this.SkipOnNonAvx2()) + { + return; + } + + TestImpl_BulkConvertNormalizedFloatToByteClampOverflows(count, + (s, d) => SimdUtils.BasicIntrinsics256.BulkConvertNormalizedFloatToByteClampOverflows(s.Span, d.Span) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesDivisibleBy32))] + public void ExtendedIntrinsics_BulkConvertNormalizedFloatToByteClampOverflows(int count) + { + TestImpl_BulkConvertNormalizedFloatToByteClampOverflows(count, + (s, d) => SimdUtils.ExtendedIntrinsics.BulkConvertNormalizedFloatToByteClampOverflows(s.Span, d.Span) + ); + } + + [Theory] + [InlineData(1234)] + public void ExtendedIntrinsics_ConvertToSingle(short scale) + { + int n = Vector.Count; + short[] sData = new Random(scale).GenerateRandomInt16Array(2 * n, (short)-scale, scale); + float[] fData = sData.Select(u => (float)u).ToArray(); + + var source = new Vector(sData); + + var expected1 = new Vector(fData, 0); + var expected2 = new Vector(fData, n); + + // Act: + SimdUtils.ExtendedIntrinsics.ConvertToSingle(source, out Vector actual1, out Vector actual2); + + // Assert: + Assert.Equal(expected1, actual1); + Assert.Equal(expected2, actual2); + } + + [Theory] + [MemberData(nameof(ArbitraryArraySizes))] + public void BulkConvertNormalizedFloatToByteClampOverflows(int count) + { + TestImpl_BulkConvertNormalizedFloatToByteClampOverflows(count, + (s, d) => SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows(s.Span, d.Span) + ); + + // for small values, let's stress test the implementation a bit: + if (count > 0 && count < 10) + { + for (int i = 0; i < 20; i++) + { + TestImpl_BulkConvertNormalizedFloatToByteClampOverflows( + count, + (s, d) => SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows(s.Span, d.Span), + i + 42); + } + } + } + + private static void TestImpl_BulkConvertNormalizedFloatToByteClampOverflows( + int count, + Action, Memory> convert, int seed = -1) + { + seed = seed > 0 ? seed : count; + float[] source = new Random(seed).GenerateRandomFloatArray(count, -0.2f, 1.2f); + byte[] expected = source.Select(NormalizedFloatToByte).ToArray(); + byte[] actual = new byte[count]; + + convert(source, actual); + + Assert.Equal(expected, actual); + } + + private static byte NormalizedFloatToByte(float f) => (byte)Math.Min(255f, Math.Max(0f, f * 255f + 0.5f)); + [Theory] [InlineData(0)] [InlineData(7)] @@ -211,7 +340,7 @@ namespace SixLabors.ImageSharp.Tests.Common float[] source = { 0, 7, 42, 255, 0.5f, 1.1f, 2.6f, 16f }; - var expected = source.Select(f => (byte)Math.Round(f)).ToArray(); + byte[] expected = source.Select(f => (byte)Math.Round(f)).ToArray(); source = source.Select(f => f / 255f).ToArray(); @@ -245,8 +374,6 @@ namespace SixLabors.ImageSharp.Tests.Common iiRef = x; - //Tuple8.OfUInt32 ii = Unsafe.As, Tuple8.OfUInt32>(ref x); - ref Tuple8.OfByte d = ref MemoryMarshal.Cast(dest)[0]; d.LoadFrom(ref ii); diff --git a/tests/ImageSharp.Tests/ComplexIntegrationTests.cs b/tests/ImageSharp.Tests/ComplexIntegrationTests.cs deleted file mode 100644 index a260ec33ca..0000000000 --- a/tests/ImageSharp.Tests/ComplexIntegrationTests.cs +++ /dev/null @@ -1,35 +0,0 @@ -using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.Primitives; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests -{ - /// - /// Might be useful to catch complex bugs - /// - public class ComplexIntegrationTests - { - [Theory] - [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 - { - using (Image image = provider.GetImage(x => x.Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max }))) - { - - image.MetaData.ExifProfile = null; // Reduce the size of the file - JpegEncoder options = new JpegEncoder { Subsample = subsample, Quality = quality }; - - provider.Utility.TestName += $"{subsample}_Q{quality}"; - provider.Utility.SaveTestOutputFile(image, "png"); - provider.Utility.SaveTestOutputFile(image, "jpg", options); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index 963d674466..208387e6d1 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -6,6 +6,7 @@ using System.Linq; using Moq; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.IO; + using Xunit; // ReSharper disable InconsistentNaming diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs index 496692d969..374454afba 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs @@ -2,10 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Numerics; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.Primitives; using Xunit; @@ -75,21 +73,15 @@ namespace SixLabors.ImageSharp.Tests using (Image image = provider.GetImage()) using (var blend = Image.Load(TestFile.Create(TestImages.Bmp.Car).Bytes)) { - Matrix3x2 rotate = Matrix3x2Extensions.CreateRotationDegrees(45F); - Matrix3x2 scale = Matrix3x2Extensions.CreateScale(new SizeF(.25F, .25F)); - Matrix3x2 matrix = rotate * scale; + AffineTransformBuilder builder = new AffineTransformBuilder() + .AppendRotationDegrees(45F) + .AppendScale(new SizeF(.25F, .25F)) + .AppendTranslation(new PointF(10, 10)); - // Lets center the matrix so we can tell whether any cut-off issues we may have belong to the drawing processor - Rectangle srcBounds = blend.Bounds(); - Rectangle destBounds = TransformHelpers.GetTransformedBoundingRectangle(srcBounds, matrix); - Matrix3x2 centeredMatrix = TransformHelpers.GetCenteredTransformMatrix(srcBounds, destBounds, matrix); - - // We pass a new rectangle here based on the dest bounds since we've offset the matrix - blend.Mutate(x => x.Transform( - centeredMatrix, - KnownResamplers.Bicubic, - new Rectangle(0, 0, destBounds.Width, destBounds.Height))); + // Apply a background color so we can see the translation. + blend.Mutate(x => x.Transform(builder).BackgroundColor(NamedColors.HotPink)); + // Lets center the matrix so we can tell whether any cut-off issues we may have belong to the drawing processor var position = new Point((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2); image.Mutate(x => x.DrawImage(blend, position, mode, .75F)); image.DebugSave(provider, new[] { "Transformed" }); diff --git a/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs index 3522ade7c4..556ec9c9ca 100644 --- a/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillLinearGradientBrushTests.cs @@ -283,10 +283,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing { // it's diagonal, so for any (a, a) on the gradient line, for all (a-x, b+x) - +/- depending on the diagonal direction - must be the same color) TPixel colorOnDiagonal = image[i, i]; + + // TODO: This is incorrect. from -0 to < 0 ?? int orthoCount = 0; for (int offset = -orthoCount; offset < orthoCount; offset++) { - Assert.Equal(colorOnDiagonal, image[i + horizontalSign * offset, i + verticalSign * offset]); + Assert.Equal(colorOnDiagonal, image[i + (horizontalSign * offset), i + (verticalSign * offset)]); } } @@ -302,8 +304,8 @@ namespace SixLabors.ImageSharp.Tests.Drawing [Theory] [WithBlankImages(500, 500, PixelTypes.Rgba32, 0, 0, 499, 499, new[] { 0f, .2f, .5f, .9f }, new[] { 0, 0, 1, 1 })] [WithBlankImages(500, 500, PixelTypes.Rgba32, 0, 499, 499, 0, new[] { 0f, 0.2f, 0.5f, 0.9f }, new[] { 0, 1, 2, 3 })] - [WithBlankImages(500, 500, PixelTypes.Rgba32, 499, 499, 0, 0, new[] { 0f, 0.7f, 0.8f, 0.9f}, new[] { 0, 1, 2, 0 })] - [WithBlankImages(500, 500, PixelTypes.Rgba32, 0, 0, 499, 499, new[] { 0f, .5f, 1f}, new[]{0, 1, 3})] + [WithBlankImages(500, 500, PixelTypes.Rgba32, 499, 499, 0, 0, new[] { 0f, 0.7f, 0.8f, 0.9f }, new[] { 0, 1, 2, 0 })] + [WithBlankImages(500, 500, PixelTypes.Rgba32, 0, 0, 499, 499, new[] { 0f, .5f, 1f }, new[] { 0, 1, 3 })] public void ArbitraryGradients( TestImageProvider provider, int startX, int startY, @@ -312,35 +314,38 @@ namespace SixLabors.ImageSharp.Tests.Drawing int[] stopColorCodes) where TPixel : struct, IPixel { - TPixel[] colors = { - NamedColors.Navy, NamedColors.LightGreen, NamedColors.Yellow, - NamedColors.Red - }; + TPixel[] colors = + { + NamedColors.Navy, NamedColors.LightGreen, NamedColors.Yellow, + NamedColors.Red + }; var coloringVariant = new StringBuilder(); - ColorStop[] colorStops = new ColorStop[stopPositions.Length]; + var colorStops = new ColorStop[stopPositions.Length]; + for (int i = 0; i < stopPositions.Length; i++) { TPixel color = colors[stopColorCodes[i % colors.Length]]; float position = stopPositions[i]; - colorStops[i] = new ColorStop(position, color); - coloringVariant.AppendFormat(CultureInfo.InvariantCulture, "{0}@{1};", color, position); + Rgba32 rgba = default; + color.ToRgba32(ref rgba); + coloringVariant.AppendFormat(CultureInfo.InvariantCulture, "{0}@{1};", rgba.ToHex(), position); } FormattableString variant = $"({startX},{startY})_TO_({endX},{endY})__[{coloringVariant}]"; provider.VerifyOperation( image => - { - var unicolorLinearGradientBrush = new LinearGradientBrush( - new SixLabors.Primitives.Point(startX, startY), - new SixLabors.Primitives.Point(endX, endY), - GradientRepetitionMode.None, - colorStops); + { + var unicolorLinearGradientBrush = new LinearGradientBrush( + new SixLabors.Primitives.Point(startX, startY), + new SixLabors.Primitives.Point(endX, endY), + GradientRepetitionMode.None, + colorStops); - image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); - }, + image.Mutate(x => x.Fill(unicolorLinearGradientBrush)); + }, variant, false, false); diff --git a/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs b/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs index 93715c586e..b310c7afc6 100644 --- a/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillPatternTests.cs @@ -54,172 +54,225 @@ namespace SixLabors.ImageSharp.Tests.Drawing [Fact] public void ImageShouldBeFloodFilledWithPercent10() { - this.Test("Percent10", Rgba32.Blue, Brushes.Percent10(Rgba32.HotPink, Rgba32.LimeGreen), + this.Test( + "Percent10", + Rgba32.Blue, + Brushes.Percent10(Rgba32.HotPink, Rgba32.LimeGreen), new[,] - { - { Rgba32.HotPink , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink , Rgba32.LimeGreen}, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen} - }); + { + { Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen }, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen } + }); } [Fact] public void ImageShouldBeFloodFilledWithPercent10Transparent() { - Test("Percent10_Transparent", Rgba32.Blue, Brushes.Percent10(Rgba32.HotPink), - new Rgba32[,] { - { Rgba32.HotPink , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, - { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, - { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink , Rgba32.Blue}, - { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue} - }); + this.Test( + "Percent10_Transparent", + Rgba32.Blue, + Brushes.Percent10(Rgba32.HotPink), + new Rgba32[,] + { + { Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, + { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, + { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue }, + { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue } + }); } [Fact] public void ImageShouldBeFloodFilledWithPercent20() { - Test("Percent20", Rgba32.Blue, Brushes.Percent20(Rgba32.HotPink, Rgba32.LimeGreen), - new Rgba32[,] { - { Rgba32.HotPink , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink , Rgba32.LimeGreen}, - { Rgba32.HotPink , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink , Rgba32.LimeGreen} - }); + this.Test( + "Percent20", + Rgba32.Blue, + Brushes.Percent20(Rgba32.HotPink, Rgba32.LimeGreen), + new Rgba32[,] + { + { Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen }, + { Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen } + }); } [Fact] public void ImageShouldBeFloodFilledWithPercent20_transparent() { - Test("Percent20_Transparent", Rgba32.Blue, Brushes.Percent20(Rgba32.HotPink), - new Rgba32[,] { - { Rgba32.HotPink , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, - { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink , Rgba32.Blue}, - { Rgba32.HotPink , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, - { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink , Rgba32.Blue} - }); + this.Test( + "Percent20_Transparent", + Rgba32.Blue, + Brushes.Percent20(Rgba32.HotPink), + new Rgba32[,] + { + { Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, + { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue }, + { Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, + { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue } + }); } [Fact] public void ImageShouldBeFloodFilledWithHorizontal() { - Test("Horizontal", Rgba32.Blue, Brushes.Horizontal(Rgba32.HotPink, Rgba32.LimeGreen), - new Rgba32[,] { - { Rgba32.LimeGreen , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, - { Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink , Rgba32.HotPink}, - { Rgba32.LimeGreen , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen , Rgba32.LimeGreen} - }); + this.Test( + "Horizontal", + Rgba32.Blue, + Brushes.Horizontal(Rgba32.HotPink, Rgba32.LimeGreen), + new Rgba32[,] + { + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, + { Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink }, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen } + }); } [Fact] public void ImageShouldBeFloodFilledWithHorizontal_transparent() { - Test("Horizontal_Transparent", Rgba32.Blue, Brushes.Horizontal(Rgba32.HotPink), - new Rgba32[,] { - { Rgba32.Blue , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, - { Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink , Rgba32.HotPink}, - { Rgba32.Blue , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, - { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue , Rgba32.Blue} - }); + this.Test( + "Horizontal_Transparent", + Rgba32.Blue, + Brushes.Horizontal(Rgba32.HotPink), + new Rgba32[,] + { + { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, + { Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink }, + { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, + { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue } + }); } - - [Fact] public void ImageShouldBeFloodFilledWithMin() { - Test("Min", Rgba32.Blue, Brushes.Min(Rgba32.HotPink, Rgba32.LimeGreen), - new Rgba32[,] { - { Rgba32.LimeGreen , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, - { Rgba32.LimeGreen , Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen , Rgba32.LimeGreen}, - { Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink , Rgba32.HotPink} - }); + this.Test( + "Min", + Rgba32.Blue, + Brushes.Min(Rgba32.HotPink, Rgba32.LimeGreen), + new Rgba32[,] + { + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, + { Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink } + }); } [Fact] public void ImageShouldBeFloodFilledWithMin_transparent() { - Test("Min_Transparent", Rgba32.Blue, Brushes.Min(Rgba32.HotPink), - new Rgba32[,] { - { Rgba32.Blue , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, - { Rgba32.Blue , Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, - { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue , Rgba32.Blue}, - { Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink , Rgba32.HotPink}, - }); + this.Test( + "Min_Transparent", + Rgba32.Blue, + Brushes.Min(Rgba32.HotPink), + new Rgba32[,] + { + { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, + { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, + { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, + { Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink, Rgba32.HotPink }, + }); } [Fact] public void ImageShouldBeFloodFilledWithVertical() { - Test("Vertical", Rgba32.Blue, Brushes.Vertical(Rgba32.HotPink, Rgba32.LimeGreen), - new Rgba32[,] { - { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen}, - { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen}, - { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen}, - { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen} - }); + this.Test( + "Vertical", + Rgba32.Blue, + Brushes.Vertical(Rgba32.HotPink, Rgba32.LimeGreen), + new Rgba32[,] + { + { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen }, + { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen }, + { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen }, + { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen } + }); } [Fact] public void ImageShouldBeFloodFilledWithVertical_transparent() { - Test("Vertical_Transparent", Rgba32.Blue, Brushes.Vertical(Rgba32.HotPink), - new Rgba32[,] { - { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue}, - { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue}, - { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue}, - { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue} - }); + this.Test( + "Vertical_Transparent", + Rgba32.Blue, + Brushes.Vertical(Rgba32.HotPink), + new Rgba32[,] + { + { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue }, + { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue }, + { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue }, + { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue } + }); } [Fact] public void ImageShouldBeFloodFilledWithForwardDiagonal() { - Test("ForwardDiagonal", Rgba32.Blue, Brushes.ForwardDiagonal(Rgba32.HotPink, Rgba32.LimeGreen), - new Rgba32[,] { - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink}, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen}, - { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen}, - { Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen} - }); + this.Test( + "ForwardDiagonal", + Rgba32.Blue, + Brushes.ForwardDiagonal(Rgba32.HotPink, Rgba32.LimeGreen), + new Rgba32[,] + { + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink }, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen }, + { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen }, + { Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen } + }); } [Fact] public void ImageShouldBeFloodFilledWithForwardDiagonal_transparent() { - Test("ForwardDiagonal_Transparent", Rgba32.Blue, Brushes.ForwardDiagonal(Rgba32.HotPink), - new Rgba32[,] { - { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink}, - { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue}, - { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue}, - { Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue} - }); + this.Test( + "ForwardDiagonal_Transparent", + Rgba32.Blue, + Brushes.ForwardDiagonal(Rgba32.HotPink), + new Rgba32[,] + { + { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink }, + { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue }, + { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue }, + { Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue } + }); } [Fact] public void ImageShouldBeFloodFilledWithBackwardDiagonal() { - Test("BackwardDiagonal", Rgba32.Blue, Brushes.BackwardDiagonal(Rgba32.HotPink, Rgba32.LimeGreen), - new Rgba32[,] { - { Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen}, - { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen}, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen}, - { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink} - }); + this.Test( + "BackwardDiagonal", + Rgba32.Blue, + Brushes.BackwardDiagonal(Rgba32.HotPink, Rgba32.LimeGreen), + new Rgba32[,] + { + { Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen }, + { Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen, Rgba32.LimeGreen }, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink, Rgba32.LimeGreen }, + { Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.LimeGreen, Rgba32.HotPink } + }); } [Fact] public void ImageShouldBeFloodFilledWithBackwardDiagonal_transparent() { - Test("BackwardDiagonal_Transparent", Rgba32.Blue, Brushes.BackwardDiagonal(Rgba32.HotPink), - new Rgba32[,] { - { Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue}, - { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue}, - { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue}, - { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink} - }); + this.Test( + "BackwardDiagonal_Transparent", + Rgba32.Blue, + Brushes.BackwardDiagonal(Rgba32.HotPink), + new Rgba32[,] + { + { Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue, Rgba32.Blue }, + { Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue, Rgba32.Blue }, + { Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink, Rgba32.Blue }, + { Rgba32.Blue, Rgba32.Blue, Rgba32.Blue, Rgba32.HotPink } + }); } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs index e86d41f574..639b3fe81a 100644 --- a/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillSolidBrushTests.cs @@ -1,20 +1,21 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; + using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Primitives; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.Primitives; using SixLabors.Shapes; + using Xunit; + // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Drawing { - using System; - - using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - using SixLabors.Primitives; - [GroupOutput("Drawing")] public class FillSolidBrushTests { @@ -55,7 +56,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing [Theory] [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, "Blue")] [WithSolidFilledImages(16, 16, "Yellow", PixelTypes.Rgba32, "Khaki")] - public void WhenColorIsOpaque_OverridePreviousColor(TestImageProvider provider, string newColorName) + public void WhenColorIsOpaque_OverridePreviousColor( + TestImageProvider provider, + string newColorName) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) @@ -63,7 +66,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing TPixel color = TestUtils.GetPixelOfNamedColor(newColorName); image.Mutate(c => c.Fill(color)); - image.DebugSave(provider, newColorName, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); + image.DebugSave( + provider, + newColorName, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); image.ComparePixelBufferTo(color); } } @@ -84,7 +91,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing [Theory] [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 5, 7, 3, 8)] [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 8, 5, 6, 4)] - public void FillRegion_WorksOnWrappedMemoryImage(TestImageProvider provider, int x0, int y0, int w, int h) + public void FillRegion_WorksOnWrappedMemoryImage( + TestImageProvider provider, + int x0, + int y0, + int w, + int h) where TPixel : struct, IPixel { FormattableString testDetails = $"(x{x0},y{y0},w{w},h{h})"; @@ -105,27 +117,22 @@ namespace SixLabors.ImageSharp.Tests.Drawing { false, "Blue", 1.0f, PixelColorBlendingMode.Normal, 0.5f }, { false, "Green", 0.5f, PixelColorBlendingMode.Normal, 0.3f }, { false, "HotPink", 0.8f, PixelColorBlendingMode.Normal, 0.8f }, - { false, "Blue", 0.5f, PixelColorBlendingMode.Multiply, 1.0f }, { false, "Blue", 1.0f, PixelColorBlendingMode.Multiply, 0.5f }, { false, "Green", 0.5f, PixelColorBlendingMode.Multiply, 0.3f }, { false, "HotPink", 0.8f, PixelColorBlendingMode.Multiply, 0.8f }, - { false, "Blue", 0.5f, PixelColorBlendingMode.Add, 1.0f }, { false, "Blue", 1.0f, PixelColorBlendingMode.Add, 0.5f }, { false, "Green", 0.5f, PixelColorBlendingMode.Add, 0.3f }, { false, "HotPink", 0.8f, PixelColorBlendingMode.Add, 0.8f }, - { true, "Blue", 0.5f, PixelColorBlendingMode.Normal, 1.0f }, { true, "Blue", 1.0f, PixelColorBlendingMode.Normal, 0.5f }, { true, "Green", 0.5f, PixelColorBlendingMode.Normal, 0.3f }, { true, "HotPink", 0.8f, PixelColorBlendingMode.Normal, 0.8f }, - { true, "Blue", 0.5f, PixelColorBlendingMode.Multiply, 1.0f }, { true, "Blue", 1.0f, PixelColorBlendingMode.Multiply, 0.5f }, { true, "Green", 0.5f, PixelColorBlendingMode.Multiply, 0.3f }, { true, "HotPink", 0.8f, PixelColorBlendingMode.Multiply, 0.8f }, - { true, "Blue", 0.5f, PixelColorBlendingMode.Add, 1.0f }, { true, "Blue", 1.0f, PixelColorBlendingMode.Add, 0.5f }, { true, "Green", 0.5f, PixelColorBlendingMode.Add, 0.3f }, @@ -147,7 +154,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing vec.W = alpha; TPixel fillColor = default; - fillColor.PackFromVector4(vec); + fillColor.FromVector4(vec); using (Image image = provider.GetImage()) { @@ -155,8 +162,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing var options = new GraphicsOptions(false) { - ColorBlendingMode = blenderMode, - BlendPercentage = blendPercentage + ColorBlendingMode = blenderMode, BlendPercentage = blendPercentage }; if (triggerFillRegion) @@ -185,11 +191,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); - PixelBlender blender = PixelOperations.Instance.GetPixelBlender(blenderMode, PixelAlphaCompositionMode.SrcOver); + PixelBlender blender = PixelOperations.Instance.GetPixelBlender( + blenderMode, + PixelAlphaCompositionMode.SrcOver); TPixel expectedPixel = blender.Blend(bgColor, fillColor, blendPercentage); image.ComparePixelBufferTo(expectedPixel); } } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs index a8fb187ced..94e12f8581 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs @@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing var c = NamedColors.Red.ToVector4(); c.W *= 0.5f; var pixel = default(TPixel); - pixel.PackFromVector4(c); + pixel.FromVector4(c); img.Mutate( x => x.Fill( diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs index 44bb160ce3..20ccb25a54 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs @@ -11,6 +11,8 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.Primitives; using Xunit; +using Xunit.Abstractions; + // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Drawing.Text @@ -22,8 +24,50 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text private const string TestText = "Sphinx of black quartz, judge my vow\n0123456789"; - public static ImageComparer TextDrawingComparer = ImageComparer.TolerantPercentage(0.01f); - public static ImageComparer OutlinedTextDrawingComparer = ImageComparer.TolerantPercentage(0.5f, 3); + public static ImageComparer TextDrawingComparer = ImageComparer.TolerantPercentage(1e-5f); + public static ImageComparer OutlinedTextDrawingComparer = ImageComparer.TolerantPercentage(5e-4f); + + public DrawTextOnImageTests(ITestOutputHelper output) + { + this.Output = output; + } + + private ITestOutputHelper Output { get; } + + [Theory] + [WithSolidFilledImages(276, 336, "White", PixelTypes.Rgba32)] + public void DoesntThrowExceptionWhenOverlappingRightEdge_Issue688(TestImageProvider provider) + where TPixel : struct, IPixel + { + Font font = CreateFont("OpenSans-Regular.ttf", 36); + TPixel color = NamedColors.Black; + float padding = 5; + var text = "A short piece of text"; + + using (var img = provider.GetImage()) + { + float targetWidth = img.Width - (padding * 2); + float targetHeight = img.Height - (padding * 2); + + // measure the text size + SizeF size = TextMeasurer.Measure(text, new RendererOptions(font)); + + //find out how much we need to scale the text to fill the space (up or down) + float scalingFactor = Math.Min(img.Width / size.Width, img.Height / size.Height); + + //create a new font + Font scaledFont = new Font(font, scalingFactor * font.Size); + + var center = new PointF(img.Width / 2, img.Height / 2); + var textGraphicOptions = new TextGraphicsOptions(true) + { + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Center + }; + + img.Mutate(i => i.DrawText(textGraphicOptions, text, scaledFont, color, center)); + } + } [Theory] [WithSolidFilledImages(200, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "SixLaborsSampleAB.woff", AB)] @@ -87,8 +131,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text TPixel color = NamedColors.Black; + // Based on the reported 0.0270% difference with AccuracyMultiple = 8 + // We should avoid quality regressions leading to higher difference! + var comparer = ImageComparer.TolerantPercentage(0.03f); + provider.VerifyOperation( - TextDrawingComparer, + comparer, img => { img.Mutate(c => c.DrawText(textOptions, sb.ToString(), font, color, new PointF(10, 5))); @@ -151,6 +199,31 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text appendSourceFileOrDescription: true); } + [Theory] + [WithSolidFilledImages(1000, 1500, "White", PixelTypes.Rgba32, "OpenSans-Regular.ttf")] + public void TextPositioningIsRobust(TestImageProvider provider, string fontName) + where TPixel : struct, IPixel + { + Font font = CreateFont(fontName, 30); + + string text = Repeat("Beware the Jabberwock, my son! The jaws that bite, the claws that catch! Beware the Jubjub bird, and shun The frumious Bandersnatch!\n", + 20); + var textOptions = new TextGraphicsOptions(true) { WrapTextWidth = 1000 }; + + string details = fontName.Replace(" ", ""); + + // Based on the reported 0.1755% difference with AccuracyMultiple = 8 + // We should avoid quality regressions leading to higher difference! + var comparer = ImageComparer.TolerantPercentage(0.2f); + + provider.RunValidatingProcessorTest( + x => x.DrawText(textOptions, text, font, NamedColors.Black, new PointF(10, 50)), + details, + comparer, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + } + private static string Repeat(string str, int times) => string.Concat(Enumerable.Repeat(str, times)); private static string ToTestOutputDisplayText(string text) @@ -167,5 +240,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text Font font = fontCollection.Install(fontPath).CreateFont(size); return font; } + + } } diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index f8b035ca83..158a085d5a 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -14,10 +14,9 @@ namespace SixLabors.ImageSharp.Tests { using System; using System.Reflection; - - using SixLabors.Memory; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; + using SixLabors.Memory; public class GeneralFormatTests : FileTestBase { @@ -58,7 +57,8 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = file.CreateImage()) { - image.Save($"{path}/{file.FileName}"); + var encoder = new PngEncoder { Quantizer = new WuQuantizer(KnownDiffusers.JarvisJudiceNinke, 256), ColorType = PngColorType.Palette }; + image.Save($"{path}/{file.FileName}.png", encoder); } } } @@ -67,7 +67,8 @@ namespace SixLabors.ImageSharp.Tests new TheoryData { nameof(KnownQuantizers.Octree), - nameof(KnownQuantizers.Palette), + nameof(KnownQuantizers.WebSafe), + nameof(KnownQuantizers.Werner), nameof(KnownQuantizers.Wu) }; @@ -135,15 +136,15 @@ namespace SixLabors.ImageSharp.Tests foreach (TestFile file in Files) { byte[] serialized; - using (Image image = Image.Load(file.Bytes, out IImageFormat mimeType)) - using (MemoryStream memoryStream = new MemoryStream()) + using (var image = Image.Load(file.Bytes, out IImageFormat mimeType)) + using (var memoryStream = new MemoryStream()) { image.Save(memoryStream, mimeType); memoryStream.Flush(); serialized = memoryStream.ToArray(); } - using (Image image2 = Image.Load(serialized)) + using (var image2 = Image.Load(serialized)) { image2.Save($"{path}/{file.FileName}"); } @@ -169,14 +170,14 @@ namespace SixLabors.ImageSharp.Tests [InlineData(10, 100, "jpg")] public void CanIdentifyImageLoadedFromBytes(int width, int height, string format) { - using (Image image = Image.LoadPixelData(new Rgba32[width * height], width, height)) + using (var image = Image.LoadPixelData(new Rgba32[width * height], width, height)) { using (var memoryStream = new MemoryStream()) { image.Save(memoryStream, GetEncoder(format)); memoryStream.Position = 0; - var imageInfo = Image.Identify(memoryStream); + IImageInfo imageInfo = Image.Identify(memoryStream); Assert.Equal(imageInfo.Width, width); Assert.Equal(imageInfo.Height, height); diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 6d2a74c03b..5bfbb058be 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -1,20 +1,20 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; +using System.Collections.Generic; +using System.IO; using System.Text; +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; -using System.IO; -using SixLabors.ImageSharp.Advanced; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Formats.Gif { - using System.Collections.Generic; - using SixLabors.ImageSharp.MetaData; - using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - public class GifDecoderTests { private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; @@ -70,6 +70,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif } } + [Fact] + public unsafe void Decode_NonTerminatedFinalFrame() + { + var testFile = TestFile.Create(TestImages.Gif.Rings); + + int length = testFile.Bytes.Length - 2; + + fixed (byte* data = testFile.Bytes.AsSpan(0, length)) + { + using (var stream = new UnmanagedMemoryStream(data, length)) + { + var decoder = new GifDecoder(); + + using (Image image = decoder.Decode(Configuration.Default, stream)) + { + Assert.Equal((200, 200), (image.Width, image.Height)); + } + } + } + } + [Theory] [MemberData(nameof(RatioFiles))] public void Decode_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index c5c971962c..b74a7a85f9 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif public class GifEncoderTests { private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; - private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.001F); + private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0015F); public static readonly TheoryData RatioFiles = new TheoryData @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif { // Use the palette quantizer without dithering to ensure results // are consistant - Quantizer = new PaletteQuantizer(false) + Quantizer = new WebSafePaletteQuantizer(false) }; // Always save as we need to compare the encoded output. diff --git a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs index 464b1564b8..c2100c302f 100644 --- a/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs +++ b/tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs @@ -2,17 +2,15 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; using System.IO; using System.Linq; +using Moq; using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Gif; -using Moq; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -27,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests { this.DefaultFormatsManager = Configuration.CreateDefaultInstance().ImageFormatsManager; this.FormatsManagerEmpty = new ImageFormatManager(); - } + } [Fact] public void IfAutoloadWellKnownFormatsIsTrueAllFormatsAreLoaded() @@ -114,7 +112,7 @@ namespace SixLabors.ImageSharp.Tests IImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); Assert.Equal(decoder2, found2); Assert.NotEqual(found, found2); - } + } [Fact] public void AddFormatCallsConfig() @@ -125,5 +123,24 @@ namespace SixLabors.ImageSharp.Tests provider.Verify(x => x.Configure(config)); } + + [Fact] + public void DetectFormatAllocatesCleanBuffer() + { + byte[] jpegImage; + using (var buffer = new MemoryStream()) + { + using (var image = new Image(100, 100)) + { + image.SaveAsJpeg(buffer); + jpegImage = buffer.ToArray(); + } + } + + byte[] invalidImage = { 1, 2, 3 }; + + Assert.Equal(Image.DetectFormat(jpegImage), JpegFormat.Instance); + Assert.True(Image.DetectFormat(invalidImage) is null); + } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs index c720fdd4a7..4b1abf9094 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.CopyToBufferArea.cs @@ -7,14 +7,16 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; +using SixLabors.Memory; using SixLabors.Primitives; using Xunit; using Xunit.Abstractions; +// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Formats.Jpg { - public partial class Block8x8FTests : JpegFixture + public partial class Block8x8FTests { public class CopyToBufferArea : JpegFixture { @@ -37,17 +39,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } } - // TODO: This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative. - [Fact(Skip = "This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.")] - //[Fact] - public void Unscaled() + [Fact] + public void Copy1x1Scale() { Block8x8F block = CreateRandomFloatBlock(0, 100); - using (var buffer = Configuration.Default.MemoryAllocator.Allocate2D(20, 20)) + using (Buffer2D buffer = Configuration.Default.MemoryAllocator.Allocate2D(20, 20, AllocationOptions.Clean)) { BufferArea area = buffer.GetArea(5, 10, 8, 8); - block.CopyTo(area); + block.Copy1x1Scale(area); Assert.Equal(block[0, 0], buffer[5, 10]); Assert.Equal(block[1, 0], buffer[6, 10]); @@ -59,22 +59,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } } - // TODO: This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative. - [Theory(Skip = "This test occasionally fails from the same reason certain ICC tests are failing. Should be false negative.")] - //[Theory] + [Theory] [InlineData(1, 1)] [InlineData(1, 2)] [InlineData(2, 1)] [InlineData(2, 2)] [InlineData(4, 2)] [InlineData(4, 4)] - public void Scaled(int horizontalFactor, int verticalFactor) + public void CopyTo(int horizontalFactor, int verticalFactor) { Block8x8F block = CreateRandomFloatBlock(0, 100); var start = new Point(50, 50); - using (var buffer = Configuration.Default.MemoryAllocator.Allocate2D(100, 100)) + using (Buffer2D buffer = Configuration.Default.MemoryAllocator.Allocate2D(100, 100, AllocationOptions.Clean)) { BufferArea area = buffer.GetArea(start.X, start.Y, 8 * horizontalFactor, 8 * verticalFactor); block.CopyTo(area, horizontalFactor, verticalFactor); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index e72f4945b7..81c76390c1 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -408,5 +408,47 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.Equal(original[i] * 42f, actual[i]); } } + + [Fact] + public void LoadFromUInt16Scalar() + { + if (this.SkipOnNonAvx2Runner()) + { + return; + } + + short[] data = Create8x8ShortData(); + + var source = new Block8x8(data); + + Block8x8F dest = default; + dest.LoadFromInt16Scalar(ref source); + + for (int i = 0; i < Block8x8F.Size; i++) + { + Assert.Equal((float)data[i], dest[i]); + } + } + + [Fact] + public void LoadFromUInt16ExtendedAvx2() + { + if (this.SkipOnNonAvx2Runner()) + { + return; + } + + short[] data = Create8x8ShortData(); + + var source = new Block8x8(data); + + Block8x8F dest = default; + dest.LoadFromInt16ExtendedAvx2(ref source); + + for (int i = 0; i < Block8x8F.Size; i++) + { + Assert.Equal((float)data[i], dest[i]); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs b/tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs index dedb094bc2..341d67f0f1 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/GenericBlock8x8Tests.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { var rgba = new Rgba32((byte)(i + 1), (byte)(j + 1), (byte)200, (byte)255); var color = default(TPixel); - color.PackFromRgba32(rgba); + color.FromRgba32(rgba); pixels[i, j] = color; } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index a80312766c..8e30eb9e5d 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -8,6 +8,7 @@ using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters; +using SixLabors.ImageSharp.Tests.Colorspaces.Conversion; using SixLabors.ImageSharp.Memory; using Xunit; @@ -17,7 +18,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { public class JpegColorConverterTests { - private const float Precision = 0.1f / 255; + private const float Precision = 0.1F / 255; + + private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(Precision); public static readonly TheoryData CommonConversionData = new TheoryData @@ -48,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg seed); } - private static void ValidateYCbCr(JpegColorConverter.ComponentValues values, Vector4[] result, int i) + private static void ValidateYCbCr(in JpegColorConverter.ComponentValues values, Vector4[] result, int i) { float y = values.Component0[i]; float cb = values.Component1[i]; @@ -59,7 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); var expected = ColorSpaceConverter.ToRgb(ycbcr); - Assert.True(actual.AlmostEquals(expected, Precision), $"{actual} != {expected}"); + Assert.Equal(expected, actual, ColorSpaceComparer); Assert.Equal(1, rgba.W); } @@ -182,7 +185,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); var expected = new Rgb(v.X, v.Y, v.Z); - Assert.True(actual.AlmostEquals(expected, Precision)); + Assert.Equal(expected, actual); Assert.Equal(1, rgba.W); } } @@ -204,7 +207,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); var expected = new Rgb(y / 255F, y / 255F, y / 255F); - Assert.True(actual.AlmostEquals(expected, Precision)); + Assert.Equal(expected, actual, ColorSpaceComparer); Assert.Equal(1, rgba.W); } } @@ -228,7 +231,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); var expected = new Rgb(r / 255F, g / 255F, b / 255F); - Assert.True(actual.AlmostEquals(expected, Precision)); + Assert.Equal(expected, actual, ColorSpaceComparer); Assert.Equal(1, rgba.W); } } @@ -266,7 +269,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var actual = new Rgb(rgba.X, rgba.Y, rgba.Z); var expected = new Rgb(v.X, v.Y, v.Z); - Assert.True(actual.AlmostEquals(expected, Precision)); + Assert.Equal(expected, actual, ColorSpaceComparer); Assert.Equal(1, rgba.W); } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs index a14c4735f5..40de25b30a 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs @@ -10,7 +10,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public static string[] BaselineTestJpegs = { TestImages.Jpeg.Baseline.Calliphora, - TestImages.Jpeg.Baseline.Cmyk, TestImages.Jpeg.Baseline.Ycck, + TestImages.Jpeg.Baseline.Cmyk, + TestImages.Jpeg.Baseline.Ycck, TestImages.Jpeg.Baseline.Jpeg400, TestImages.Jpeg.Baseline.Testorig420, @@ -19,13 +20,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Bad.BadEOF, - TestImages.Jpeg.Issues.MultiHuffmanBaseline394, TestImages.Jpeg.Baseline.MultiScanBaselineCMYK, TestImages.Jpeg.Baseline.Bad.BadRST, TestImages.Jpeg.Issues.MultiHuffmanBaseline394, TestImages.Jpeg.Issues.ExifDecodeOutOfRange694, TestImages.Jpeg.Issues.InvalidEOI695, - TestImages.Jpeg.Issues.ExifResizeOutOfRange696 + TestImages.Jpeg.Issues.ExifResizeOutOfRange696, + TestImages.Jpeg.Issues.InvalidAPP0721, + TestImages.Jpeg.Issues.ExifGetString750Load, + TestImages.Jpeg.Issues.ExifGetString750Transform }; public static string[] ProgressiveTestJpegs = @@ -41,20 +44,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Progressive.Bad.ExifUndefType, TestImages.Jpeg.Issues.NoEoiProgressive517, TestImages.Jpeg.Issues.BadRstProgressive518, - TestImages.Jpeg.Issues.MissingFF00ProgressiveBedroom159, TestImages.Jpeg.Issues.DhtHasWrongLength624, + TestImages.Jpeg.Issues.OrderedInterleavedProgressive723A, + TestImages.Jpeg.Issues.OrderedInterleavedProgressive723B, + TestImages.Jpeg.Issues.OrderedInterleavedProgressive723C }; - /// - /// Golang decoder is unable to decode these - /// - public static string[] PdfJsOnly = - { - TestImages.Jpeg.Issues.NoEoiProgressive517, TestImages.Jpeg.Issues.BadRstProgressive518, - TestImages.Jpeg.Issues.MissingFF00ProgressiveBedroom159 - }; - - private static readonly Dictionary CustomToleranceValues = + private static readonly Dictionary CustomToleranceValues = new Dictionary { // Baseline: diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 5977e59cf3..15f7f92a83 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -49,7 +49,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg TestImages.Jpeg.Issues.NoEoiProgressive517, TestImages.Jpeg.Issues.BadRstProgressive518, TestImages.Jpeg.Issues.InvalidEOI695, - TestImages.Jpeg.Issues.ExifResizeOutOfRange696 + TestImages.Jpeg.Issues.ExifResizeOutOfRange696, + TestImages.Jpeg.Issues.ExifGetString750Transform }; return !TestEnvironment.Is64BitProcess && largeImagesToSkipOn32Bit.Contains(provider.SourceFileOrDescription); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs index cfa421a82b..b3219115db 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegImagePostProcessorTests.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { string imageFile = provider.SourceFileOrDescription; using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(imageFile)) - using (var pp = new JpegImagePostProcessor(Configuration.Default.MemoryAllocator, decoder)) + using (var pp = new JpegImagePostProcessor(Configuration.Default, decoder)) using (var imageFrame = new ImageFrame(Configuration.Default, decoder.ImageWidth, decoder.ImageHeight)) { pp.DoPostProcessorStep(imageFrame); @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { string imageFile = provider.SourceFileOrDescription; using (JpegDecoderCore decoder = JpegFixture.ParseJpegStream(imageFile)) - using (var pp = new JpegImagePostProcessor(Configuration.Default.MemoryAllocator, decoder)) + using (var pp = new JpegImagePostProcessor(Configuration.Default, decoder)) using (var image = new Image(decoder.ImageWidth, decoder.ImageHeight)) { pp.PostProcess(image.Frames.RootFrame); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs index d14fbc3fc3..89fdd5745e 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs @@ -58,7 +58,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils { for (int j = 0; j < 8; j++) { - result[i * 8 + j] = (short)(i * 10 + j); + short val = (short)(i * 10 + j); + if ((i + j) % 2 == 0) + { + val *= -1; + } + result[i * 8 + j] = val; } } return result; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs index 57d92fa151..e4fcd10c5f 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs @@ -67,9 +67,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils for (int y = 0; y < result.HeightInBlocks; y++) { + Span blockRow = c.SpectralBlocks.GetRowSpan(y); for (int x = 0; x < result.WidthInBlocks; x++) { - short[] data = c.GetBlockReference(x, y).ToArray(); + short[] data = blockRow[x].ToArray(); result.MakeBlock(data, y, x); } } @@ -103,7 +104,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils var v = new Vector4(val, val, val, 1); Rgba32 color = default; - color.PackFromVector4(v); + color.FromVector4(v); int yy = by * 8 + y; int xx = bx * 8 + x; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs index bcfabca390..f5618d26d2 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.SpectralData.cs @@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils var v = new Vector4(val0, val1, val2, 1); Rgba32 color = default; - color.PackFromVector4(v); + color.FromVector4(v); int yy = by * 8 + y; int xx = bx * 8 + x; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs index 35652dd6b5..894d902b78 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngChunkTypeTests.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png Assert.Equal(PngChunkType.Palette, GetType("PLTE")); Assert.Equal(PngChunkType.Data, GetType("IDAT")); Assert.Equal(PngChunkType.End, GetType("IEND")); - Assert.Equal(PngChunkType.PaletteAlpha, GetType("tRNS")); + Assert.Equal(PngChunkType.Transparency, GetType("tRNS")); Assert.Equal(PngChunkType.Text, GetType("tEXt")); Assert.Equal(PngChunkType.Gamma, GetType("gAMA")); Assert.Equal(PngChunkType.Physical, GetType("pHYs")); @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png private static PngChunkType GetType(string text) { - return (PngChunkType)BinaryPrimitives.ReadInt32BigEndian(Encoding.UTF8.GetBytes(text)); + return (PngChunkType)BinaryPrimitives.ReadInt32BigEndian(Encoding.ASCII.GetBytes(text)); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs index dc29b19497..6a0119f0f1 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png [Theory] [InlineData((uint)PngChunkType.Gamma)] // gAMA - [InlineData((uint)PngChunkType.PaletteAlpha)] // tRNS + [InlineData((uint)PngChunkType.Transparency)] // tRNS [InlineData((uint)PngChunkType.Physical)] // pHYs: It's ok to test physical as we don't throw for duplicate chunks. //[InlineData(PngChunkTypes.Text)] //TODO: Figure out how to test this public void Decode_IncorrectCRCForNonCriticalChunk_ExceptionIsThrown(uint chunkType) @@ -113,7 +113,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png { // Needs a minimum length of 9 for pHYs chunk. memStream.Write(new byte[] { 0, 0, 0, 9 }, 0, 4); - memStream.Write(Encoding.GetEncoding("ASCII").GetBytes(chunkName), 0, 4); // 4 bytes chunk header + memStream.Write(Encoding.ASCII.GetBytes(chunkName), 0, 4); // 4 bytes chunk header memStream.Write(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 9); // 9 bytes of chunk data memStream.Write(new byte[] { 0, 0, 0, 0 }, 0, 4); // Junk Crc } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 54f3e397c3..f51f9b6c5c 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -40,7 +40,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png TestImages.Png.VimImage2, TestImages.Png.Rgb24BppTrans, - TestImages.Png.GrayAlpha8Bit + TestImages.Png.GrayAlpha8Bit, + TestImages.Png.Gray1BitTrans }; public static readonly string[] TestImages48Bpp = diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 0508ac8c26..9079b15fb0 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -9,7 +9,6 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -19,10 +18,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png { public class PngEncoderTests { - // This is bull. Failing online for no good reason. - // The images are an exact match. Maybe the submodule isn't updating? - private const float ToleranceThresholdForPaletteEncoder = 1.3F / 100; - public static readonly TheoryData PngBitDepthFiles = new TheoryData { @@ -30,6 +25,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png { TestImages.Png.Bpp1, PngBitDepth.Bit1 } }; + public static readonly TheoryData PngTrnsFiles = + new TheoryData + { + { TestImages.Png.Gray1BitTrans, PngBitDepth.Bit1, PngColorType.Grayscale }, + { TestImages.Png.Gray2BitTrans, PngBitDepth.Bit2, PngColorType.Grayscale }, + { TestImages.Png.Gray4BitTrans, PngBitDepth.Bit4, PngColorType.Grayscale }, + { TestImages.Png.Gray8BitTrans, PngBitDepth.Bit8, PngColorType.Grayscale }, + { TestImages.Png.GrayTrns16BitInterlaced, PngBitDepth.Bit16, PngColorType.Grayscale }, + { TestImages.Png.Rgb24BppTrans, PngBitDepth.Bit8, PngColorType.Rgb }, + { TestImages.Png.Rgb48BppTrans, PngBitDepth.Bit16, PngColorType.Rgb } + }; + /// /// All types except Palette /// @@ -137,19 +144,32 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } [Theory] - [WithTestPatternImages(24, 24, PixelTypes.Rgba64, PngColorType.Rgb)] - [WithTestPatternImages(24, 24, PixelTypes.Rgba64, PngColorType.RgbWithAlpha)] - [WithTestPatternImages(24, 24, PixelTypes.Rgba32, PngColorType.RgbWithAlpha)] - public void WorksWithBitDepth16(TestImageProvider provider, PngColorType pngColorType) + [WithTestPatternImages(24, 24, PixelTypes.Rgba32, PngColorType.Rgb, PngBitDepth.Bit8)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba64, PngColorType.Rgb, PngBitDepth.Bit16)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba32, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba64, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba32, PngColorType.Palette, PngBitDepth.Bit1)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba32, PngColorType.Palette, PngBitDepth.Bit2)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba32, PngColorType.Palette, PngBitDepth.Bit4)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba32, PngColorType.Palette, PngBitDepth.Bit8)] + [WithTestPatternImages(24, 24, PixelTypes.Rgb24, PngColorType.Grayscale, PngBitDepth.Bit1)] + [WithTestPatternImages(24, 24, PixelTypes.Rgb24, PngColorType.Grayscale, PngBitDepth.Bit2)] + [WithTestPatternImages(24, 24, PixelTypes.Rgb24, PngColorType.Grayscale, PngBitDepth.Bit4)] + [WithTestPatternImages(24, 24, PixelTypes.Rgb24, PngColorType.Grayscale, PngBitDepth.Bit8)] + [WithTestPatternImages(24, 24, PixelTypes.Rgb48, PngColorType.Grayscale, PngBitDepth.Bit16)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba32, PngColorType.GrayscaleWithAlpha, PngBitDepth.Bit8)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba64, PngColorType.GrayscaleWithAlpha, PngBitDepth.Bit16)] + public void WorksWithAllBitDepths(TestImageProvider provider, PngColorType pngColorType, PngBitDepth pngBitDepth) where TPixel : struct, IPixel { TestPngEncoderCore( provider, pngColorType, PngFilterMethod.Adaptive, - PngBitDepth.Bit16, + pngBitDepth, appendPngColorType: true, - appendPixelType: true); + appendPixelType: true, + appendPngBitDepth: true); } [Theory] @@ -166,87 +186,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png appendPaletteSize: true); } - private static bool HasAlpha(PngColorType pngColorType) => - pngColorType == PngColorType.GrayscaleWithAlpha || pngColorType == PngColorType.RgbWithAlpha; - - private static void TestPngEncoderCore( - TestImageProvider provider, - PngColorType pngColorType, - PngFilterMethod pngFilterMethod, - PngBitDepth bitDepth, - int compressionLevel = 6, - int paletteSize = 255, - bool appendPngColorType = false, - bool appendPngFilterMethod = false, - bool appendPixelType = false, - bool appendCompressionLevel = false, - bool appendPaletteSize = false) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - if (!HasAlpha(pngColorType)) - { - image.Mutate(c => c.MakeOpaque()); - } - - var encoder = new PngEncoder - { - ColorType = pngColorType, - FilterMethod = pngFilterMethod, - CompressionLevel = compressionLevel, - BitDepth = bitDepth, - Quantizer = new WuQuantizer(paletteSize) - }; - - string pngColorTypeInfo = appendPngColorType ? pngColorType.ToString() : string.Empty; - string pngFilterMethodInfo = appendPngFilterMethod ? pngFilterMethod.ToString() : string.Empty; - string compressionLevelInfo = appendCompressionLevel ? $"_C{compressionLevel}" : string.Empty; - string paletteSizeInfo = appendPaletteSize ? $"_PaletteSize-{paletteSize}" : string.Empty; - string debugInfo = $"{pngColorTypeInfo}{pngFilterMethodInfo}{compressionLevelInfo}{paletteSizeInfo}"; - //string referenceInfo = $"{pngColorTypeInfo}"; - - // Does DebugSave & load reference CompareToReferenceInput(): - string actualOutputFile = ((ITestImageProvider)provider).Utility.SaveTestOutputFile(image, "png", encoder, debugInfo, appendPixelType); - - if (TestEnvironment.IsMono) - { - // There are bugs in mono's System.Drawing implementation, reference decoders are not always reliable! - return; - } - - IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); - string referenceOutputFile = ((ITestImageProvider)provider).Utility.GetReferenceOutputFileName("png", debugInfo, appendPixelType, true); - - bool referenceOutputFileExists = File.Exists(referenceOutputFile); - - using (var actualImage = Image.Load(actualOutputFile, referenceDecoder)) - { - // TODO: Do we still need the reference output files? - Image referenceImage = referenceOutputFileExists - ? Image.Load(referenceOutputFile, referenceDecoder) - : image; - - float paletteToleranceHack = 80f / paletteSize; - paletteToleranceHack *= paletteToleranceHack; - ImageComparer comparer = pngColorType == PngColorType.Palette - ? ImageComparer.Tolerant(ToleranceThresholdForPaletteEncoder * paletteToleranceHack) - : ImageComparer.Exact; - try - { - comparer.VerifySimilarity(referenceImage, actualImage); - } - finally - { - if (referenceOutputFileExists) - { - referenceImage.Dispose(); - } - } - } - } - } - [Theory] [WithBlankImages(1, 1, PixelTypes.Rgba32)] public void WritesFileMarker(TestImageProvider provider) @@ -321,5 +260,109 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } } } + + [Theory] + [MemberData(nameof(PngTrnsFiles))] + public void Encode_PreserveTrns(string imagePath, PngBitDepth pngBitDepth, PngColorType pngColorType) + { + var options = new PngEncoder(); + + var testFile = TestFile.Create(imagePath); + using (Image input = testFile.CreateImage()) + { + PngMetaData inMeta = input.MetaData.GetFormatMetaData(PngFormat.Instance); + Assert.True(inMeta.HasTrans); + + using (var memStream = new MemoryStream()) + { + input.Save(memStream, options); + memStream.Position = 0; + using (var output = Image.Load(memStream)) + { + PngMetaData outMeta = output.MetaData.GetFormatMetaData(PngFormat.Instance); + Assert.True(outMeta.HasTrans); + + switch (pngColorType) + { + case PngColorType.Grayscale: + if (pngBitDepth.Equals(PngBitDepth.Bit16)) + { + Assert.True(outMeta.TransparentGray16.HasValue); + Assert.Equal(inMeta.TransparentGray16, outMeta.TransparentGray16); + } + else + { + Assert.True(outMeta.TransparentGray8.HasValue); + Assert.Equal(inMeta.TransparentGray8, outMeta.TransparentGray8); + } + + break; + case PngColorType.Rgb: + if (pngBitDepth.Equals(PngBitDepth.Bit16)) + { + Assert.True(outMeta.TransparentRgb48.HasValue); + Assert.Equal(inMeta.TransparentRgb48, outMeta.TransparentRgb48); + } + else + { + Assert.True(outMeta.TransparentRgb24.HasValue); + Assert.Equal(inMeta.TransparentRgb24, outMeta.TransparentRgb24); + } + + break; + } + } + } + } + } + + private static void TestPngEncoderCore( + TestImageProvider provider, + PngColorType pngColorType, + PngFilterMethod pngFilterMethod, + PngBitDepth bitDepth, + int compressionLevel = 6, + int paletteSize = 255, + bool appendPngColorType = false, + bool appendPngFilterMethod = false, + bool appendPixelType = false, + bool appendCompressionLevel = false, + bool appendPaletteSize = false, + bool appendPngBitDepth = false) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var encoder = new PngEncoder + { + ColorType = pngColorType, + FilterMethod = pngFilterMethod, + CompressionLevel = compressionLevel, + BitDepth = bitDepth, + Quantizer = new WuQuantizer(paletteSize) + }; + + string pngColorTypeInfo = appendPngColorType ? pngColorType.ToString() : string.Empty; + string pngFilterMethodInfo = appendPngFilterMethod ? pngFilterMethod.ToString() : string.Empty; + string compressionLevelInfo = appendCompressionLevel ? $"_C{compressionLevel}" : string.Empty; + string paletteSizeInfo = appendPaletteSize ? $"_PaletteSize-{paletteSize}" : string.Empty; + string pngBitDepthInfo = appendPngBitDepth ? bitDepth.ToString() : string.Empty; + string debugInfo = $"{pngColorTypeInfo}{pngFilterMethodInfo}{compressionLevelInfo}{paletteSizeInfo}{pngBitDepthInfo}"; + + string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "png", encoder, debugInfo, appendPixelType); + + // Compare to the Magick reference decoder. + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); + + // We compare using both our decoder and the reference decoder as pixel transformation + // occurrs within the encoder itself leaving the input image unaffected. + // This means we are benefiting from testing our decoder also. + using (var imageSharpImage = Image.Load(actualOutputFile, new PngDecoder())) + using (var referenceImage = Image.Load(actualOutputFile, referenceDecoder)) + { + ImageComparer.Exact.VerifySimilarity(referenceImage, imageSharpImage); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/GraphicsOptionsTests.cs b/tests/ImageSharp.Tests/GraphicsOptionsTests.cs new file mode 100644 index 0000000000..6ff38626d6 --- /dev/null +++ b/tests/ImageSharp.Tests/GraphicsOptionsTests.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public class GraphicsOptionsTests + { + [Fact] + public void IsOpaqueColor() + { + Assert.True(new GraphicsOptions(true).IsOpaqueColorWithoutBlending(Rgba32.Red)); + Assert.False(new GraphicsOptions(true, 0.5f).IsOpaqueColorWithoutBlending(Rgba32.Red)); + Assert.False(new GraphicsOptions(true).IsOpaqueColorWithoutBlending(Rgba32.Transparent)); + Assert.False(new GraphicsOptions(true, PixelColorBlendingMode.Lighten, 1).IsOpaqueColorWithoutBlending(Rgba32.Red)); + Assert.False(new GraphicsOptions(true, PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.DestOver, 1).IsOpaqueColorWithoutBlending(Rgba32.Red)); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Helpers/GuardTests.cs b/tests/ImageSharp.Tests/Helpers/GuardTests.cs index 0d1bb5ce9f..b847e581f5 100644 --- a/tests/ImageSharp.Tests/Helpers/GuardTests.cs +++ b/tests/ImageSharp.Tests/Helpers/GuardTests.cs @@ -3,7 +3,10 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Linq; + using Xunit; +// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Helpers { @@ -16,6 +19,35 @@ namespace SixLabors.ImageSharp.Tests.Helpers { } + [Theory] + [InlineData(0, 0)] + [InlineData(0, 1)] + [InlineData(0, 42)] + [InlineData(1, 1)] + [InlineData(10, 42)] + [InlineData(42, 42)] + public void DestinationShouldNotBeTooShort_WhenOk(int sourceLength, int destLength) + { + ReadOnlySpan source = new int[sourceLength]; + Span dest = new float[destLength]; + + Guard.DestinationShouldNotBeTooShort(source, dest, nameof(dest)); + } + + [Theory] + [InlineData(1, 0)] + [InlineData(42, 41)] + public void DestinationShouldNotBeTooShort_WhenThrows(int sourceLength, int destLength) + { + Assert.ThrowsAny( + () => + { + ReadOnlySpan source = new int[sourceLength]; + Span dest = new float[destLength]; + Guard.DestinationShouldNotBeTooShort(source, dest, nameof(dest)); + }); + } + /// /// Tests that the method throws when the argument is null. /// diff --git a/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs b/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs new file mode 100644 index 0000000000..018fabd982 --- /dev/null +++ b/tests/ImageSharp.Tests/Helpers/ImageMathsTests.cs @@ -0,0 +1,136 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Helpers +{ + public class ImageMathsTests + { + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + [InlineData(4)] + [InlineData(100)] + [InlineData(123)] + [InlineData(53436353)] + public void Modulo4(int x) + { + int actual = ImageMaths.Modulo4(x); + Assert.Equal(x % 4, actual); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + [InlineData(6)] + [InlineData(7)] + [InlineData(8)] + [InlineData(100)] + [InlineData(123)] + [InlineData(53436353)] + [InlineData(975)] + public void Modulo8(int x) + { + int actual = ImageMaths.Modulo8(x); + Assert.Equal(x % 8, actual); + } + + [Theory] + [InlineData(0, 2)] + [InlineData(1, 2)] + [InlineData(2, 2)] + [InlineData(0, 4)] + [InlineData(3, 4)] + [InlineData(5, 4)] + [InlineData(5, 8)] + [InlineData(8, 8)] + [InlineData(8, 16)] + [InlineData(15, 16)] + [InlineData(17, 16)] + [InlineData(17, 32)] + [InlineData(31, 32)] + [InlineData(32, 32)] + [InlineData(33, 32)] + public void Modulo2P(int x, int m) + { + int actual = ImageMaths.ModuloP2(x, m); + Assert.Equal(x % m, actual); + } + + [Theory] + [InlineData(0, 0, 0, 0)] + [InlineData(0.5f, 0, 1, 0.5f)] + [InlineData(-0.5f, -0.1f, 10, -0.1f)] + [InlineData(-0.05f, -0.1f, 10, -0.05f)] + [InlineData(9.9f, -0.1f, 10, 9.9f)] + [InlineData(10f, -0.1f, 10, 10f)] + [InlineData(10.1f, -0.1f, 10, 10f)] + public void Clamp(float x, float min, float max, float expected) + { + float actual = x.Clamp(min, max); + Assert.Equal(expected, actual); + } + + [Fact] + public void FasAbsResultMatchesMath() + { + const int X = -33; + int expected = Math.Abs(X); + + Assert.Equal(expected, ImageMaths.FastAbs(X)); + } + + [Fact] + public void Pow2ResultMatchesMath() + { + const float X = -33; + float expected = (float)Math.Pow(X, 2); + + Assert.Equal(expected, ImageMaths.Pow2(X)); + } + + [Fact] + public void Pow3ResultMatchesMath() + { + const float X = -33; + float expected = (float)Math.Pow(X, 3); + + Assert.Equal(expected, ImageMaths.Pow3(X)); + } + + [Theory] + [InlineData(1, 1, 1)] + [InlineData(1, 42, 1)] + [InlineData(10, 8, 2)] + [InlineData(12, 18, 6)] + [InlineData(4536, 1000, 8)] + [InlineData(1600, 1024, 64)] + public void GreatestCommonDivisor(int a, int b, int expected) + { + int actual = ImageMaths.GreatestCommonDivisor(a, b); + + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(1, 1, 1)] + [InlineData(1, 42, 42)] + [InlineData(3, 4, 12)] + [InlineData(6, 4, 12)] + [InlineData(1600, 1024, 25600)] + [InlineData(3264, 100, 81600)] + public void LeastCommonMultiple(int a, int b, int expected) + { + int actual = ImageMaths.LeastCommonMultiple(a, b); + + Assert.Equal(expected, actual); + } + + // TODO: We need to test all ImageMaths methods! + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs b/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs new file mode 100644 index 0000000000..ef6b133f75 --- /dev/null +++ b/tests/ImageSharp.Tests/Helpers/ParallelHelperTests.cs @@ -0,0 +1,338 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Numerics; +using System.Threading; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.Memory; +using SixLabors.Primitives; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.Helpers +{ + public class ParallelHelperTests + { + private readonly ITestOutputHelper Output; + + public ParallelHelperTests(ITestOutputHelper output) + { + this.Output = output; + } + + /// + /// maxDegreeOfParallelism, minY, maxY, expectedStepLength, expectedLastStepLength + /// + public static TheoryData IterateRows_OverMinimumPixelsLimit_Data = + new TheoryData() + { + { 1, 0, 100, -1, 100 }, + { 2, 0, 9, 5, 4 }, + { 4, 0, 19, 5, 4 }, + { 2, 10, 19, 5, 4 }, + { 4, 0, 200, 50, 50 }, + { 4, 123, 323, 50, 50 }, + { 4, 0, 1201, 301, 298 }, + { 8, 10, 236, 29, 23 } + }; + + [Theory] + [MemberData(nameof(IterateRows_OverMinimumPixelsLimit_Data))] + public void IterateRows_OverMinimumPixelsLimit_IntervalsAreCorrect( + int maxDegreeOfParallelism, + int minY, + int maxY, + int expectedStepLength, + int expectedLastStepLength) + { + var parallelSettings = new ParallelExecutionSettings( + maxDegreeOfParallelism, + 1, + Configuration.Default.MemoryAllocator); + + var rectangle = new Rectangle(0, minY, 10, maxY - minY); + + int actualNumberOfSteps = 0; + + ParallelHelper.IterateRows( + rectangle, + parallelSettings, + rows => + { + Assert.True(rows.Min >= minY); + Assert.True(rows.Max <= maxY); + + int step = rows.Max - rows.Min; + int expected = rows.Max < maxY ? expectedStepLength : expectedLastStepLength; + + Interlocked.Increment(ref actualNumberOfSteps); + Assert.Equal(expected, step); + }); + + Assert.Equal(maxDegreeOfParallelism, actualNumberOfSteps); + } + + [Theory] + [MemberData(nameof(IterateRows_OverMinimumPixelsLimit_Data))] + public void IterateRows_OverMinimumPixelsLimit_ShouldVisitAllRows( + int maxDegreeOfParallelism, + int minY, + int maxY, + int expectedStepLength, + int expectedLastStepLength) + { + var parallelSettings = new ParallelExecutionSettings( + maxDegreeOfParallelism, + 1, + Configuration.Default.MemoryAllocator); + + var rectangle = new Rectangle(0, minY, 10, maxY - minY); + + + int[] expectedData = Enumerable.Repeat(0, minY).Concat(Enumerable.Range(minY, maxY - minY)).ToArray(); + int[] actualData = new int[maxY]; + + ParallelHelper.IterateRows( + rectangle, + parallelSettings, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + actualData[y] = y; + } + }); + + Assert.Equal(expectedData, actualData); + } + + [Theory] + [MemberData(nameof(IterateRows_OverMinimumPixelsLimit_Data))] + public void IterateRowsWithTempBuffer_OverMinimumPixelsLimit( + int maxDegreeOfParallelism, + int minY, + int maxY, + int expectedStepLength, + int expectedLastStepLength) + { + var parallelSettings = new ParallelExecutionSettings( + maxDegreeOfParallelism, + 1, + Configuration.Default.MemoryAllocator); + + var rectangle = new Rectangle(0, minY, 10, maxY - minY); + + var bufferHashes = new ConcurrentBag(); + + int actualNumberOfSteps = 0; + ParallelHelper.IterateRowsWithTempBuffer( + rectangle, + parallelSettings, + (RowInterval rows, Memory buffer) => + { + Assert.True(rows.Min >= minY); + Assert.True(rows.Max <= maxY); + + bufferHashes.Add(buffer.GetHashCode()); + + int step = rows.Max - rows.Min; + int expected = rows.Max < maxY ? expectedStepLength : expectedLastStepLength; + + Interlocked.Increment(ref actualNumberOfSteps); + Assert.Equal(expected, step); + }); + + Assert.Equal(maxDegreeOfParallelism, actualNumberOfSteps); + + int numberOfDifferentBuffers = bufferHashes.Distinct().Count(); + Assert.Equal(actualNumberOfSteps, numberOfDifferentBuffers); + } + + [Theory] + [MemberData(nameof(IterateRows_OverMinimumPixelsLimit_Data))] + public void IterateRowsWithTempBuffer_OverMinimumPixelsLimit_ShouldVisitAllRows( + int maxDegreeOfParallelism, + int minY, + int maxY, + int expectedStepLength, + int expectedLastStepLength) + { + var parallelSettings = new ParallelExecutionSettings( + maxDegreeOfParallelism, + 1, + Configuration.Default.MemoryAllocator); + + var rectangle = new Rectangle(0, minY, 10, maxY - minY); + + int[] expectedData = Enumerable.Repeat(0, minY).Concat(Enumerable.Range(minY, maxY - minY)).ToArray(); + int[] actualData = new int[maxY]; + + ParallelHelper.IterateRowsWithTempBuffer( + rectangle, + parallelSettings, + (RowInterval rows, Memory buffer) => + { + for (int y = rows.Min; y < rows.Max; y++) + { + actualData[y] = y; + } + }); + + Assert.Equal(expectedData, actualData); + + } + + public static TheoryData IterateRows_WithEffectiveMinimumPixelsLimit_Data = + new TheoryData() + { + { 2, 200, 50, 2, 1, -1, 2 }, + { 2, 200, 200, 1, 1, -1, 1 }, + { 4, 200, 100, 4, 2, 2, 2 }, + { 4, 300, 100, 8, 3, 3, 2 }, + { 2, 5000, 1, 4500, 1, -1, 4500 }, + { 2, 5000, 1, 5000, 1, -1, 5000 }, + { 2, 5000, 1, 5001, 2, 2501, 2500 }, + }; + + [Theory] + [MemberData(nameof(IterateRows_WithEffectiveMinimumPixelsLimit_Data))] + public void IterateRows_WithEffectiveMinimumPixelsLimit( + int maxDegreeOfParallelism, + int minimumPixelsProcessedPerTask, + int width, + int height, + int expectedNumberOfSteps, + int expectedStepLength, + int expectedLastStepLength) + { + var parallelSettings = new ParallelExecutionSettings( + maxDegreeOfParallelism, + minimumPixelsProcessedPerTask, + Configuration.Default.MemoryAllocator); + + var rectangle = new Rectangle(0, 0, width, height); + + int actualNumberOfSteps = 0; + + ParallelHelper.IterateRows( + rectangle, + parallelSettings, + rows => + { + Assert.True(rows.Min >= 0); + Assert.True(rows.Max <= height); + + int step = rows.Max - rows.Min; + int expected = rows.Max < height ? expectedStepLength : expectedLastStepLength; + + Interlocked.Increment(ref actualNumberOfSteps); + Assert.Equal(expected, step); + }); + + Assert.Equal(expectedNumberOfSteps, actualNumberOfSteps); + } + + [Theory] + [MemberData(nameof(IterateRows_WithEffectiveMinimumPixelsLimit_Data))] + public void IterateRowsWithTempBuffer_WithEffectiveMinimumPixelsLimit( + int maxDegreeOfParallelism, + int minimumPixelsProcessedPerTask, + int width, + int height, + int expectedNumberOfSteps, + int expectedStepLength, + int expectedLastStepLength) + { + var parallelSettings = new ParallelExecutionSettings( + maxDegreeOfParallelism, + minimumPixelsProcessedPerTask, + Configuration.Default.MemoryAllocator); + + var rectangle = new Rectangle(0, 0, width, height); + + int actualNumberOfSteps = 0; + ParallelHelper.IterateRowsWithTempBuffer( + rectangle, + parallelSettings, + (RowInterval rows, Memory buffer) => + { + Assert.True(rows.Min >= 0); + Assert.True(rows.Max <= height); + + int step = rows.Max - rows.Min; + int expected = rows.Max < height ? expectedStepLength : expectedLastStepLength; + + Interlocked.Increment(ref actualNumberOfSteps); + Assert.Equal(expected, step); + }); + + Assert.Equal(expectedNumberOfSteps, actualNumberOfSteps); + } + + public static readonly TheoryData IterateRectangularBuffer_Data = + new TheoryData() + { + { 8, 582, 453, 10, 10, 291, 226 }, // boundary data from DetectEdgesTest.DetectEdges_InBox + { 2, 582, 453, 10, 10, 291, 226 }, + { 16, 582, 453, 10, 10, 291, 226 }, + { 16, 582, 453, 10, 10, 1, 226 }, + { 16, 1, 453, 0, 10, 1, 226 }, + }; + + [Theory] + [MemberData(nameof(IterateRectangularBuffer_Data))] + public void IterateRectangularBuffer( + int maxDegreeOfParallelism, + int bufferWidth, + int bufferHeight, + int rectX, + int rectY, + int rectWidth, + int rectHeight) + { + MemoryAllocator memoryAllocator = Configuration.Default.MemoryAllocator; + + using (Buffer2D expected = memoryAllocator.Allocate2D(bufferWidth, bufferHeight, AllocationOptions.Clean)) + using (Buffer2D actual = memoryAllocator.Allocate2D(bufferWidth, bufferHeight, AllocationOptions.Clean)) + { + var rect = new Rectangle(rectX, rectY, rectWidth, rectHeight); + + void FillRow(int y, Buffer2D buffer) + { + for (int x = rect.Left; x < rect.Right; x++) + { + buffer[x, y] = new Point(x, y); + } + } + + // Fill Expected data: + for (int y = rectY; y < rect.Bottom; y++) + { + FillRow(y, expected); + } + + // Fill actual data using IterateRows: + var settings = new ParallelExecutionSettings(maxDegreeOfParallelism, memoryAllocator); + + ParallelHelper.IterateRows(rect, settings, + rows => + { + this.Output.WriteLine(rows.ToString()); + for (int y = rows.Min; y < rows.Max; y++) + { + FillRow(y, actual); + } + }); + + // Assert: + TestImageExtensions.CompareBuffers(expected.Span, actual.Span); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs b/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs new file mode 100644 index 0000000000..629b3cdeb3 --- /dev/null +++ b/tests/ImageSharp.Tests/Helpers/RowIntervalTests.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Memory; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Helpers +{ + public class RowIntervalTests + { + [Theory] + [InlineData(10, 20, 5, 10)] + [InlineData(1, 10, 0, 10)] + [InlineData(1, 10, 5, 8)] + [InlineData(1, 1, 0, 1)] + [InlineData(10, 20, 9, 10)] + [InlineData(10, 20, 0, 1)] + public void GetMultiRowSpan(int width, int height, int min, int max) + { + using (Buffer2D buffer = Configuration.Default.MemoryAllocator.Allocate2D(width, height)) + { + var rows = new RowInterval(min, max); + + Span span = buffer.GetMultiRowSpan(rows); + + ref int expected0 = ref buffer.Span[min * width]; + int expectedLength = (max - min) * width; + + ref int actual0 = ref span[0]; + + Assert.Equal(span.Length, expectedLength); + Assert.True(Unsafe.AreSame(ref expected0, ref actual0)); + } + } + } +} diff --git a/tests/ImageSharp.Tests/Helpers/TolerantMathTests.cs b/tests/ImageSharp.Tests/Helpers/TolerantMathTests.cs new file mode 100644 index 0000000000..6c7a1f2752 --- /dev/null +++ b/tests/ImageSharp.Tests/Helpers/TolerantMathTests.cs @@ -0,0 +1,168 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using Xunit; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Helpers +{ + public class TolerantMathTests + { + private readonly TolerantMath tolerantMath = new TolerantMath(0.1); + + [Theory] + [InlineData(0)] + [InlineData(0.01)] + [InlineData(-0.05)] + public void IsZero_WhenTrue(double a) + { + Assert.True(this.tolerantMath.IsZero(a)); + } + + [Theory] + [InlineData(0.11)] + [InlineData(-0.101)] + [InlineData(42)] + public void IsZero_WhenFalse(double a) + { + Assert.False(this.tolerantMath.IsZero(a)); + } + + [Theory] + [InlineData(0.11)] + [InlineData(100)] + public void IsPositive_WhenTrue(double a) + { + Assert.True(this.tolerantMath.IsPositive(a)); + } + + [Theory] + [InlineData(0.09)] + [InlineData(-0.1)] + [InlineData(-1000)] + public void IsPositive_WhenFalse(double a) + { + Assert.False(this.tolerantMath.IsPositive(a)); + } + + [Theory] + [InlineData(-0.11)] + [InlineData(-100)] + public void IsNegative_WhenTrue(double a) + { + Assert.True(this.tolerantMath.IsNegative(a)); + } + + [Theory] + [InlineData(-0.09)] + [InlineData(0.1)] + [InlineData(1000)] + public void IsNegative_WhenFalse(double a) + { + Assert.False(this.tolerantMath.IsNegative(a)); + } + + [Theory] + [InlineData(4.2, 4.2)] + [InlineData(4.2, 4.25)] + [InlineData(-Math.PI, -Math.PI + 0.05)] + [InlineData(999999.2, 999999.25)] + public void AreEqual_WhenTrue(double a, double b) + { + Assert.True(this.tolerantMath.AreEqual(a, b)); + } + + [Theory] + [InlineData(1, 2)] + [InlineData(-1000000, -1000000.2)] + public void AreEqual_WhenFalse(double a, double b) + { + Assert.False(this.tolerantMath.AreEqual(a, b)); + } + + [Theory] + [InlineData(2, 1.8)] + [InlineData(-20, -20.2)] + [InlineData(0.1, -0.1)] + [InlineData(100, 10)] + public void IsGreater_IsLess_WhenTrue(double a, double b) + { + Assert.True(this.tolerantMath.IsGreater(a, b)); + Assert.True(this.tolerantMath.IsLess(b, a)); + } + + [Theory] + [InlineData(2, 1.95)] + [InlineData(-20, -20.02)] + [InlineData(0.01, -0.01)] + [InlineData(999999, 999999.09)] + public void IsGreater_IsLess_WhenFalse(double a, double b) + { + Assert.False(this.tolerantMath.IsGreater(a, b)); + Assert.False(this.tolerantMath.IsLess(b, a)); + } + + [Theory] + [InlineData(3, 2)] + [InlineData(3, 2.99)] + [InlineData(2.99, 3)] + [InlineData(-5, -6)] + [InlineData(-5, -5.05)] + [InlineData(-5.05, -5)] + public void IsGreaterOrEqual_IsLessOrEqual_WhenTrue(double a, double b) + { + Assert.True(this.tolerantMath.IsGreaterOrEqual(a, b)); + Assert.True(this.tolerantMath.IsLessOrEqual(b, a)); + } + + [Theory] + [InlineData(2, 3)] + [InlineData(2.89, 3)] + [InlineData(-3, -2.89)] + public void IsGreaterOrEqual_IsLessOrEqual_WhenFalse(double a, double b) + { + Assert.False(this.tolerantMath.IsGreaterOrEqual(a, b)); + Assert.False(this.tolerantMath.IsLessOrEqual(b, a)); + } + + [Theory] + [InlineData(3.5, 4.0)] + [InlineData(3.89, 4.0)] + [InlineData(4.09, 4.0)] + [InlineData(4.11, 5.0)] + [InlineData(0.11, 1)] + [InlineData(0.05, 0)] + [InlineData(-0.5, 0)] + [InlineData(-0.95, -1)] + [InlineData(-1.05, -1)] + [InlineData(-1.5, -1)] + public void Ceiling(double value, double expected) + { + double actual = this.tolerantMath.Ceiling(value); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(1, 1)] + [InlineData(0.99, 1)] + [InlineData(0.5, 0)] + [InlineData(0.01, 0)] + [InlineData(-0.09, 0)] + [InlineData(-0.11, -1)] + [InlineData(-100.11, -101)] + [InlineData(-100.09, -100)] + public void Floor(double value, double expected) + { + double plz1 = Math.IEEERemainder(1.1, 1); + double plz2 = Math.IEEERemainder(0.9, 1); + + double plz3 = Math.IEEERemainder(-1.1, 1); + double plz4 = Math.IEEERemainder(-0.9, 1); + + double actual = this.tolerantMath.Floor(value); + Assert.Equal(expected, actual); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Helpers/Vector4UtilsTests.cs b/tests/ImageSharp.Tests/Helpers/Vector4UtilsTests.cs new file mode 100644 index 0000000000..9416be740a --- /dev/null +++ b/tests/ImageSharp.Tests/Helpers/Vector4UtilsTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Linq; +using System.Numerics; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Helpers +{ + public class Vector4UtilsTests + { + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(30)] + public void Premultiply_VectorSpan(int length) + { + var rnd = new Random(42); + Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); + Vector4[] expected = source.Select(v => { Vector4Utils.Premultiply(ref v); return v; }).ToArray(); + + Vector4Utils.Premultiply(source); + + Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(30)] + public void UnPremultiply_VectorSpan(int length) + { + var rnd = new Random(42); + Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); + Vector4[] expected = source.Select(v => { Vector4Utils.UnPremultiply(ref v); return v; }).ToArray(); + + Vector4Utils.UnPremultiply(source); + + Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); + } + } +} diff --git a/tests/ImageSharp.Tests/Image/ImageTests.cs b/tests/ImageSharp.Tests/Image/ImageTests.cs index f3c04d5e14..c5c7d19e1e 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.cs @@ -2,7 +2,10 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.Memory; + using Xunit; // ReSharper disable InconsistentNaming @@ -46,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests } [Fact] - public void Configuration_Width_Height_BackroundColor() + public void Configuration_Width_Height_BackgroundColor() { Configuration configuration = Configuration.Default.Clone(); Rgba32 color = Rgba32.Aquamarine; @@ -61,6 +64,26 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(configuration, image.GetConfiguration()); } } + + [Fact] + public void CreateUninitialized() + { + Configuration configuration = Configuration.Default.Clone(); + + byte dirtyValue = 123; + configuration.MemoryAllocator = new TestMemoryAllocator(dirtyValue); + var metadata = new ImageMetaData(); + + using (Image image = Image.CreateUninitialized(configuration, 21, 22, metadata)) + { + Assert.Equal(21, image.Width); + Assert.Equal(22, image.Height); + Assert.Same(configuration, image.GetConfiguration()); + Assert.Same(metadata, image.MetaData); + + Assert.Equal(dirtyValue, image[5, 5].PackedValue); + } + } } } } diff --git a/tests/ImageSharp.Tests/ImageOperationTests.cs b/tests/ImageSharp.Tests/ImageOperationTests.cs index d73eea6870..869882f672 100644 --- a/tests/ImageSharp.Tests/ImageOperationTests.cs +++ b/tests/ImageSharp.Tests/ImageOperationTests.cs @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void CloneCallsImageOperationsProvider_Func_WithDuplicateImage() { - var returned = this.image.Clone(x => x.ApplyProcessor(this.processor)); + Image returned = this.image.Clone(x => x.ApplyProcessor(this.processor)); Assert.True(this.provider.HasCreated(returned)); Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.Processor)); @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void CloneCallsImageOperationsProvider_ListOfProcessors_WithDuplicateImage() { - var returned = this.image.Clone(this.processor); + Image returned = this.image.Clone(this.processor); Assert.True(this.provider.HasCreated(returned)); Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.Processor)); @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void CloneCallsImageOperationsProvider_Func_NotOnOrigional() { - var returned = this.image.Clone(x => x.ApplyProcessor(this.processor)); + Image returned = this.image.Clone(x => x.ApplyProcessor(this.processor)); Assert.False(this.provider.HasCreated(this.image)); Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.Processor)); } @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void CloneCallsImageOperationsProvider_ListOfProcessors_NotOnOrigional() { - var returned = this.image.Clone(this.processor); + Image returned = this.image.Clone(this.processor); Assert.False(this.provider.HasCreated(this.image)); Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.Processor)); } @@ -95,9 +95,6 @@ namespace SixLabors.ImageSharp.Tests Assert.Contains(this.processor, operations.Applied.Select(x => x.Processor)); } - public void Dispose() - { - this.image.Dispose(); - } + public void Dispose() => this.image.Dispose(); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 5ef8f0111f..86c1a7a259 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -1,8 +1,8 @@  - net462;net471;netcoreapp2.1 + net462;net472;netcoreapp2.1 True - 7.3 + latest full portable True @@ -11,15 +11,15 @@ AnyCPU;x64;x86 - true + false - true + false - true + false @@ -27,7 +27,7 @@ - + diff --git a/tests/ImageSharp.Tests/Issues/Issue412.cs b/tests/ImageSharp.Tests/Issues/Issue412.cs index 6123c822b8..b0374ce1fa 100644 --- a/tests/ImageSharp.Tests/Issues/Issue412.cs +++ b/tests/ImageSharp.Tests/Issues/Issue412.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Tests.Issues [WithBlankImages(40, 30, PixelTypes.Rgba32)] public void AllPixelsExpectedToBeRedWhenAntialiasedDisabled(TestImageProvider provider) where TPixel : struct, IPixel { - using (var image = provider.GetImage()) + using (Image image = provider.GetImage()) { image.Mutate( context => diff --git a/tests/ImageSharp.Tests/Issues/Issue594.cs b/tests/ImageSharp.Tests/Issues/Issue594.cs index 81fd59885a..927f0a5edc 100644 --- a/tests/ImageSharp.Tests/Issues/Issue594.cs +++ b/tests/ImageSharp.Tests/Issues/Issue594.cs @@ -31,9 +31,9 @@ namespace SixLabors.ImageSharp.Tests.Issues Assert.Equal(0, scaled.Z); Assert.Equal(0, scaled.W); - // Test PackFromScaledVector4. + // Test FromScaledVector4. var pixel = default(NormalizedByte4); - pixel.PackFromScaledVector4(scaled); + pixel.FromScaledVector4(scaled); Assert.Equal(0x81818181, pixel.PackedValue); // Test Ordering @@ -43,51 +43,51 @@ namespace SixLabors.ImageSharp.Tests.Issues float w = -0.7f; Assert.Equal(0xA740DA0D, new NormalizedByte4(x, y, z, w).PackedValue); var n = default(NormalizedByte4); - n.PackFromRgba32(new Rgba32(141, 90, 192, 39)); + n.FromRgba32(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); - var rgb = default(Rgb24); - var rgba = default(Rgba32); - var bgr = default(Bgr24); - var bgra = default(Bgra32); - var argb = default(Argb32); + //var rgb = default(Rgb24); + //var rgba = default(Rgba32); + //var bgr = default(Bgr24); + //var bgra = default(Bgra32); + //var argb = default(Argb32); - new NormalizedByte4(x, y, z, w).ToRgb24(ref rgb); - Assert.Equal(rgb, new Rgb24(141, 90, 192)); + //new NormalizedByte4(x, y, z, w).ToRgb24(ref rgb); + //Assert.Equal(rgb, new Rgb24(141, 90, 192)); - new NormalizedByte4(x, y, z, w).ToRgba32(ref rgba); - Assert.Equal(rgba, new Rgba32(141, 90, 192, 39)); + //new NormalizedByte4(x, y, z, w).ToRgba32(ref rgba); + //Assert.Equal(rgba, new Rgba32(141, 90, 192, 39)); - new NormalizedByte4(x, y, z, w).ToBgr24(ref bgr); - Assert.Equal(bgr, new Bgr24(141, 90, 192)); + //new NormalizedByte4(x, y, z, w).ToBgr24(ref bgr); + //Assert.Equal(bgr, new Bgr24(141, 90, 192)); - new NormalizedByte4(x, y, z, w).ToBgra32(ref bgra); - Assert.Equal(bgra, new Bgra32(141, 90, 192, 39)); // this assert fails in Release build on linux (#594) + //new NormalizedByte4(x, y, z, w).ToBgra32(ref bgra); + //Assert.Equal(bgra, new Bgra32(141, 90, 192, 39)); // this assert fails in Release build on linux (#594) - new NormalizedByte4(x, y, z, w).ToArgb32(ref argb); - Assert.Equal(argb, new Argb32(141, 90, 192, 39)); + //new NormalizedByte4(x, y, z, w).ToArgb32(ref argb); + //Assert.Equal(argb, new Argb32(141, 90, 192, 39)); // http://community.monogame.net/t/normalizedbyte4-texture2d-gives-different-results-from-xna/8012/8 - var r = default(NormalizedByte4); - r.PackFromRgba32(new Rgba32(9, 115, 202, 127)); - r.ToRgba32(ref rgba); - Assert.Equal(rgba, new Rgba32(9, 115, 202, 127)); - - r.PackedValue = 0xff4af389; - r.ToRgba32(ref rgba); - Assert.Equal(rgba, new Rgba32(9, 115, 202, 127)); - - r = default(NormalizedByte4); - r.PackFromArgb32(new Argb32(9, 115, 202, 127)); - r.ToArgb32(ref argb); - Assert.Equal(argb, new Argb32(9, 115, 202, 127)); - - r = default(NormalizedByte4); - r.PackFromBgra32(new Bgra32(9, 115, 202, 127)); - r.ToBgra32(ref bgra); - Assert.Equal(bgra, new Bgra32(9, 115, 202, 127)); + //var r = default(NormalizedByte4); + //r.FromRgba32(new Rgba32(9, 115, 202, 127)); + //r.ToRgba32(ref rgba); + //Assert.Equal(rgba, new Rgba32(9, 115, 202, 127)); + + //r.PackedValue = 0xff4af389; + //r.ToRgba32(ref rgba); + //Assert.Equal(rgba, new Rgba32(9, 115, 202, 127)); + + //r = default(NormalizedByte4); + //r.FromArgb32(new Argb32(9, 115, 202, 127)); + //r.ToArgb32(ref argb); + //Assert.Equal(argb, new Argb32(9, 115, 202, 127)); + + //r = default(NormalizedByte4); + //r.FromBgra32(new Bgra32(9, 115, 202, 127)); + //r.ToBgra32(ref bgra); + //Assert.Equal(bgra, new Bgra32(9, 115, 202, 127)); } // This test fails for unknown reason in Release mode on linux and is meant to help reproducing the issue @@ -114,9 +114,9 @@ namespace SixLabors.ImageSharp.Tests.Issues Assert.Equal(1, scaled.Z); Assert.Equal(1, scaled.W); - // Test PackFromScaledVector4. + // Test FromScaledVector4. var pixel = default(NormalizedShort4); - pixel.PackFromScaledVector4(scaled); + pixel.FromScaledVector4(scaled); Assert.Equal((ulong)0x7FFF7FFF7FFF7FFF, pixel.PackedValue); // Test Ordering @@ -127,41 +127,41 @@ namespace SixLabors.ImageSharp.Tests.Issues Assert.Equal(0xa6674000d99a0ccd, new NormalizedShort4(x, y, z, w).PackedValue); Assert.Equal((ulong)4150390751449251866, new NormalizedShort4(0.0008f, 0.15f, 0.30f, 0.45f).PackedValue); - var rgb = default(Rgb24); - var rgba = default(Rgba32); - var bgr = default(Bgr24); - var bgra = default(Bgra32); - var argb = default(Argb32); + //var rgb = default(Rgb24); + //var rgba = default(Rgba32); + //var bgr = default(Bgr24); + //var bgra = default(Bgra32); + //var argb = default(Argb32); - new NormalizedShort4(x, y, z, w).ToRgb24(ref rgb); - Assert.Equal(rgb, new Rgb24(141, 90, 192)); + //new NormalizedShort4(x, y, z, w).ToRgb24(ref rgb); + //Assert.Equal(rgb, new Rgb24(141, 90, 192)); - new NormalizedShort4(x, y, z, w).ToRgba32(ref rgba); - Assert.Equal(rgba, new Rgba32(141, 90, 192, 39)); // this assert fails in Release build on linux (#594) + //new NormalizedShort4(x, y, z, w).ToRgba32(ref rgba); + //Assert.Equal(rgba, new Rgba32(141, 90, 192, 39)); // this assert fails in Release build on linux (#594) - new NormalizedShort4(x, y, z, w).ToBgr24(ref bgr); - Assert.Equal(bgr, new Bgr24(141, 90, 192)); + //new NormalizedShort4(x, y, z, w).ToBgr24(ref bgr); + //Assert.Equal(bgr, new Bgr24(141, 90, 192)); - new NormalizedShort4(x, y, z, w).ToBgra32(ref bgra); - Assert.Equal(bgra, new Bgra32(141, 90, 192, 39)); + //new NormalizedShort4(x, y, z, w).ToBgra32(ref bgra); + //Assert.Equal(bgra, new Bgra32(141, 90, 192, 39)); - new NormalizedShort4(x, y, z, w).ToArgb32(ref argb); - Assert.Equal(argb, new Argb32(141, 90, 192, 39)); + //new NormalizedShort4(x, y, z, w).ToArgb32(ref argb); + //Assert.Equal(argb, new Argb32(141, 90, 192, 39)); - var r = default(NormalizedShort4); - r.PackFromRgba32(new Rgba32(9, 115, 202, 127)); - r.ToRgba32(ref rgba); - Assert.Equal(rgba, new Rgba32(9, 115, 202, 127)); + //var r = default(NormalizedShort4); + //r.FromRgba32(new Rgba32(9, 115, 202, 127)); + //r.ToRgba32(ref rgba); + //Assert.Equal(rgba, new Rgba32(9, 115, 202, 127)); - r = default(NormalizedShort4); - r.PackFromBgra32(new Bgra32(9, 115, 202, 127)); - r.ToBgra32(ref bgra); - Assert.Equal(bgra, new Bgra32(9, 115, 202, 127)); + //r = default(NormalizedShort4); + //r.FromBgra32(new Bgra32(9, 115, 202, 127)); + //r.ToBgra32(ref bgra); + //Assert.Equal(bgra, new Bgra32(9, 115, 202, 127)); - r = default(NormalizedShort4); - r.PackFromArgb32(new Argb32(9, 115, 202, 127)); - r.ToArgb32(ref argb); - Assert.Equal(argb, new Argb32(9, 115, 202, 127)); + //r = default(NormalizedShort4); + //r.FromArgb32(new Argb32(9, 115, 202, 127)); + //r.ToArgb32(ref argb); + //Assert.Equal(argb, new Argb32(9, 115, 202, 127)); } // This test fails for unknown reason in Release mode on linux and is meant to help reproducing the issue @@ -190,9 +190,9 @@ namespace SixLabors.ImageSharp.Tests.Issues Assert.Equal(1, scaled.Z); Assert.Equal(1, scaled.W); - // Test PackFromScaledVector4. + // Test FromScaledVector4. var pixel = default(Short4); - pixel.PackFromScaledVector4(scaled); + pixel.FromScaledVector4(scaled); Assert.Equal((ulong)0x7FFF7FFF7FFF7FFF, pixel.PackedValue); // Test clamping. @@ -212,41 +212,41 @@ namespace SixLabors.ImageSharp.Tests.Issues w = 193; Assert.Equal((ulong)0x00c173b7316d2d1b, new Short4(x, y, z, w).PackedValue); - var rgb = default(Rgb24); - var rgba = default(Rgba32); - var bgr = default(Bgr24); - var bgra = default(Bgra32); - var argb = default(Argb32); + //var rgb = default(Rgb24); + //var rgba = default(Rgba32); + //var bgr = default(Bgr24); + //var bgra = default(Bgra32); + //var argb = default(Argb32); - new Short4(x, y, z, w).ToRgb24(ref rgb); - Assert.Equal(rgb, new Rgb24(172, 177, 243)); // this assert fails in Release build on linux (#594) + //new Short4(x, y, z, w).ToRgb24(ref rgb); + //Assert.Equal(rgb, new Rgb24(172, 177, 243)); // this assert fails in Release build on linux (#594) - new Short4(x, y, z, w).ToRgba32(ref rgba); - Assert.Equal(rgba, new Rgba32(172, 177, 243, 128)); + //new Short4(x, y, z, w).ToRgba32(ref rgba); + //Assert.Equal(rgba, new Rgba32(172, 177, 243, 128)); - new Short4(x, y, z, w).ToBgr24(ref bgr); - Assert.Equal(bgr, new Bgr24(172, 177, 243)); + //new Short4(x, y, z, w).ToBgr24(ref bgr); + //Assert.Equal(bgr, new Bgr24(172, 177, 243)); - new Short4(x, y, z, w).ToBgra32(ref bgra); - Assert.Equal(bgra, new Bgra32(172, 177, 243, 128)); + //new Short4(x, y, z, w).ToBgra32(ref bgra); + //Assert.Equal(bgra, new Bgra32(172, 177, 243, 128)); - new Short4(x, y, z, w).ToArgb32(ref argb); - Assert.Equal(argb, new Argb32(172, 177, 243, 128)); + //new Short4(x, y, z, w).ToArgb32(ref argb); + //Assert.Equal(argb, new Argb32(172, 177, 243, 128)); - var r = default(Short4); - r.PackFromRgba32(new Rgba32(20, 38, 0, 255)); - r.ToRgba32(ref rgba); - Assert.Equal(rgba, new Rgba32(20, 38, 0, 255)); + //var r = default(Short4); + //r.FromRgba32(new Rgba32(20, 38, 0, 255)); + //r.ToRgba32(ref rgba); + //Assert.Equal(rgba, new Rgba32(20, 38, 0, 255)); - r = default(Short4); - r.PackFromBgra32(new Bgra32(20, 38, 0, 255)); - r.ToBgra32(ref bgra); - Assert.Equal(bgra, new Bgra32(20, 38, 0, 255)); + //r = default(Short4); + //r.FromBgra32(new Bgra32(20, 38, 0, 255)); + //r.ToBgra32(ref bgra); + //Assert.Equal(bgra, new Bgra32(20, 38, 0, 255)); - r = default(Short4); - r.PackFromArgb32(new Argb32(20, 38, 0, 255)); - r.ToArgb32(ref argb); - Assert.Equal(argb, new Argb32(20, 38, 0, 255)); + //r = default(Short4); + //r.FromArgb32(new Argb32(20, 38, 0, 255)); + //r.ToArgb32(ref argb); + //Assert.Equal(argb, new Argb32(20, 38, 0, 255)); } // Comparison helpers with small tolerance to allow for floating point rounding during computations. diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs index 56a4f8c0ca..845a149b5d 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.PrimitivesTests.cs @@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Tests.Icc byte[] output = writer.GetData(); Assert.Equal(0, count); - Assert.Equal(new byte[0], output); + Assert.Equal(Array.Empty(), output); } [Fact] @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Icc byte[] output = writer.GetData(); Assert.Equal(0, count); - Assert.Equal(new byte[0], output); + Assert.Equal(Array.Empty(), output); } [Theory] diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccProfileTests.cs index 2e2c92182e..17b5dacc4c 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccProfileTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccProfileTests.cs @@ -9,9 +9,6 @@ namespace SixLabors.ImageSharp.Tests.Icc { public class IccProfileTests { - -#if !NETSTANDARD1_1 - [Theory] [MemberData(nameof(IccTestDataProfiles.ProfileIdTestData), MemberType = typeof(IccTestDataProfiles))] public void CalculateHash_WithByteArray_CalculatesProfileHash(byte[] data, IccProfileId expected) @@ -33,8 +30,6 @@ namespace SixLabors.ImageSharp.Tests.Icc Assert.Equal(data, copy); } -#endif - [Theory] [MemberData(nameof(IccTestDataProfiles.ProfileValidityTestData), MemberType = typeof(IccTestDataProfiles))] public void CheckIsValid_WithProfiles_ReturnsValidity(byte[] data, bool expected) diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs index b3215ee7ae..c91076afc9 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/IccReaderTests.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Icc IccProfile output = reader.Read(IccTestDataProfiles.Header_Random_Array); - Assert.Equal(0, output.Entries.Count); + Assert.Equal(0, output.Entries.Length); Assert.NotNull(output.Header); IccProfileHeader header = output.Header; @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Tests.Icc IccProfile output = reader.Read(IccTestDataProfiles.Profile_Random_Array); - Assert.Equal(2, output.Entries.Count); + Assert.Equal(2, output.Entries.Length); Assert.True(ReferenceEquals(output.Entries[0], output.Entries[1])); } diff --git a/tests/ImageSharp.Tests/Numerics/RationalTests.cs b/tests/ImageSharp.Tests/Numerics/RationalTests.cs index a9b9106c5c..caddd49216 100644 --- a/tests/ImageSharp.Tests/Numerics/RationalTests.cs +++ b/tests/ImageSharp.Tests/Numerics/RationalTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.Primitives; using Xunit; diff --git a/tests/ImageSharp.Tests/PixelFormats/Alpha8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Alpha8Tests.cs index 9a29236dba..8f68c9d03f 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Alpha8Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Alpha8Tests.cs @@ -10,200 +10,88 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats public class Alpha8Tests { [Fact] - public void Alpha8_PackedValue() + public void Alpha8_Constructor() { // Test the limits. - Assert.Equal(0x0, new Alpha8(0F).PackedValue); - Assert.Equal(0xFF, new Alpha8(1F).PackedValue); + Assert.Equal(byte.MinValue, new Alpha8(0F).PackedValue); + Assert.Equal(byte.MaxValue, new Alpha8(1F).PackedValue); // Test clamping. - Assert.Equal(0x0, new Alpha8(-1234F).PackedValue); - Assert.Equal(0xFF, new Alpha8(1234F).PackedValue); + Assert.Equal(byte.MinValue, new Alpha8(-1234F).PackedValue); + Assert.Equal(byte.MaxValue, new Alpha8(1234F).PackedValue); // Test ordering - Assert.Equal(124, new Alpha8(124F / 0xFF).PackedValue); + Assert.Equal(124, new Alpha8(124F / byte.MaxValue).PackedValue); Assert.Equal(26, new Alpha8(0.1F).PackedValue); } [Fact] - public void Alpha8_ToVector4() - { - // arrange - var alpha = new Alpha8(.5F); - - // act - var actual = alpha.ToVector4(); - - // assert - Assert.Equal(0, actual.X); - Assert.Equal(0, actual.Y); - Assert.Equal(0, actual.Z); - Assert.Equal(.5F, actual.W, 2); - } - - [Fact] - public void Alpha8_ToScaledVector4() + public void Alpha8_Equality() { - // arrange - var alpha = new Alpha8(.5F); - - // act - Vector4 actual = alpha.ToScaledVector4(); + var left = new Alpha8(16); + var right = new Alpha8(32); - // assert - Assert.Equal(0, actual.X); - Assert.Equal(0, actual.Y); - Assert.Equal(0, actual.Z); - Assert.Equal(.5F, actual.W, 2); + Assert.True(left == new Alpha8(16)); + Assert.True(left != right); + Assert.Equal(left, (object)new Alpha8(16)); } [Fact] - public void Alpha8_PackFromScaledVector4() + public void Alpha8_FromScaledVector4() { - // arrange + // Arrange Alpha8 alpha = default; int expected = 128; Vector4 scaled = new Alpha8(.5F).ToScaledVector4(); - // act - alpha.PackFromScaledVector4(scaled); + // Act + alpha.FromScaledVector4(scaled); byte actual = alpha.PackedValue; - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Alpha8_PackFromScaledVector4_ToRgb24() - { - // arrange - Rgb24 actual = default; - Alpha8 alpha = default; - var expected = new Rgb24(0, 0, 0); - Vector4 scaled = new Alpha8(.5F).ToScaledVector4(); - - // act - alpha.PackFromScaledVector4(scaled); - alpha.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Alpha8_PackFromScaledVector4_ToRgba32() - { - // arrange - Rgba32 actual = default; - Alpha8 alpha = default; - var expected = new Rgba32(0, 0, 0, 128); - Vector4 scaled = new Alpha8(.5F).ToScaledVector4(); - - // act - alpha.PackFromScaledVector4(scaled); - alpha.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Alpha8_PackFromScaledVector4_ToBgr24() - { - // arrange - Bgr24 actual = default; - Alpha8 alpha = default; - var expected = new Bgr24(0, 0, 0); - Vector4 scaled = new Alpha8(.5F).ToScaledVector4(); - - // act - alpha.PackFromScaledVector4(scaled); - alpha.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Alpha8_PackFromScaledVector4_ToBgra32() - { - // arrange - Bgra32 actual = default; - Alpha8 alpha = default; - var expected = new Bgra32(0, 0, 0, 128); - Vector4 scaled = new Alpha8(.5F).ToScaledVector4(); - - // act - alpha.PackFromScaledVector4(scaled); - alpha.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Alpha8_PackFromScaledVector4_ToArgb32() - { - // arrange - Alpha8 alpha = default; - Argb32 actual = default; - var expected = new Argb32(0, 0, 0, 128); - Vector4 scaled = new Alpha8(.5F).ToScaledVector4(); - - // act - alpha.PackFromScaledVector4(scaled); - alpha.ToArgb32(ref actual); - - // assert + // Assert Assert.Equal(expected, actual); } [Fact] - public void Alpha8_PackFromScaledVector4_ToRgba64() + public void Alpha8_ToScaledVector4() { - // arrange - Alpha8 alpha = default; - Rgba64 actual = default; - var expected = new Rgba64(0, 0, 0, 65535); - Vector4 scaled = new Alpha8(1F).ToScaledVector4(); + // Arrange + var alpha = new Alpha8(.5F); - // act - alpha.PackFromScaledVector4(scaled); - alpha.ToRgba64(ref actual); + // Act + Vector4 actual = alpha.ToScaledVector4(); - // assert - Assert.Equal(expected, actual); + // Assert + Assert.Equal(0, actual.X); + Assert.Equal(0, actual.Y); + Assert.Equal(0, actual.Z); + Assert.Equal(.5F, actual.W, 2); } [Fact] - public void Alpha8_PackFromRgb48_ToRgb48() + public void Alpha8_ToVector4() { - // arrange - var alpha = default(Alpha8); - var actual = default(Rgb48); - var expected = new Rgb48(0, 0, 0); + // Arrange + var alpha = new Alpha8(.5F); - // act - alpha.PackFromRgb48(expected); - alpha.ToRgb48(ref actual); + // Act + var actual = alpha.ToVector4(); - // assert - Assert.Equal(expected, actual); + // Assert + Assert.Equal(0, actual.X); + Assert.Equal(0, actual.Y); + Assert.Equal(0, actual.Z); + Assert.Equal(.5F, actual.W, 2); } [Fact] - public void Alpha8_PackFromRgba64_ToRgba64() + public void Alpha8_ToRgba32() { - // arrange - var alpha = default(Alpha8); - var actual = default(Rgba64); - var expected = new Rgba64(0, 0, 0, 65535); - - // act - alpha.PackFromRgba64(expected); - alpha.ToRgba64(ref actual); + var input = new Alpha8(128); + var expected = new Rgba32(0, 0, 0, 128); - // assert + Rgba32 actual = default; + input.ToRgba32(ref actual); Assert.Equal(expected, actual); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs index 5817b5c329..b9f7414900 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Argb32Tests.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Argb32_PackFromScaledVector4() + public void Argb32_FromScaledVector4() { // arrange Vector4 scaled = new Argb32(Vector4.One).ToScaledVector4(); @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats uint expected = 0xFFFFFFFF; // act - pixel.PackFromScaledVector4(scaled); + pixel.FromScaledVector4(scaled); uint actual = pixel.PackedValue; // assert @@ -66,160 +66,5 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(Vector4.Zero, new Argb32(Vector4.One * -1234.0f).ToVector4()); Assert.Equal(Vector4.One, new Argb32(Vector4.One * +1234.0f).ToVector4()); } - - [Fact] - public void Argb32_ToRgb24() - { - // arrange - var argb = new Argb32(+0.1f, -0.3f, +0.5f, -0.7f); - var actual = default(Rgb24); - var expected = new Rgb24(0x1a, 0, 0x80); - - // act - argb.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Argb32_ToRgba32() - { - // arrange - var argb = new Argb32(+0.1f, -0.3f, +0.5f, -0.7f); - var actual = default(Rgba32); - var expected = new Rgba32(0x1a, 0, 0x80, 0); - - // act - argb.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Argb32_ToBgr24() - { - // arrange - var argb = new Argb32(+0.1f, -0.3f, +0.5f, -0.7f); - var actual = default(Bgr24); - var expected = new Bgr24(0x1a, 0, 0x80); - - // act - argb.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Argb32_ToBgra32() - { - // arrange - var argb = new Argb32(+0.1f, -0.3f, +0.5f, -0.7f); - var actual = default(Bgra32); - var expected = new Bgra32(0x1a, 0, 0x80, 0); - - // act - argb.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Argb32_ToArgb32() - { - // arrange - var argb = new Argb32(+0.1f, -0.3f, +0.5f, -0.7f); - var actual = default(Argb32); - var expected = new Argb32(0x1a, 0, 0x80, 0); - - // act - argb.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Argb32_PackFromRgba32_ToRgba32() - { - // arrange - var argb = default(Argb32); - var actual = default(Rgba32); - var expected = new Rgba32(0x1a, 0, 0x80, 0); - - // act - argb.PackFromRgba32(expected); - argb.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Argb32_PackFromBgra32_ToBgra32() - { - // arrange - var argb = default(Argb32); - var actual = default(Bgra32); - var expected = new Bgra32(0x1a, 0, 0x80, 0); - - // act - argb.PackFromBgra32(expected); - argb.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Argb32_PackFromArgb32_ToArgb32() - { - // arrange - var argb = default(Argb32); - var actual = default(Argb32); - var expected = new Argb32(0x1a, 0, 0x80, 0); - - // act - argb.PackFromArgb32(expected); - argb.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Argb32_PackFromRgb48_ToRgb48() - { - // arrange - var argb = default(Argb32); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); - - // act - argb.PackFromRgb48(expected); - argb.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Argb32_PackFromRgba64_ToRgba64() - { - // arrange - var argb = default(Argb32); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 0); - - // act - argb.PackFromRgba64(expected); - argb.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs index 048a38380e..2295fbe56f 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr24Tests.cs @@ -61,10 +61,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Fact] - public void PackFromRgba32() + public void FromRgba32() { var rgb = default(Bgr24); - rgb.PackFromRgba32(new Rgba32(1, 2, 3, 4)); + rgb.FromRgba32(new Rgba32(1, 2, 3, 4)); Assert.Equal(1, rgb.R); Assert.Equal(2, rgb.G); @@ -78,10 +78,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats a / 255f); [Fact] - public void PackFromVector4() + public void FromVector4() { var rgb = default(Bgr24); - rgb.PackFromVector4(Vec(1, 2, 3, 4)); + rgb.FromVector4(Vec(1, 2, 3, 4)); Assert.Equal(1, rgb.R); Assert.Equal(2, rgb.G); @@ -95,81 +95,5 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats 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); - } - - [Fact] - public void Bgr24_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(Bgr24); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgr24_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(Bgr24); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 65535); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs index b66cac9ca3..967e358e1e 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgr565Tests.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Bgr565_PackFromScaledVector4() + public void Bgr565_FromScaledVector4() { // arrange Vector4 scaled = new Bgr565(Vector3.One).ToScaledVector4(); @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var pixel = default(Bgr565); // act - pixel.PackFromScaledVector4(scaled); + pixel.FromScaledVector4(scaled); ushort actual = pixel.PackedValue; // assert @@ -69,112 +69,5 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(Vector3.Zero, new Bgr565(Vector3.One * -1234F).ToVector3()); Assert.Equal(Vector3.One, new Bgr565(Vector3.One * 1234F).ToVector3()); } - - [Fact] - public void Bgr565_ToRgb24() - { - // arrange - var bgra = new Bgr565(0.1F, -0.3F, 0.5F); - var actual = default(Rgb24); - var expected = new Rgb24(25, 0, 132); - - // act - bgra.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgr565_ToRgba32() - { - // arrange - var bgra = new Bgr565(0.1F, -0.3F, 0.5F); - var actual = default(Rgba32); - var expected = new Rgba32(25, 0, 132, 255); - - // act - bgra.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgr565_ToBgr24() - { - // arrange - var bgra = new Bgr565(0.1F, -0.3F, 0.5F); - var actual = default(Bgr24); - var expected = new Bgr24(25, 0, 132); - - // act - bgra.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgr565_ToBgra32() - { - // arrange - var bgra = new Bgr565(0.1F, -0.3F, 0.5F); - var actual = default(Bgra32); - var expected = new Bgra32(25, 0, 132, 255); - - // act - bgra.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgr565_ToArgb32() - { - // arrange - var bgra = new Bgr565(0.1F, -0.3F, 0.5F); - var actual = default(Argb32); - var expected = new Argb32(25, 0, 132, 255); - - // act - bgra.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgr565_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(Bgr565); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgr565_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(Bgr565); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 65535); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs index 70f8c35dfc..a5c53ed8b0 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra32Tests.cs @@ -67,10 +67,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Fact] - public void PackFromRgba32() + public void FromRgba32() { var rgb = default(Rgb24); - rgb.PackFromRgba32(new Rgba32(1, 2, 3, 4)); + rgb.FromRgba32(new Rgba32(1, 2, 3, 4)); Assert.Equal(1, rgb.R); Assert.Equal(2, rgb.G); @@ -84,10 +84,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats a / 255f); [Fact] - public void PackFromVector4() + public void FromVector4() { var c = default(Bgra32); - c.PackFromVector4(Vec(1, 2, 3, 4)); + c.FromVector4(Vec(1, 2, 3, 4)); Assert.Equal(1, c.R); Assert.Equal(2, c.G); @@ -102,81 +102,5 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats 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); - } - - [Fact] - public void Bgra32_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(Bgra32); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra32_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(Bgra32); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 0); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs index f643d152ef..8b56ec19fe 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra4444Tests.cs @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Bgra4444_PackFromScaledVector4() + public void Bgra4444_FromScaledVector4() { // arrange Vector4 scaled = new Bgra4444(Vector4.One).ToScaledVector4(); @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var bgra = default(Bgra4444); // act - bgra.PackFromScaledVector4(scaled); + bgra.FromScaledVector4(scaled); ushort actual = bgra.PackedValue; // assert @@ -70,160 +70,5 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(Vector4.Zero, new Bgra4444(Vector4.One * -1234.0f).ToVector4()); Assert.Equal(Vector4.One, new Bgra4444(Vector4.One * 1234.0f).ToVector4()); } - - [Fact] - public void Bgra4444_ToRgb24() - { - // arrange - var bgra = new Bgra4444(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Rgb24); - var expected = new Rgb24(34, 0, 136); - - // act - bgra.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra4444_ToRgba32() - { - // arrange - var bgra = new Bgra4444(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Rgba32); - var expected = new Rgba32(34, 0, 136, 0); - - // act - bgra.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra4444_ToBgr24() - { - // arrange - var bgra = new Bgra4444(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Bgr24); - var expected = new Bgr24(34, 0, 136); - - // act - bgra.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra4444_ToBgra32() - { - // arrange - var bgra = new Bgra4444(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Bgra32); - var expected = new Bgra32(34, 0, 136, 0); - - // act - bgra.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra4444_ToArgb32() - { - // arrange - var bgra = new Bgra4444(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Argb32); - var expected = new Argb32(34, 0, 136, 0); - - // act - bgra.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra4444_PackFromRgba32_ToRgba32() - { - // arrange - var bgra = default(Bgra4444); - var actual = default(Rgba32); - var expected = new Rgba32(34, 0, 136, 0); - - // act - bgra.PackFromRgba32(expected); - bgra.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra4444_PackFromBgra32_ToBgra32() - { - // arrange - var bgra = default(Bgra4444); - var actual = default(Bgra32); - var expected = new Bgra32(34, 0, 136, 0); - - // act - bgra.PackFromBgra32(expected); - bgra.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra4444_PackFromArgb32_ToArgb32() - { - // arrange - var bgra = default(Bgra4444); - var actual = default(Argb32); - var expected = new Argb32(34, 0, 136, 0); - - // act - bgra.PackFromArgb32(expected); - bgra.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra4444_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(Bgra4444); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra4444_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(Bgra4444); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 0); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs index b6a0780312..76edee8a73 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Bgra5551Tests.cs @@ -47,16 +47,16 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Bgra5551_PackFromScaledVector4() + public void Bgra5551_FromScaledVector4() { // arrange Vector4 scaled = new Bgra5551(Vector4.One).ToScaledVector4(); int expected = 0xFFFF; var pixel = default(Bgra5551); - pixel.PackFromScaledVector4(scaled); + pixel.FromScaledVector4(scaled); // act - pixel.PackFromScaledVector4(scaled); + pixel.FromScaledVector4(scaled); ushort actual = pixel.PackedValue; // assert @@ -69,160 +69,5 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(Vector4.Zero, new Bgra5551(Vector4.One * -1234.0f).ToVector4()); Assert.Equal(Vector4.One, new Bgra5551(Vector4.One * 1234.0f).ToVector4()); } - - [Fact] - public void Bgra5551_ToRgb24() - { - // arrange - var bgra = new Bgra5551(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Rgb24); - var expected = new Rgb24(24, 0, 131); - - // act - bgra.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra5551_Rgba32() - { - // arrange - var bgra = new Bgra5551(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Rgba32); - var expected = new Rgba32(24, 0, 131, 0); - - // act - bgra.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra5551_ToBgr24() - { - // arrange - var bgra = new Bgra5551(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Bgr24); - var expected = new Bgr24(24, 0, 131); - - // act - bgra.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra5551_Bgra32() - { - // arrange - var bgra = new Bgra5551(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Bgra32); - var expected = new Bgra32(24, 0, 131, 0); - - // act - bgra.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra5551_ToArgb32() - { - // arrange - var bgra = new Bgra5551(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Argb32); - var expected = new Argb32(24, 0, 131, 0); - - // act - bgra.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra5551_PackFromRgba32_ToRgba32() - { - // arrange - var bgra = default(Bgra5551); - var expected = new Rgba32(24, 0, 131, 0); - var actual = default(Rgba32); - - // act - bgra.PackFromRgba32(expected); - bgra.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra5551_PackFromBgra32_ToBgra32() - { - // arrange - var bgra = default(Bgra5551); - var expected = new Bgra32(24, 0, 131, 0); - var actual = default(Bgra32); - - // act - bgra.PackFromBgra32(expected); - bgra.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra5551_PackFromArgb32_ToArgb32() - { - // arrange - var bgra = default(Bgra5551); - var expected = new Argb32(24, 0, 131, 0); - var actual = default(Argb32); - - // act - bgra.PackFromArgb32(expected); - bgra.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra5551_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(Bgra5551); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Bgra5551_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(Bgra5551); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 0); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs index 28555a7dff..8391ef25ae 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Byte4Tests.cs @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Byte4_PackFromScaledVector4() + public void Byte4_FromScaledVector4() { // arrange Vector4 scaled = new Byte4(Vector4.One * 255).ToScaledVector4(); @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats uint expected = 0xFFFFFFFF; // act - pixel.PackFromScaledVector4(scaled); + pixel.FromScaledVector4(scaled); uint actual = pixel.PackedValue; // assert @@ -67,160 +67,5 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(Vector4.Zero, new Byte4(Vector4.One * -1234.0f).ToVector4()); Assert.Equal(Vector4.One * 255, new Byte4(Vector4.One * 1234.0f).ToVector4()); } - - [Fact] - public void Byte4_ToRgb24() - { - // arrange - var byte4 = new Byte4(127.5f, -12.3f, 0.5f, -0.7f); - var actual = default(Rgb24); - var expected = new Rgb24(128, 0, 0); - - // act - byte4.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Byte4_Rgba32() - { - // arrange - var byte4 = new Byte4(127.5f, -12.3f, 0.5f, -0.7f); - var actual = default(Rgba32); - var expected = new Rgba32(128, 0, 0, 0); - - // act - byte4.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Byte4_ToBgr24() - { - // arrange - var byte4 = new Byte4(127.5f, -12.3f, 0.5f, -0.7f); - var actual = default(Bgr24); - var expected = new Bgr24(128, 0, 0); - - // act - byte4.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Byte4_Bgra32() - { - // arrange - var byte4 = new Byte4(127.5f, -12.3f, 0.5f, -0.7f); - var actual = default(Bgra32); - var expected = new Bgra32(128, 0, 0, 0); - - // act - byte4.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Byte4_Argb32() - { - // arrange - var byte4 = new Byte4(127.5f, -12.3f, 0.5f, -0.7f); - var actual = default(Argb32); - var expected = new Argb32(128, 0, 0, 0); - - // act - byte4.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Byte4_PackFromRgba32_ToRgba32() - { - // arrange - var byte4 = default(Byte4); - var actual = default(Rgba32); - var expected = new Rgba32(20, 38, 0, 255); - - // act - byte4.PackFromRgba32(expected); - byte4.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Byte4_PackFromBgra32_ToBgra32() - { - // arrange - var byte4 = default(Byte4); - var actual = default(Bgra32); - var expected = new Bgra32(20, 38, 0, 255); - - // act - byte4.PackFromBgra32(expected); - byte4.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Byte4_PackFromArgb32_ToArgb32() - { - // arrange - var byte4 = default(Byte4); - var actual = default(Argb32); - var expected = new Argb32(20, 38, 0, 255); - - // act - byte4.PackFromArgb32(expected); - byte4.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Byte4_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(Byte4); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Byte4_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(Byte4); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 0); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/ColorConstructorTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorConstructorTests.cs deleted file mode 100644 index b0d5929f49..0000000000 --- a/tests/ImageSharp.Tests/PixelFormats/ColorConstructorTests.cs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Collections.Generic; -using System.Numerics; -using SixLabors.ImageSharp.PixelFormats; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Colors -{ - public class ColorConstructorTests - { - public static IEnumerable Vector4Data - { - get - { - Vector4[] vector4Values = new Vector4[] - { - Vector4.Zero, - Vector4.One, - Vector4.UnitX, - Vector4.UnitY, - Vector4.UnitZ, - Vector4.UnitW, - }; - - foreach (Vector4 vector4 in vector4Values) - { - // using float array to work around a bug in xunit corruptint the state of any Vector4 passed as MemberData - float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - - yield return new object[] { new Argb32(vector4), vector4Components }; - yield return new object[] { new Bgra4444(vector4), vector4Components }; - yield return new object[] { new Bgra5551(vector4), vector4Components }; - yield return new object[] { new Byte4(vector4), vector4Components }; - yield return new object[] { new HalfVector4(vector4), vector4Components }; - yield return new object[] { new NormalizedByte4(vector4), vector4Components }; - yield return new object[] { new NormalizedShort4(vector4), vector4Components }; - yield return new object[] { new Rgba1010102(vector4), vector4Components }; - yield return new object[] { new Rgba64(vector4), vector4Components }; - yield return new object[] { new Short4(vector4), vector4Components }; - } - } - } - - public static IEnumerable Vector3Data - { - get - { - Dictionary vector3Values = new Dictionary() - { - { Vector3.One, Vector4.One }, - { Vector3.Zero, new Vector4(0, 0, 0, 1) }, - { Vector3.UnitX, new Vector4(1, 0, 0, 1) }, - { Vector3.UnitY, new Vector4(0, 1, 0, 1) }, - { Vector3.UnitZ, new Vector4(0, 0, 1, 1) }, - }; - - foreach (Vector3 vector3 in vector3Values.Keys) - { - Vector4 vector4 = vector3Values[vector3]; - // using float array to work around a bug in xunit corruptint the state of any Vector4 passed as MemberData - float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - - yield return new object[] { new Argb32(vector3), vector4Components }; - yield return new object[] { new Bgr565(vector3), vector4Components }; - } - } - } - - public static IEnumerable Float4Data - { - get - { - Vector4[] vector4Values = new Vector4[] - { - Vector4.Zero, - Vector4.One, - Vector4.UnitX, - Vector4.UnitY, - Vector4.UnitZ, - Vector4.UnitW, - }; - - foreach (Vector4 vector4 in vector4Values) - { - // using float array to work around a bug in xunit corruptint the state of any Vector4 passed as MemberData - float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - - yield return new object[] { new Argb32(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; - yield return new object[] { new Bgra4444(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; - yield return new object[] { new Bgra5551(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; - yield return new object[] { new Byte4(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; - yield return new object[] { new HalfVector4(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; - yield return new object[] { new NormalizedByte4(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; - yield return new object[] { new NormalizedShort4(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; - yield return new object[] { new Rgba1010102(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; - yield return new object[] { new Rgba64(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; - yield return new object[] { new Short4(vector4.X, vector4.Y, vector4.Z, vector4.W), vector4Components }; - } - } - } - - public static IEnumerable Float3Data - { - get - { - Dictionary vector3Values = new Dictionary() - { - { Vector3.One, Vector4.One }, - { Vector3.Zero, new Vector4(0, 0, 0, 1) }, - { Vector3.UnitX, new Vector4(1, 0, 0, 1) }, - { Vector3.UnitY, new Vector4(0, 1, 0, 1) }, - { Vector3.UnitZ, new Vector4(0, 0, 1, 1) }, - }; - - foreach (Vector3 vector3 in vector3Values.Keys) - { - Vector4 vector4 = vector3Values[vector3]; - // using float array to work around a bug in xunit corruptint the state of any Vector4 passed as MemberData - float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - - yield return new object[] { new Argb32(vector3.X, vector3.Y, vector3.Z), vector4Components }; - yield return new object[] { new Bgr565(vector3.X, vector3.Y, vector3.Z), vector4Components }; - } - } - } - - [Theory] - [MemberData(nameof(Vector4Data))] - [MemberData(nameof(Vector3Data))] - [MemberData(nameof(Float4Data))] - [MemberData(nameof(Float3Data))] - public void ConstructorToVector4(IPixel packedVector, float[] expectedVector4Components) - { - // Arrange - int precision = 2; - // using float array to work around a bug in xunit corruptint the state of any Vector4 passed as MemberData - Vector4 expectedVector4 = new Vector4(expectedVector4Components[0], expectedVector4Components[1], expectedVector4Components[2], expectedVector4Components[3]); - - // Act - Vector4 vector4 = packedVector.ToVector4(); - - // Assert - Assert.Equal(expectedVector4.X, vector4.X, precision); - Assert.Equal(expectedVector4.Y, vector4.Y, precision); - Assert.Equal(expectedVector4.Z, vector4.Z, precision); - Assert.Equal(expectedVector4.W, vector4.W, precision); - } - } -} diff --git a/tests/ImageSharp.Tests/PixelFormats/ColorEqualityTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorEqualityTests.cs deleted file mode 100644 index d3815f2eb6..0000000000 --- a/tests/ImageSharp.Tests/PixelFormats/ColorEqualityTests.cs +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Numerics; -using SixLabors.ImageSharp.PixelFormats; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Colors -{ - /// - /// Test implementations of IEquatable - /// - public class ColorEqualityTests - { - public static readonly TheoryData EqualityData = - new TheoryData() - { - { new Alpha8(.5F), new Alpha8(.5F), typeof(Alpha8) }, - { new Argb32(Vector4.One), new Argb32(Vector4.One), typeof(Argb32) }, - { new Bgr565(Vector3.One), new Bgr565(Vector3.One), typeof(Bgr565) }, - { new Bgra4444(Vector4.One), new Bgra4444(Vector4.One), typeof(Bgra4444) }, - { new Bgra5551(Vector4.One), new Bgra5551(Vector4.One), typeof(Bgra5551) }, - { new Byte4(Vector4.One * 255), new Byte4(Vector4.One * 255), typeof(Byte4) }, - { new HalfSingle(-1F), new HalfSingle(-1F), typeof(HalfSingle) }, - { new HalfVector2(0.1f, -0.3f), new HalfVector2(0.1f, -0.3f), typeof(HalfVector2) }, - { new HalfVector4(Vector4.One), new HalfVector4(Vector4.One), typeof(HalfVector4) }, - { new NormalizedByte2(-Vector2.One), new NormalizedByte2(-Vector2.One), typeof(NormalizedByte2) }, - { new NormalizedByte4(Vector4.One), new NormalizedByte4(Vector4.One), typeof(NormalizedByte4) }, - { new NormalizedShort2(Vector2.One), new NormalizedShort2(Vector2.One), typeof(NormalizedShort2) }, - { new NormalizedShort4(Vector4.One), new NormalizedShort4(Vector4.One), typeof(NormalizedShort4) }, - { new Rg32(Vector2.One), new Rg32(Vector2.One), typeof(Rg32) }, - { new Rgba1010102(Vector4.One), new Rgba1010102(Vector4.One), typeof(Rgba1010102) }, - { new Rgba32(Vector4.One), new Rgba32(Vector4.One), typeof(Rgba32) }, - { new Rgba64(Vector4.One), new Rgba64(Vector4.One), typeof(Rgba64) }, - { new Short2(Vector2.One * 0x7FFF), new Short2(Vector2.One * 0x7FFF), typeof(Short2) }, - { new Short4(Vector4.One * 0x7FFF), new Short4(Vector4.One * 0x7FFF), typeof(Short4) }, - }; - - public static readonly TheoryData NotEqualityDataNulls = - new TheoryData() - { - // Valid object against null - { new Alpha8(.5F), null, typeof(Alpha8) }, - { new Argb32(Vector4.One), null, typeof(Argb32) }, - { new Bgr565(Vector3.One), null, typeof(Bgr565) }, - { new Bgra4444(Vector4.One), null, typeof(Bgra4444) }, - { new Bgra5551(Vector4.One), null, typeof(Bgra5551) }, - { new Byte4(Vector4.One * 255), null, typeof(Byte4) }, - { new HalfSingle(-1F), null, typeof(HalfSingle) }, - { new HalfVector2(0.1f, -0.3f), null, typeof(HalfVector2) }, - { new HalfVector4(Vector4.One), null, typeof(HalfVector4) }, - { new NormalizedByte2(-Vector2.One), null, typeof(NormalizedByte2) }, - { new NormalizedByte4(Vector4.One), null, typeof(NormalizedByte4) }, - { new NormalizedShort2(Vector2.One), null, typeof(NormalizedShort2) }, - { new NormalizedShort4(Vector4.One), null, typeof(NormalizedShort4) }, - { new Rg32(Vector2.One), null, typeof(Rg32) }, - { new Rgba1010102(Vector4.One), null, typeof(Rgba1010102) }, - { new Rgba64(Vector4.One), null, typeof(Rgba64) }, - { new Short2(Vector2.One * 0x7FFF), null, typeof(Short2) }, - { new Short4(Vector4.One * 0x7FFF), null, typeof(Short4) }, - }; - - public static readonly TheoryData NotEqualityDataDifferentObjects = - new TheoryData() - { - // Valid objects of different types but not equal - { new Alpha8(.5F), new Argb32(Vector4.Zero), null }, - { new HalfSingle(-1F), new NormalizedShort2(Vector2.Zero), null }, - { new Rgba1010102(Vector4.One), new Bgra5551(Vector4.Zero), null }, - }; - - public static readonly TheoryData NotEqualityData = - new TheoryData() - { - // Valid objects of the same type but not equal - { new Alpha8(.5F), new Alpha8(.8F), typeof(Alpha8) }, - { new Argb32(Vector4.One), new Argb32(Vector4.Zero), typeof(Argb32) }, - { new Bgr565(Vector3.One), new Bgr565(Vector3.Zero), typeof(Bgr565) }, - { new Bgra4444(Vector4.One), new Bgra4444(Vector4.Zero), typeof(Bgra4444) }, - { new Bgra5551(Vector4.One), new Bgra5551(Vector4.Zero), typeof(Bgra5551) }, - { new Byte4(Vector4.One * 255), new Byte4(Vector4.Zero), typeof(Byte4) }, - { new HalfSingle(-1F), new HalfSingle(1F), typeof(HalfSingle) }, - { new HalfVector2(0.1f, -0.3f), new HalfVector2(0.1f, 0.3f), typeof(HalfVector2) }, - { new HalfVector4(Vector4.One), new HalfVector4(Vector4.Zero), typeof(HalfVector4) }, - { new NormalizedByte2(-Vector2.One), new NormalizedByte2(-Vector2.Zero), typeof(NormalizedByte2) }, - { new NormalizedByte4(Vector4.One), new NormalizedByte4(Vector4.Zero), typeof(NormalizedByte4) }, - { new NormalizedShort2(Vector2.One), new NormalizedShort2(Vector2.Zero), typeof(NormalizedShort2) }, - { new NormalizedShort4(Vector4.One), new NormalizedShort4(Vector4.Zero), typeof(NormalizedShort4) }, - { new Rg32(Vector2.One), new Rg32(Vector2.Zero), typeof(Rg32) }, - { new Rgba1010102(Vector4.One), new Rgba1010102(Vector4.Zero), typeof(Rgba1010102) }, - { new Rgba32(Vector4.One), new Rgba32(Vector4.Zero), typeof(Rgba32) }, - { new Rgba64(Vector4.One), new Rgba64(Vector4.Zero), typeof(Rgba64) }, - { new Short2(Vector2.One * 0x7FFF), new Short2(Vector2.Zero), typeof(Short2) }, - { new Short4(Vector4.One * 0x7FFF), new Short4(Vector4.Zero), typeof(Short4) }, - }; - - [Theory] - [MemberData(nameof(EqualityData))] - public void Equality(object first, object second, Type type) - { - // Act - bool equal = first.Equals(second); - - // Assert - Assert.True(equal); - } - - [Theory] - [MemberData(nameof(NotEqualityDataNulls))] - [MemberData(nameof(NotEqualityDataDifferentObjects))] - [MemberData(nameof(NotEqualityData))] - public void NotEquality(object first, object second, Type type) - { - // Act - bool equal = first.Equals(second); - - // Assert - Assert.False(equal); - } - - [Theory] - [MemberData(nameof(EqualityData))] - public void HashCodeEqual(object first, object second, Type type) - { - // Act - bool equal = first.GetHashCode() == second.GetHashCode(); - - // Assert - Assert.True(equal); - } - - [Theory] - [MemberData(nameof(NotEqualityDataDifferentObjects))] - public void HashCodeNotEqual(object first, object second, Type type) - { - // Act - bool equal = first.GetHashCode() == second.GetHashCode(); - - // Assert - Assert.False(equal); - } - - [Theory] - [MemberData(nameof(EqualityData))] - public void EqualityObject(object first, object second, Type type) - { - // Arrange - // Cast to the known object types, this is so that we can hit the - // equality operator on the concrete type, otherwise it goes to the - // default "object" one :) - dynamic firstObject = Convert.ChangeType(first, type); - dynamic secondObject = Convert.ChangeType(second, type); - - // Act - dynamic equal = firstObject.Equals(secondObject); - - // Assert - Assert.True(equal); - } - - [Theory] - [MemberData(nameof(NotEqualityData))] - public void NotEqualityObject(object first, object second, Type type) - { - // Arrange - // Cast to the known object types, this is so that we can hit the - // equality operator on the concrete type, otherwise it goes to the - // default "object" one :) - dynamic firstObject = Convert.ChangeType(first, type); - dynamic secondObject = Convert.ChangeType(second, type); - - // Act - dynamic equal = firstObject.Equals(secondObject); - - // Assert - Assert.False(equal); - } - - [Theory] - [MemberData(nameof(EqualityData))] - public void EqualityOperator(object first, object second, Type type) - { - // Arrange - // Cast to the known object types, this is so that we can hit the - // equality operator on the concrete type, otherwise it goes to the - // default "object" one :) - dynamic firstObject = Convert.ChangeType(first, type); - dynamic secondObject = Convert.ChangeType(second, type); - - // Act - dynamic equal = firstObject == secondObject; - - // Assert - Assert.True(equal); - } - - [Theory] - [MemberData(nameof(NotEqualityData))] - public void NotEqualityOperator(object first, object second, Type type) - { - // Arrange - // Cast to the known object types, this is so that we can hit the - // equality operator on the concrete type, otherwise it goes to the - // default "object" one :) - dynamic firstObject = Convert.ChangeType(first, type); - dynamic secondObject = Convert.ChangeType(second, type); - - // Act - dynamic notEqual = firstObject != secondObject; - - // Assert - Assert.True(notEqual); - } - } -} diff --git a/tests/ImageSharp.Tests/PixelFormats/ColorPackingTests.cs b/tests/ImageSharp.Tests/PixelFormats/ColorPackingTests.cs deleted file mode 100644 index c9a1c8fe7e..0000000000 --- a/tests/ImageSharp.Tests/PixelFormats/ColorPackingTests.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Collections.Generic; -using System.Numerics; -using SixLabors.ImageSharp.PixelFormats; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Colors -{ - public class ColorPackingTests - { - public static IEnumerable Vector4PackData - { - get - { - Vector4[] vector4Values = new Vector4[] - { - Vector4.Zero, - Vector4.One, - Vector4.UnitX, - Vector4.UnitY, - Vector4.UnitZ, - Vector4.UnitW, - }; - - foreach (Vector4 vector4 in vector4Values) - { - float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - - yield return new object[] { default(Argb32), vector4Components }; - yield return new object[] { default(Bgra4444), vector4Components }; - yield return new object[] { default(Bgra5551), vector4Components }; - yield return new object[] { default(Byte4), vector4Components }; - yield return new object[] { default(HalfVector4), vector4Components }; - yield return new object[] { default(NormalizedByte4), vector4Components }; - yield return new object[] { default(NormalizedShort4), vector4Components }; - yield return new object[] { default(Rgba1010102), vector4Components }; - yield return new object[] { default(Rgba64), vector4Components }; - yield return new object[] { default(Short4), vector4Components }; - } - } - } - - public static IEnumerable Vector3PackData - { - get - { - Vector4[] vector4Values = new Vector4[] - { - Vector4.One, - new Vector4(0, 0, 0, 1), - new Vector4(1, 0, 0, 1), - new Vector4(0, 1, 0, 1), - new Vector4(0, 0, 1, 1), - }; - - foreach (Vector4 vector4 in vector4Values) - { - float[] vector4Components = new float[] { vector4.X, vector4.Y, vector4.Z, vector4.W }; - - yield return new object[] { default(Argb32), vector4Components }; - yield return new object[] { new Bgr565(), vector4Components }; - } - } - } - - [Theory] - [MemberData(nameof(Vector4PackData))] - [MemberData(nameof(Vector3PackData))] - public void FromVector4ToVector4(IPixel packedVector, float[] vector4ComponentsToPack) - { - // Arrange - int precision = 2; - Vector4 vector4ToPack = new Vector4(vector4ComponentsToPack[0], vector4ComponentsToPack[1], vector4ComponentsToPack[2], vector4ComponentsToPack[3]); - packedVector.PackFromVector4(vector4ToPack); - - // Act - Vector4 vector4 = packedVector.ToVector4(); - - // Assert - Assert.Equal(vector4ToPack.X, vector4.X, precision); - Assert.Equal(vector4ToPack.Y, vector4.Y, precision); - Assert.Equal(vector4ToPack.Z, vector4.Z, precision); - Assert.Equal(vector4ToPack.W, vector4.W, precision); - } - } -} diff --git a/tests/ImageSharp.Tests/PixelFormats/Gray16Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Gray16Tests.cs new file mode 100644 index 0000000000..cb19c031d0 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/Gray16Tests.cs @@ -0,0 +1,130 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.PixelFormats +{ + public class Gray16Tests + { + [Theory] + [InlineData(0)] + [InlineData(65535)] + [InlineData(32767)] + [InlineData(42)] + public void Gray16_PackedValue_EqualsInput(ushort input) + => Assert.Equal(input, new Gray16(input).PackedValue); + + [Fact] + public void Gray16_FromScaledVector4() + { + // Arrange + Gray16 gray = default; + const ushort expected = 32767; + Vector4 scaled = new Gray16(expected).ToScaledVector4(); + + // Act + gray.FromScaledVector4(scaled); + ushort actual = gray.PackedValue; + + // Assert + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(0)] + [InlineData(65535)] + [InlineData(32767)] + public void Gray16_ToScaledVector4(ushort input) + { + // Arrange + var gray = new Gray16(input); + + // Act + Vector4 actual = gray.ToScaledVector4(); + + // Assert + float vectorInput = input / 65535F; + Assert.Equal(vectorInput, actual.X); + Assert.Equal(vectorInput, actual.Y); + Assert.Equal(vectorInput, actual.Z); + Assert.Equal(1F, actual.W); + } + + [Fact] + public void Gray16_FromVector4() + { + // Arrange + Gray16 gray = default; + const ushort expected = 32767; + var vector = new Gray16(expected).ToVector4(); + + // Act + gray.FromVector4(vector); + ushort actual = gray.PackedValue; + + // Assert + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(0)] + [InlineData(65535)] + [InlineData(32767)] + public void Gray16_ToVector4(ushort input) + { + // Arrange + var gray = new Gray16(input); + + // Act + var actual = gray.ToVector4(); + + // Assert + float vectorInput = input / 65535F; + Assert.Equal(vectorInput, actual.X); + Assert.Equal(vectorInput, actual.Y); + Assert.Equal(vectorInput, actual.Z); + Assert.Equal(1F, actual.W); + } + + [Fact] + public void Gray16_FromRgba32() + { + // Arrange + Gray16 gray = default; + const byte rgb = 128; + ushort scaledRgb = ImageMaths.UpscaleFrom8BitTo16Bit(rgb); + ushort expected = ImageMaths.Get16BitBT709Luminance(scaledRgb, scaledRgb, scaledRgb); + + // Act + gray.FromRgba32(new Rgba32(rgb, rgb, rgb)); + ushort actual = gray.PackedValue; + + // Assert + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(0)] + [InlineData(65535)] + [InlineData(8100)] + public void Gray16_ToRgba32(ushort input) + { + // Arrange + ushort expected = ImageMaths.DownScaleFrom16BitTo8Bit(input); + var gray = new Gray16(input); + + // Act + Rgba32 actual = default; + gray.ToRgba32(ref actual); + + // Assert + Assert.Equal(expected, actual.R); + Assert.Equal(expected, actual.G); + Assert.Equal(expected, actual.B); + Assert.Equal(byte.MaxValue, actual.A); + } + } +} diff --git a/tests/ImageSharp.Tests/PixelFormats/Gray8Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Gray8Tests.cs new file mode 100644 index 0000000000..1e17985e60 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/Gray8Tests.cs @@ -0,0 +1,252 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Diagnostics; +using System.Numerics; + +using SixLabors.ImageSharp.Common.Helpers; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; + +// ReSharper disable InconsistentNaming +namespace SixLabors.ImageSharp.Tests.PixelFormats +{ + public class Gray8Tests + { + public static readonly TheoryData LuminanceData = new TheoryData() + { + 0, + 1, + 2, + 3, + 5, + 13, + 31, + 71, + 73, + 79, + 83, + 109, + 127, + 128, + 131, + 199, + 250, + 251, + 254, + 255 + }; + + [Theory] + [InlineData(0)] + [InlineData(255)] + [InlineData(10)] + [InlineData(42)] + public void Gray8_PackedValue_EqualsInput(byte input) + => Assert.Equal(input, new Gray8(input).PackedValue); + + [Fact] + public void Gray8_FromScaledVector4() + { + // Arrange + Gray8 gray = default; + const byte expected = 128; + Vector4 scaled = new Gray8(expected).ToScaledVector4(); + + // Act + gray.FromScaledVector4(scaled); + byte actual = gray.PackedValue; + + // Assert + Assert.Equal(expected, actual); + } + + [Theory] + [MemberData(nameof(LuminanceData))] + public void Gray8_ToScaledVector4(byte input) + { + // Arrange + var gray = new Gray8(input); + + // Act + Vector4 actual = gray.ToScaledVector4(); + + // Assert + float scaledInput = input / 255F; + Assert.Equal(scaledInput, actual.X); + Assert.Equal(scaledInput, actual.Y); + Assert.Equal(scaledInput, actual.Z); + Assert.Equal(1, actual.W); + } + + [Theory] + [MemberData(nameof(LuminanceData))] + public void Gray8_FromVector4(byte luminance) + { + // Arrange + Gray8 gray = default; + var vector = new Gray8(luminance).ToVector4(); + + // Act + gray.FromVector4(vector); + byte actual = gray.PackedValue; + + // Assert + Assert.Equal(luminance, actual); + } + + [Theory] + [MemberData(nameof(LuminanceData))] + public void Gray8_ToVector4(byte input) + { + // Arrange + var gray = new Gray8(input); + + // Act + var actual = gray.ToVector4(); + + // Assert + float scaledInput = input / 255F; + Assert.Equal(scaledInput, actual.X); + Assert.Equal(scaledInput, actual.Y); + Assert.Equal(scaledInput, actual.Z); + Assert.Equal(1, actual.W); + } + + [Theory] + [MemberData(nameof(LuminanceData))] + public void Gray8_FromRgba32(byte rgb) + { + // Arrange + Gray8 gray = default; + byte expected = ImageMaths.Get8BitBT709Luminance(rgb, rgb, rgb); + + // Act + gray.FromRgba32(new Rgba32(rgb, rgb, rgb)); + byte actual = gray.PackedValue; + + // Assert + Assert.Equal(expected, actual); + } + + + [Theory] + [MemberData(nameof(LuminanceData))] + public void Gray8_ToRgba32(byte luminance) + { + // Arrange + var gray = new Gray8(luminance); + + // Act + Rgba32 actual = default; + gray.ToRgba32(ref actual); + + // Assert + Assert.Equal(luminance, actual.R); + Assert.Equal(luminance, actual.G); + Assert.Equal(luminance, actual.B); + Assert.Equal(byte.MaxValue, actual.A); + } + + public class Rgba32Compatibility + { + // ReSharper disable once MemberHidesStaticFromOuterClass + public static readonly TheoryData LuminanceData = Gray8Tests.LuminanceData; + + [Theory] + [MemberData(nameof(LuminanceData))] + public void Gray8_FromRgba32_IsInverseOf_ToRgba32(byte luminance) + { + var original = new Gray8(luminance); + + Rgba32 rgba = default; + original.ToRgba32(ref rgba); + + Gray8 mirror = default; + mirror.FromRgba32(rgba); + + Assert.Equal(original, mirror); + } + + + [Theory] + [MemberData(nameof(LuminanceData))] + public void Rgba32_ToGray8_IsInverseOf_Gray8_ToRgba32(byte luminance) + { + var original = new Gray8(luminance); + + Rgba32 rgba = default; + original.ToRgba32(ref rgba); + + Gray8 mirror = default; + mirror.FromRgba32(rgba); + + Assert.Equal(original, mirror); + } + + [Theory] + [MemberData(nameof(LuminanceData))] + public void ToVector4_IsRgba32Compatible(byte luminance) + { + var original = new Gray8(luminance); + + Rgba32 rgba = default; + original.ToRgba32(ref rgba); + + var gray8Vector = original.ToVector4(); + var rgbaVector = original.ToVector4(); + + Assert.Equal(gray8Vector, rgbaVector, new ApproximateFloatComparer(1e-5f)); + } + + [Theory] + [MemberData(nameof(LuminanceData))] + public void FromVector4_IsRgba32Compatible(byte luminance) + { + var original = new Gray8(luminance); + + Rgba32 rgba = default; + original.ToRgba32(ref rgba); + + Vector4 rgbaVector = original.ToVector4(); + + Gray8 mirror = default; + mirror.FromVector4(rgbaVector); + + Assert.Equal(original, mirror); + } + + [Theory] + [MemberData(nameof(LuminanceData))] + public void ToScaledVector4_IsRgba32Compatible(byte luminance) + { + var original = new Gray8(luminance); + + Rgba32 rgba = default; + original.ToRgba32(ref rgba); + + Vector4 gray8Vector = original.ToScaledVector4(); + Vector4 rgbaVector = original.ToScaledVector4(); + + Assert.Equal(gray8Vector, rgbaVector, new ApproximateFloatComparer(1e-5f)); + } + + [Theory] + [MemberData(nameof(LuminanceData))] + public void FromScaledVector4_IsRgba32Compatible(byte luminance) + { + var original = new Gray8(luminance); + + Rgba32 rgba = default; + original.ToRgba32(ref rgba); + + Vector4 rgbaVector = original.ToScaledVector4(); + + Gray8 mirror = default; + mirror.FromScaledVector4(rgbaVector); + + Assert.Equal(original, mirror); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs index 3376645f34..85a3b8b320 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfSingleTests.cs @@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void HalfSingle_PackFromScaledVector4() + public void HalfSingle_FromScaledVector4() { // arrange Vector4 scaled = new HalfSingle(-1F).ToScaledVector4(); @@ -60,118 +60,11 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var halfSingle = default(HalfSingle); // act - halfSingle.PackFromScaledVector4(scaled); + halfSingle.FromScaledVector4(scaled); ushort actual = halfSingle.PackedValue; // assert Assert.Equal(expected, actual); } - - [Fact] - public void HalfSingle_ToRgb24() - { - // arrange - var halfVector = new HalfSingle(.5F); - var actual = default(Rgb24); - var expected = new Rgb24(128, 0, 0); - - // act - halfVector.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfSingle_Rgba32() - { - // arrange - var halfVector = new HalfSingle(.5F); - var actual = default(Rgba32); - var expected = new Rgba32(128, 0, 0, 255); - - // act - halfVector.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfSingle_ToBgr24() - { - // arrange - var halfVector = new HalfSingle(.5F); - var actual = default(Bgr24); - var expected = new Bgr24(128, 0, 0); - - // act - halfVector.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfSingle_Bgra32() - { - // arrange - var halfVector = new HalfSingle(.5F); - var actual = default(Bgra32); - var expected = new Bgra32(128, 0, 0, 255); - - // act - halfVector.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfSingle_Argb32() - { - // arrange - var halfVector = new HalfSingle(.5F); - var actual = default(Argb32); - var expected = new Argb32(128, 0, 0, 255); - - // act - halfVector.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfSingle_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(HalfSingle); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 0); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfSingle_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(HalfSingle); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 0, 65535); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs index c2a524f0dc..ccdd23e8fb 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfVector2Tests.cs @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void HalfVector2_PackFromScaledVector4() + public void HalfVector2_FromScaledVector4() { // arrange Vector4 scaled = new HalfVector2(Vector2.One).ToScaledVector4(); @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var halfVector = default(HalfVector2); // act - halfVector.PackFromScaledVector4(scaled); + halfVector.FromScaledVector4(scaled); uint actual = halfVector.PackedValue; // assert @@ -71,112 +71,5 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, actual); } - - [Fact] - public void HalfVector2_ToRgb24() - { - // arrange - var halfVector = new HalfVector2(.5F, .25F); - var actual = default(Rgb24); - var expected = new Rgb24(128, 64, 0); - - // act - halfVector.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfVector2_Rgba32() - { - // arrange - var halfVector = new HalfVector2(.5F, .25F); - var actual = default(Rgba32); - var expected = new Rgba32(128, 64, 0, 255); - - // act - halfVector.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfVector2_ToBgr24() - { - // arrange - var halfVector = new HalfVector2(.5F, .25F); - var actual = default(Bgr24); - var expected = new Bgr24(128, 64, 0); - - // act - halfVector.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfVector2_Bgra32() - { - // arrange - var halfVector = new HalfVector2(.5F, .25F); - var actual = default(Bgra32); - var expected = new Bgra32(128, 64, 0, 255); - - // act - halfVector.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfVector2_Argb32() - { - // arrange - var halfVector = new HalfVector2(.5F, .25F); - var actual = default(Argb32); - var expected = new Argb32(128, 64, 0, 255); - - // act - halfVector.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfVector2_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(HalfVector2); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 65535, 0); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfVector2_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(HalfVector2); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 65535, 0, 65535); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs index 8b28dd8277..c61dd97d2a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/HalfVector4Tests.cs @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void HalfVector4_PackFromScaledVector4() + public void HalfVector4_FromScaledVector4() { // arrange var halfVector4 = default(HalfVector4); @@ -59,166 +59,11 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats ulong expected = 13547034390470638592uL; // act - halfVector4.PackFromScaledVector4(scaled); + halfVector4.FromScaledVector4(scaled); ulong actual = halfVector4.PackedValue; // assert Assert.Equal(expected, actual); } - - [Fact] - public void HalfVector4_ToRgb24() - { - // arrange - var halfVector = new HalfVector4(.25F, .5F, .75F, 1F); - var actual = default(Rgb24); - var expected = new Rgb24(64, 128, 191); - - // act - halfVector.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfVector4_Rgba32() - { - // arrange - var halfVector = new HalfVector4(.25F, .5F, .75F, 1F); - var actual = default(Rgba32); - var expected = new Rgba32(64, 128, 191, 255); - - // act - halfVector.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfVector4_ToBgr24() - { - // arrange - var halfVector = new HalfVector4(.25F, .5F, .75F, 1F); - var actual = default(Bgr24); - var expected = new Bgr24(64, 128, 191); - - // act - halfVector.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfVector4_Bgra32() - { - // arrange - var halfVector = new HalfVector4(.25F, .5F, .75F, 1F); - var actual = default(Bgra32); - var expected = new Bgra32(64, 128, 191, 255); - - // act - halfVector.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfVector4_Argb32() - { - // arrange - var halfVector = new HalfVector4(.25F, .5F, .75F, 1F); - var actual = default(Argb32); - var expected = new Argb32(64, 128, 191, 255); - - // act - halfVector.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfVector4_PackFromRgba32_ToRgba32() - { - // arrange - var halVector = default(HalfVector4); - var actual = default(Rgba32); - var expected = new Rgba32(64, 128, 191, 255); - - // act - halVector.PackFromRgba32(expected); - halVector.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfVector4_PackFromBgra32_ToBgra32() - { - // arrange - var halVector = default(HalfVector4); - var actual = default(Bgra32); - var expected = new Bgra32(64, 128, 191, 255); - - // act - halVector.PackFromBgra32(expected); - halVector.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfVector4_PackFromArgb32_ToArgb32() - { - // arrange - var halVector = default(HalfVector4); - var actual = default(Argb32); - var expected = new Argb32(64, 128, 191, 255); - - // act - halVector.PackFromArgb32(expected); - halVector.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfVector4_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(HalfVector4); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void HalfVector4_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(HalfVector4); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 0); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs index 83f22e2aa0..506ebe0fe0 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte2Tests.cs @@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void NormalizedByte2_PackFromScaledVector4() + public void NormalizedByte2_FromScaledVector4() { // arrange Vector4 scaled = new NormalizedByte2(-Vector2.One).ToScaledVector4(); @@ -60,134 +60,11 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats uint expected = 0x8181; // act - byte2.PackFromScaledVector4(scaled); + byte2.FromScaledVector4(scaled); uint actual = byte2.PackedValue; // assert Assert.Equal(expected, actual); } - - [Fact] - public void NormalizedByte2_PackFromRgba32() - { - // arrange - var byte2 = new NormalizedByte2(); - var rgba = new Rgba32(141, 90, 0, 0); - int expected = 0xda0d; - - // act - byte2.PackFromRgba32(rgba); - ushort actual = byte2.PackedValue; - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedByte2_ToRgb24() - { - // arrange - var short4 = new NormalizedByte2(0.1f, -0.3f); - var actual = default(Rgb24); - var expected = new Rgb24(141, 90, 0); - - // act - short4.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedByte2_ToRgba32() - { - // arrange - var short4 = new NormalizedByte2(0.1f, -0.3f); - var actual = default(Rgba32); - var expected = new Rgba32(141, 90, 0, 255); - - // act - short4.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedByte2_ToBgr24() - { - // arrange - var short4 = new NormalizedByte2(0.1f, -0.3f); - var actual = default(Bgr24); - var expected = new Bgr24(141, 90, 0); - - // act - short4.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedByte2_ToBgra32() - { - // arrange - var short4 = new NormalizedByte2(0.1f, -0.3f); - var actual = default(Bgra32); - var expected = new Bgra32(141, 90, 0, 255); - - // act - short4.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedByte2_ToArgb32() - { - // arrange - var short4 = new NormalizedByte2(0.1f, -0.3f); - var actual = default(Argb32); - var expected = new Argb32(141, 90, 0, 255); - - // act - short4.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedByte2_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(NormalizedByte2); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 65535, 0); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedByte2_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(NormalizedByte2); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 65535, 0, 65535); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs index 16516496f8..19a49e5d8a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedByte4Tests.cs @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void NormalizedByte4_PackFromScaledVector4() + public void NormalizedByte4_FromScaledVector4() { // arrange var pixel = default(NormalizedByte4); @@ -54,151 +54,11 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats uint expected = 0x81818181; // act - pixel.PackFromScaledVector4(scaled); + pixel.FromScaledVector4(scaled); uint actual = pixel.PackedValue; // assert Assert.Equal(expected, actual); } - - [Fact] - public void NormalizedByte4_ToRgb24() - { - // arrange - var short4 = new NormalizedByte4(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Rgb24); - var expected = new Rgb24(141, 90, 192); - - // act - short4.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedByte4_ToRgba32() - { - // arrange - var short4 = new NormalizedByte4(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Rgba32); - var expected = new Rgba32(141, 90, 192, 39); - - // act - short4.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedByte4_ToBgr24() - { - // arrange - var short4 = new NormalizedByte4(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Bgr24); - var expected = new Bgr24(141, 90, 192); - - // act - short4.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedByte4_ToArgb32() - { - // arrange - var short4 = new NormalizedByte4(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Argb32); - var expected = new Argb32(141, 90, 192, 39); - - // act - short4.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedByte4_PackFromRgba32_ToRgba32() - { - // arrange - var short4 = default(NormalizedByte4); - var actual = default(Rgba32); - var expected = new Rgba32(9, 115, 202, 127); - - // act - short4.PackFromRgba32(new Rgba32(9, 115, 202, 127)); - short4.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedByte4_PackFromBgra32_ToRgba32() - { - // arrange - var actual = default(Bgra32); - var short4 = default(NormalizedByte4); - var expected = new Bgra32(9, 115, 202, 127); - - // act - short4.PackFromBgra32(expected); - short4.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedByte4_PackFromArgb32_ToRgba32() - { - // arrange - var short4 = default(NormalizedByte4); - var actual = default(Argb32); - var expected = new Argb32(9, 115, 202, 127); - - // act - short4.PackFromArgb32(expected); - short4.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedByte4_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(NormalizedByte4); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 65535, 0); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedByte4_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(NormalizedByte4); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 65535, 0, 65535); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs index 2fb7f05aca..216ed4ad75 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort2Tests.cs @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void NormalizedShort2_PackFromScaledVector4() + public void NormalizedShort2_FromScaledVector4() { // arrange Vector4 scaled = new NormalizedShort2(-Vector2.One).ToScaledVector4(); @@ -63,135 +63,11 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats uint expected = 0x80018001; // act - short2.PackFromScaledVector4(scaled); + short2.FromScaledVector4(scaled); uint actual = short2.PackedValue; // assert Assert.Equal(expected, actual); } - - [Fact] - public void NormalizedShort2_PackFromRgba32_ToRgb24() - { - // arrange - var actual = default(Rgb24); - var short2 = new NormalizedShort2(); - var rgba = new Rgba32(141, 90, 0, 0); - var expected = new Rgb24(141, 90, 0); - - // act - short2.PackFromRgba32(rgba); - short2.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedShort2_ToRgb24() - { - // arrange - var short2 = new NormalizedShort2(0.1f, -0.3f); - var actual = default(Rgb24); - var expected = new Rgb24(141, 90, 0); - - // act - short2.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedShort2_ToRgba32() - { - // arrange - var short2 = new NormalizedShort2(0.1f, -0.3f); - var actual = default(Rgba32); - var expected = new Rgba32(141, 90, 0, 255); - - // act - short2.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedShort2_ToBgr24() - { - // arrange - var short2 = new NormalizedShort2(0.1f, -0.3f); - var actual = default(Bgr24); - var expected = new Bgr24(141, 90, 0); - - // act - short2.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedShort2_ToBgra32() - { - // arrange - var short2 = new NormalizedShort2(0.1f, -0.3f); - var actual = default(Bgra32); - var expected = new Bgra32(141, 90, 0, 255); - - // act - short2.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedShort2_ToArgb32() - { - // arrange - var short2 = new NormalizedShort2(0.1f, -0.3f); - var actual = default(Argb32); - var expected = new Argb32(141, 90, 0, 255); - - // act - short2.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedShort2_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(NormalizedShort2); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 65535, 0); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedShort2_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(NormalizedShort2); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 65535, 0, 65535); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs index 7dcdd9c88b..d06d46d06f 100644 --- a/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/NormalizedShort4Tests.cs @@ -47,159 +47,19 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void NormalizedShort4_PackFromScaledVector4() + public void NormalizedShort4_FromScaledVector4() { // arrange var pixel = default(NormalizedShort4); Vector4 scaled = new NormalizedShort4(Vector4.One).ToScaledVector4(); - ulong expected = (ulong)0x7FFF7FFF7FFF7FFF; + ulong expected = 0x7FFF7FFF7FFF7FFF; // act - pixel.PackFromScaledVector4(scaled); + pixel.FromScaledVector4(scaled); ulong actual = pixel.PackedValue; // assert Assert.Equal(expected, actual); } - - [Fact] - public void NormalizedShort4_ToRgb24() - { - // arrange - var short4 = new NormalizedShort4(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Rgb24); - var expected = new Rgb24(141, 90, 192); - - // act - short4.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedShort4_ToRgba32() - { - // arrange - var short4 = new NormalizedShort4(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Rgba32); - var expected = new Rgba32(141, 90, 192, 39); - - // act - short4.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedShort4_ToBgr24() - { - // arrange - var short4 = new NormalizedShort4(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Bgr24); - var expected = new Bgr24(141, 90, 192); - - // act - short4.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedShort4_ToArgb32() - { - // arrange - var short4 = new NormalizedShort4(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Argb32); - var expected = new Argb32(141, 90, 192, 39); - - // act - short4.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedShort4_PackFromRgba32_ToRgba32() - { - // arrange - var short4 = default(NormalizedShort4); - var expected = new Rgba32(9, 115, 202, 127); - var actual = default(Rgba32); - - // act - short4.PackFromRgba32(expected); - short4.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedShort4_PackFromBgra32_ToRgba32() - { - // arrange - var short4 = default(NormalizedShort4); - var actual = default(Bgra32); - var expected = new Bgra32(9, 115, 202, 127); - - // act - short4.PackFromBgra32(expected); - short4.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedShort4_PackFromArgb32_ToRgba32() - { - // arrange - var short4 = default(NormalizedShort4); - var actual = default(Argb32); - var expected = new Argb32(9, 115, 202, 127); - - // act - short4.PackFromArgb32(expected); - short4.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedShort4_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(NormalizedShort4); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 65535, 0); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void NormalizedShort4_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(NormalizedShort4); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 65535, 0, 65535); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs index 3ea9bcad40..7de1cbb190 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffFunctionsTests_TPixel.cs @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders { new TestPixel(1,1,1,1), new TestPixel(0,0,0,.8f), .5f, new TestPixel(0.6f, 0.6f, 0.6f, 1) }, }; - private MemoryAllocator MemoryAllocator { get; } = Configuration.Default.MemoryAllocator; + private Configuration Configuration => Configuration.Default; [Theory] [MemberData(nameof(NormalBlendFunctionData))] @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.NormalSrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.NormalSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.MultiplySrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.MultiplySrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -128,7 +128,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.AddSrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.AddSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -167,7 +167,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.SubtractSrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.SubtractSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -206,7 +206,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.ScreenSrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.ScreenSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -245,7 +245,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.DarkenSrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.DarkenSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -284,7 +284,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.LightenSrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.LightenSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -323,7 +323,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.OverlaySrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.OverlaySrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } @@ -362,7 +362,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders where TPixel : struct, IPixel { var dest = new Span(new TPixel[1]); - new DefaultPixelBlenders.HardLightSrcOver().Blend(this.MemoryAllocator, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); + new DefaultPixelBlenders.HardLightSrcOver().Blend(this.Configuration, dest, back.AsSpan(), source.AsSpan(), AsSpan(amount)); VectorAssert.Equal(expected, dest[0], 2); } } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs new file mode 100644 index 0000000000..c539e9dcf0 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelConverterTests.cs @@ -0,0 +1,160 @@ +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.PixelFormats.Utils; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.PixelFormats +{ + public abstract class PixelConverterTests + { + public static readonly TheoryData RgbaData = + new TheoryData + { + { 0, 0, 0, 0 }, + { 0, 0, 0, 255 }, + { 0, 0, 255, 0 }, + { 0, 255, 0, 0 }, + { 255, 0, 0, 0 }, + { 255, 255, 255, 255 }, + { 0, 0, 0, 1 }, + { 0, 0, 1, 0 }, + { 0, 1, 0, 0 }, + { 1, 0, 0, 0 }, + { 3, 5, 7, 11 }, + { 67, 71, 101, 109 } + }; + + public class FromRgba32 : PixelConverterTests + { + [Theory] + [MemberData(nameof(RgbaData))] + public void ToArgb32(byte r, byte g, byte b, byte a) + { + Rgba32 s = ReferenceImplementations.MakeRgba32(r, g, b, a); + + // Act: + uint actualPacked = PixelConverter.FromRgba32.ToArgb32(s.PackedValue); + + // Assert: + uint expectedPacked = ReferenceImplementations.MakeArgb32(r, g, b, a).PackedValue; + + Assert.Equal(expectedPacked, actualPacked); + } + + [Theory] + [MemberData(nameof(RgbaData))] + public void ToBgra32(byte r, byte g, byte b, byte a) + { + Rgba32 s = ReferenceImplementations.MakeRgba32(r, g, b, a); + + // Act: + uint actualPacked = PixelConverter.FromRgba32.ToBgra32(s.PackedValue); + + // Assert: + uint expectedPacked = ReferenceImplementations.MakeBgra32(r, g, b, a).PackedValue; + + Assert.Equal(expectedPacked, actualPacked); + } + } + + public class FromArgb32 : PixelConverterTests + { + [Theory] + [MemberData(nameof(RgbaData))] + public void ToRgba32(byte r, byte g, byte b, byte a) + { + Argb32 s = ReferenceImplementations.MakeArgb32(r, g, b, a); + + // Act: + uint actualPacked = PixelConverter.FromArgb32.ToRgba32(s.PackedValue); + + // Assert: + uint expectedPacked = ReferenceImplementations.MakeRgba32(r, g, b, a).PackedValue; + + Assert.Equal(expectedPacked, actualPacked); + } + + [Theory] + [MemberData(nameof(RgbaData))] + public void ToBgra32(byte r, byte g, byte b, byte a) + { + Argb32 s = ReferenceImplementations.MakeArgb32(r, g, b, a); + + // Act: + uint actualPacked = PixelConverter.FromArgb32.ToBgra32(s.PackedValue); + + // Assert: + uint expectedPacked = ReferenceImplementations.MakeBgra32(r, g, b, a).PackedValue; + + Assert.Equal(expectedPacked, actualPacked); + } + } + + public class FromBgra32 : PixelConverterTests + { + [Theory] + [MemberData(nameof(RgbaData))] + public void ToArgb32(byte r, byte g, byte b, byte a) + { + Bgra32 s = ReferenceImplementations.MakeBgra32(r, g, b, a); + + // Act: + uint actualPacked = PixelConverter.FromBgra32.ToArgb32(s.PackedValue); + + // Assert: + uint expectedPacked = ReferenceImplementations.MakeArgb32(r, g, b, a).PackedValue; + + Assert.Equal(expectedPacked, actualPacked); + } + + [Theory] + [MemberData(nameof(RgbaData))] + public void ToRgba32(byte r, byte g, byte b, byte a) + { + Bgra32 s = ReferenceImplementations.MakeBgra32(r, g, b, a); + + // Act: + uint actualPacked = PixelConverter.FromBgra32.ToRgba32(s.PackedValue); + + // Assert: + uint expectedPacked = ReferenceImplementations.MakeRgba32(r, g, b, a).PackedValue; + + Assert.Equal(expectedPacked, actualPacked); + } + } + + + private static class ReferenceImplementations + { + public static Rgba32 MakeRgba32(byte r, byte g, byte b, byte a) + { + Rgba32 d = default; + d.R = r; + d.G = g; + d.B = b; + d.A = a; + return d; + } + + public static Argb32 MakeArgb32(byte r, byte g, byte b, byte a) + { + Argb32 d = default; + d.R = r; + d.G = g; + d.B = b; + d.A = a; + return d; + } + + public static Bgra32 MakeBgra32(byte r, byte g, byte b, byte a) + { + Bgra32 d = default; + d.R = r; + d.G = g; + d.B = b; + d.A = a; + return d; + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Argb32OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Argb32OperationsTests.cs new file mode 100644 index 0000000000..c881ae96ba --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Argb32OperationsTests.cs @@ -0,0 +1,25 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations +{ + public partial class PixelOperationsTests + { + public class Argb32OperationsTests : PixelOperationsTests + { + + public Argb32OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Bgr24OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Bgr24OperationsTests.cs new file mode 100644 index 0000000000..323d3914cf --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Bgr24OperationsTests.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations +{ + public partial class PixelOperationsTests + { + public class Bgr24OperationsTests : PixelOperationsTests + { + public Bgr24OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Bgra32OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Bgra32OperationsTests.cs new file mode 100644 index 0000000000..1c966951fc --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Bgra32OperationsTests.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations +{ + public partial class PixelOperationsTests + { + public class Bgra32OperationsTests : PixelOperationsTests + { + public Bgra32OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray16OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray16OperationsTests.cs new file mode 100644 index 0000000000..c3de335470 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray16OperationsTests.cs @@ -0,0 +1,114 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations +{ + public partial class PixelOperationsTests + { + public class Gray16OperationsTests : PixelOperationsTests + { + public Gray16OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void FromGray8Bytes(int count) + { + byte[] source = CreateByteTestData(count); + var expected = new Gray16[count]; + + for (int i = 0; i < count; i++) + { + expected[i].FromGray8(new Gray8(source[i])); + } + + TestOperation( + source, + expected, + (s, d) => Operations.FromGray8Bytes(this.Configuration, s, d.GetSpan(), count) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void ToGray8Bytes(int count) + { + Gray16[] source = CreatePixelTestData(count); + byte[] expected = new byte[count]; + var gray = default(Gray8); + + for (int i = 0; i < count; i++) + { + gray.FromScaledVector4(source[i].ToScaledVector4()); + expected[i] = gray.PackedValue; + } + + TestOperation( + source, + expected, + (s, d) => Operations.ToGray8Bytes(this.Configuration, s, d.GetSpan(), count) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void FromGray16Bytes(int count) + { + byte[] source = CreateByteTestData(count * 2); + Span sourceSpan = source.AsSpan(); + var expected = new Gray16[count]; + + for (int i = 0; i < count; i++) + { + int i2 = i * 2; + expected[i].FromGray16(MemoryMarshal.Cast(sourceSpan.Slice(i2, 2))[0]); + } + + TestOperation( + source, + expected, + (s, d) => Operations.FromGray16Bytes(this.Configuration, s, d.GetSpan(), count) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void ToGray16Bytes(int count) + { + Gray16[] source = CreatePixelTestData(count); + byte[] expected = new byte[count * 2]; + Gray16 gray = default; + + for (int i = 0; i < count; i++) + { + int i2 = i * 2; + gray.FromScaledVector4(source[i].ToScaledVector4()); + OctetBytes bytes = Unsafe.As(ref gray); + expected[i2] = bytes[0]; + expected[i2 + 1] = bytes[1]; + } + + TestOperation( + source, + expected, + (s, d) => Operations.ToGray16Bytes(this.Configuration, s, d.GetSpan(), count) + ); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray8OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray8OperationsTests.cs new file mode 100644 index 0000000000..acd6ef23ac --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Gray8OperationsTests.cs @@ -0,0 +1,114 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations +{ + public partial class PixelOperationsTests + { + public class Gray8OperationsTests : PixelOperationsTests + { + public Gray8OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void FromGray8Bytes(int count) + { + byte[] source = CreateByteTestData(count); + var expected = new Gray8[count]; + + for (int i = 0; i < count; i++) + { + expected[i].FromGray8(new Gray8(source[i])); + } + + TestOperation( + source, + expected, + (s, d) => Operations.FromGray8Bytes(this.Configuration, s, d.GetSpan(), count) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void ToGray8Bytes(int count) + { + Gray8[] source = CreatePixelTestData(count); + byte[] expected = new byte[count]; + var gray = default(Gray8); + + for (int i = 0; i < count; i++) + { + gray.FromScaledVector4(source[i].ToScaledVector4()); + expected[i] = gray.PackedValue; + } + + TestOperation( + source, + expected, + (s, d) => Operations.ToGray8Bytes(this.Configuration, s, d.GetSpan(), count) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void FromGray16Bytes(int count) + { + byte[] source = CreateByteTestData(count * 2); + Span sourceSpan = source.AsSpan(); + var expected = new Gray8[count]; + + for (int i = 0; i < count; i++) + { + int i2 = i * 2; + expected[i].FromGray16(MemoryMarshal.Cast(sourceSpan.Slice(i2, 2))[0]); + } + + TestOperation( + source, + expected, + (s, d) => Operations.FromGray16Bytes(this.Configuration, s, d.GetSpan(), count) + ); + } + + [Theory] + [MemberData(nameof(ArraySizesData))] + public void ToGray16Bytes(int count) + { + Gray8[] source = CreatePixelTestData(count); + byte[] expected = new byte[count * 2]; + Gray16 gray = default; + + for (int i = 0; i < count; i++) + { + int i2 = i * 2; + gray.FromScaledVector4(source[i].ToScaledVector4()); + OctetBytes bytes = Unsafe.As(ref gray); + expected[i2] = bytes[0]; + expected[i2 + 1] = bytes[1]; + } + + TestOperation( + source, + expected, + (s, d) => Operations.ToGray16Bytes(this.Configuration, s, d.GetSpan(), count) + ); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb48OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb48OperationsTests.cs new file mode 100644 index 0000000000..0a28db6b0a --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgb48OperationsTests.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations +{ + public partial class PixelOperationsTests + { + public class Rgb48OperationsTests : PixelOperationsTests + { + public Rgb48OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgba32OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgba32OperationsTests.cs new file mode 100644 index 0000000000..1ecbaf3615 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgba32OperationsTests.cs @@ -0,0 +1,46 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Buffers; +using System.Numerics; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations +{ + public partial class PixelOperationsTests + { + public class Rgba32OperationsTests : PixelOperationsTests + { + public Rgba32OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + + [Fact(Skip = SkipProfilingBenchmarks)] + public void Benchmark_ToVector4() + { + const int times = 200000; + const int count = 1024; + + using (IMemoryOwner source = Configuration.Default.MemoryAllocator.Allocate(count)) + using (IMemoryOwner dest = Configuration.Default.MemoryAllocator.Allocate(count)) + { + this.Measure( + times, + () => PixelOperations.Instance.ToVector4( + this.Configuration, + source.GetSpan(), + dest.GetSpan())); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgba64OperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgba64OperationsTests.cs new file mode 100644 index 0000000000..6787602bb2 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.Rgba64OperationsTests.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations +{ + public partial class PixelOperationsTests + { + public class Rgba64OperationsTests : PixelOperationsTests + { + public Rgba64OperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.RgbaVectorOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.RgbaVectorOperationsTests.cs new file mode 100644 index 0000000000..f9cc042a77 --- /dev/null +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.RgbaVectorOperationsTests.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations +{ + public partial class PixelOperationsTests + { + public class RgbaVectorOperationsTests : PixelOperationsTests + { + public RgbaVectorOperationsTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void IsSpecialImplementation() => Assert.IsType(PixelOperations.Instance); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs similarity index 65% rename from tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs rename to tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index 9e41fd94f3..d9845e4741 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -6,113 +6,69 @@ using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; + using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; + using Xunit; using Xunit.Abstractions; -namespace SixLabors.ImageSharp.Tests.PixelFormats +namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelOperations { public partial class PixelOperationsTests { - public class Rgba32 : PixelOperationsTests - { - public Rgba32(ITestOutputHelper output) - : base(output) - { - } - - // For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class: - public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; - - [Fact] - public void IsSpecialImplementation() - { - Assert.IsType(PixelOperations.Instance); - } - - [Fact] - public void ToVector4SimdAligned() - { - if (!Vector.IsHardwareAccelerated) - { - return; - } - - ImageSharp.PixelFormats.Rgba32[] source = CreatePixelTestData(64); - Vector4[] expected = CreateExpectedVector4Data(source); - - TestOperation( - source, - expected, - (s, d) => ImageSharp.PixelFormats.Rgba32.PixelOperations.ToVector4SimdAligned(s, d.GetSpan(), 64) - ); - } - - - // [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; - - using (IMemoryOwner source = Configuration.Default.MemoryAllocator.Allocate(count)) - using (IMemoryOwner dest = Configuration.Default.MemoryAllocator.Allocate(count)) - { - this.Measure( - times, - () => - { - PixelOperations.Instance.ToVector4(source.GetSpan(), dest.GetSpan(), count); - }); - } - } - } - - public class Argb32 : PixelOperationsTests - { - // For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class: - public Argb32(ITestOutputHelper output) - : base(output) - { - } - - public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; - } - [Theory] [WithBlankImages(1, 1, PixelTypes.All)] - public void GetGlobalInstance(TestImageProvider dummy) - where TPixel : struct, IPixel - { - Assert.NotNull(PixelOperations.Instance); - } - - [Fact] - public void IsOpaqueColor() - { - Assert.True(new GraphicsOptions(true).IsOpaqueColorWithoutBlending(ImageSharp.PixelFormats.Rgba32.Red)); - - Assert.False(new GraphicsOptions(true, 0.5f).IsOpaqueColorWithoutBlending(ImageSharp.PixelFormats.Rgba32.Red)); - Assert.False(new GraphicsOptions(true).IsOpaqueColorWithoutBlending(ImageSharp.PixelFormats.Rgba32.Transparent)); - Assert.False(new GraphicsOptions(true, PixelColorBlendingMode.Lighten, 1).IsOpaqueColorWithoutBlending(ImageSharp.PixelFormats.Rgba32.Red)); - Assert.False(new GraphicsOptions(true, PixelColorBlendingMode.Normal,PixelAlphaCompositionMode.DestOver, 1).IsOpaqueColorWithoutBlending(ImageSharp.PixelFormats.Rgba32.Red)); - } + public void GetGlobalInstance(TestImageProvider _) + where T : struct, IPixel => Assert.NotNull(PixelOperations.Instance); } public abstract class PixelOperationsTests : MeasureFixture where TPixel : struct, IPixel { + public const string SkipProfilingBenchmarks = +#if true + "Profiling benchmark - enable manually!"; +#else + null; +#endif + protected PixelOperationsTests(ITestOutputHelper output) : base(output) { } - public static TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; - - private static PixelOperations Operations => PixelOperations.Instance; + public static TheoryData ArraySizesData => + new TheoryData + { + 0, + 1, + 2, + 7, + 16, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520, + 521, + 522, + 523, + 524, + 525, + 526, + 527, + 528, + 1111 + }; + + protected Configuration Configuration => Configuration.Default; + + internal static PixelOperations Operations => PixelOperations.Instance; internal static TPixel[] CreateExpectedPixelData(Vector4[] source) { @@ -120,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats for (int i = 0; i < expected.Length; i++) { - expected[i].PackFromVector4(source[i]); + expected[i].FromVector4(source[i]); } return expected; } @@ -131,14 +87,14 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats for (int i = 0; i < expected.Length; i++) { - expected[i].PackFromScaledVector4(source[i]); + expected[i].FromScaledVector4(source[i]); } return expected; } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackFromVector4(int count) + public void FromVector4(int count) { Vector4[] source = CreateVector4TestData(count); TPixel[] expected = CreateExpectedPixelData(source); @@ -146,13 +102,13 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats TestOperation( source, expected, - (s, d) => Operations.PackFromVector4(s, d.GetSpan(), count) + (s, d) => Operations.FromVector4(this.Configuration, s, d.GetSpan()) ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackFromScaledVector4(int count) + public void FromScaledVector4(int count) { Vector4[] source = CreateVector4TestData(count); TPixel[] expected = CreateScaledExpectedPixelData(source); @@ -160,32 +116,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats TestOperation( source, expected, - (s, d) => Operations.PackFromScaledVector4(s, d.GetSpan(), count) + (s, d) => Operations.FromScaledVector4(this.Configuration, s, d.GetSpan()) ); } - internal static Vector4[] CreateExpectedVector4Data(TPixel[] source) - { - var expected = new Vector4[source.Length]; - - for (int i = 0; i < expected.Length; i++) - { - expected[i] = source[i].ToVector4(); - } - return expected; - } - - internal static Vector4[] CreateExpectedScaledVector4Data(TPixel[] source) - { - var expected = new Vector4[source.Length]; - - for (int i = 0; i < expected.Length; i++) - { - expected[i] = source[i].ToScaledVector4(); - } - return expected; - } - [Theory] [MemberData(nameof(ArraySizesData))] public void ToVector4(int count) @@ -196,7 +130,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats TestOperation( source, expected, - (s, d) => Operations.ToVector4(s, d.GetSpan(), count) + (s, d) => Operations.ToVector4(this.Configuration, s, d.GetSpan()) ); } @@ -210,389 +144,359 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats TestOperation( source, expected, - (s, d) => Operations.ToScaledVector4(s, d.GetSpan(), count) + (s, d) => Operations.ToScaledVector4(this.Configuration, s, d.GetSpan()) ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackFromRgb24Bytes(int count) + public void FromArgb32Bytes(int count) { - byte[] source = CreateByteTestData(count * 3); + byte[] source = CreateByteTestData(count * 4); var expected = new TPixel[count]; for (int i = 0; i < count; i++) { - int i3 = i * 3; + int i4 = i * 4; - expected[i].PackFromRgba32(new Rgba32(source[i3 + 0], source[i3 + 1], source[i3 + 2], 255)); + expected[i].FromArgb32(new Argb32(source[i4 + 1], source[i4 + 2], source[i4 + 3], source[i4 + 0])); } TestOperation( source, expected, - (s, d) => Operations.PackFromRgb24Bytes(s, d.GetSpan(), count) + (s, d) => Operations.FromArgb32Bytes(this.Configuration, s, d.GetSpan(), count) ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void ToRgb24Bytes(int count) + public void ToArgb32Bytes(int count) { TPixel[] source = CreatePixelTestData(count); - byte[] expected = new byte[count * 3]; - var rgb = default(Rgb24); + byte[] expected = new byte[count * 4]; + var argb = default(Argb32); for (int i = 0; i < count; i++) { - int i3 = i * 3; - source[i].ToRgb24(ref rgb); - expected[i3] = rgb.R; - expected[i3 + 1] = rgb.G; - expected[i3 + 2] = rgb.B; + int i4 = i * 4; + argb.FromScaledVector4(source[i].ToScaledVector4()); + + expected[i4] = argb.A; + expected[i4 + 1] = argb.R; + expected[i4 + 2] = argb.G; + expected[i4 + 3] = argb.B; } TestOperation( source, expected, - (s, d) => Operations.ToRgb24Bytes(s, d.GetSpan(), count) + (s, d) => Operations.ToArgb32Bytes(this.Configuration, s, d.GetSpan(), count) ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackFromRgba32Bytes(int count) + public void FromBgr24Bytes(int count) { - byte[] source = CreateByteTestData(count * 4); + byte[] source = CreateByteTestData(count * 3); var expected = new TPixel[count]; for (int i = 0; i < count; i++) { - int i4 = i * 4; + int i3 = i * 3; - expected[i].PackFromRgba32(new Rgba32(source[i4 + 0], source[i4 + 1], source[i4 + 2], source[i4 + 3])); + expected[i].FromBgr24(new Bgr24(source[i3 + 2], source[i3 + 1], source[i3])); } TestOperation( source, expected, - (s, d) => Operations.PackFromRgba32Bytes(s, d.GetSpan(), count) + (s, d) => Operations.FromBgr24Bytes(this.Configuration, s, d.GetSpan(), count) ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void ToRgba32Bytes(int count) + public void ToBgr24Bytes(int count) { TPixel[] source = CreatePixelTestData(count); - byte[] expected = new byte[count * 4]; - var rgba = default(Rgba32); + byte[] expected = new byte[count * 3]; + var bgr = default(Bgr24); for (int i = 0; i < count; i++) { - int i4 = i * 4; - source[i].ToRgba32(ref rgba); - expected[i4] = rgba.R; - expected[i4 + 1] = rgba.G; - expected[i4 + 2] = rgba.B; - expected[i4 + 3] = rgba.A; + int i3 = i * 3; + bgr.FromScaledVector4(source[i].ToScaledVector4()); + expected[i3] = bgr.B; + expected[i3 + 1] = bgr.G; + expected[i3 + 2] = bgr.R; } TestOperation( source, expected, - (s, d) => Operations.ToRgba32Bytes(s, d.GetSpan(), count) + (s, d) => Operations.ToBgr24Bytes(this.Configuration, s, d.GetSpan(), count) ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackFromRgb48Bytes(int count) + public void FromBgra32Bytes(int count) { - byte[] source = CreateByteTestData(count * 6); - Span sourceSpan = source.AsSpan(); + byte[] source = CreateByteTestData(count * 4); var expected = new TPixel[count]; - var rgba64 = new Rgba64(0, 0, 0, 65535); for (int i = 0; i < count; i++) { - int i6 = i * 6; - rgba64.Rgb = MemoryMarshal.Cast(sourceSpan.Slice(i6, 6))[0]; - expected[i].PackFromRgba64(rgba64); + int i4 = i * 4; + + expected[i].FromBgra32(new Bgra32(source[i4 + 2], source[i4 + 1], source[i4 + 0], source[i4 + 3])); } TestOperation( source, expected, - (s, d) => Operations.PackFromRgb48Bytes(s, d.GetSpan(), count) + (s, d) => Operations.FromBgra32Bytes(this.Configuration, s, d.GetSpan(), count) ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void ToRgb48Bytes(int count) + public void ToBgra32Bytes(int count) { TPixel[] source = CreatePixelTestData(count); - byte[] expected = new byte[count * 6]; - Rgb48 rgb = default; + byte[] expected = new byte[count * 4]; + var bgra = default(Bgra32); for (int i = 0; i < count; i++) { - int i6 = i * 6; - source[i].ToRgb48(ref rgb); - Rgba64Bytes rgb48Bytes = Unsafe.As(ref rgb); - expected[i6] = rgb48Bytes[0]; - expected[i6 + 1] = rgb48Bytes[1]; - expected[i6 + 2] = rgb48Bytes[2]; - expected[i6 + 3] = rgb48Bytes[3]; - expected[i6 + 4] = rgb48Bytes[4]; - expected[i6 + 5] = rgb48Bytes[5]; + int i4 = i * 4; + bgra.FromScaledVector4(source[i].ToScaledVector4()); + expected[i4] = bgra.B; + expected[i4 + 1] = bgra.G; + expected[i4 + 2] = bgra.R; + expected[i4 + 3] = bgra.A; } TestOperation( source, expected, - (s, d) => Operations.ToRgb48Bytes(s, d.GetSpan(), count) + (s, d) => Operations.ToBgra32Bytes(this.Configuration, s, d.GetSpan(), count) ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackFromRgba64Bytes(int count) + public void FromRgb24Bytes(int count) { - byte[] source = CreateByteTestData(count * 8); - Span sourceSpan = source.AsSpan(); + byte[] source = CreateByteTestData(count * 3); var expected = new TPixel[count]; for (int i = 0; i < count; i++) { - int i8 = i * 8; - expected[i].PackFromRgba64(MemoryMarshal.Cast(sourceSpan.Slice(i8, 8))[0]); + int i3 = i * 3; + + expected[i].FromRgb24(new Rgb24(source[i3 + 0], source[i3 + 1], source[i3 + 2])); } TestOperation( source, expected, - (s, d) => Operations.PackFromRgba64Bytes(s, d.GetSpan(), count) + (s, d) => Operations.FromRgb24Bytes(this.Configuration, s, d.GetSpan(), count) ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void ToRgba64Bytes(int count) + public void ToRgb24Bytes(int count) { TPixel[] source = CreatePixelTestData(count); - byte[] expected = new byte[count * 8]; - Rgba64 rgba = default; + byte[] expected = new byte[count * 3]; + var rgb = default(Rgb24); for (int i = 0; i < count; i++) { - int i8 = i * 8; - source[i].ToRgba64(ref rgba); - Rgba64Bytes rgba64Bytes = Unsafe.As(ref rgba); - expected[i8] = rgba64Bytes[0]; - expected[i8 + 1] = rgba64Bytes[1]; - expected[i8 + 2] = rgba64Bytes[2]; - expected[i8 + 3] = rgba64Bytes[3]; - expected[i8 + 4] = rgba64Bytes[4]; - expected[i8 + 5] = rgba64Bytes[5]; - expected[i8 + 6] = rgba64Bytes[6]; - expected[i8 + 7] = rgba64Bytes[7]; + int i3 = i * 3; + rgb.FromScaledVector4(source[i].ToScaledVector4()); + expected[i3] = rgb.R; + expected[i3 + 1] = rgb.G; + expected[i3 + 2] = rgb.B; } TestOperation( source, expected, - (s, d) => Operations.ToRgba64Bytes(s, d.GetSpan(), count) + (s, d) => Operations.ToRgb24Bytes(this.Configuration, s, d.GetSpan(), count) ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackFromBgr24Bytes(int count) + public void FromRgba32Bytes(int count) { - byte[] source = CreateByteTestData(count * 3); + byte[] source = CreateByteTestData(count * 4); var expected = new TPixel[count]; for (int i = 0; i < count; i++) { - int i3 = i * 3; + int i4 = i * 4; - expected[i].PackFromRgba32(new Rgba32(source[i3 + 2], source[i3 + 1], source[i3 + 0], 255)); + expected[i].FromRgba32(new Rgba32(source[i4 + 0], source[i4 + 1], source[i4 + 2], source[i4 + 3])); } TestOperation( source, expected, - (s, d) => Operations.PackFromBgr24Bytes(s, d.GetSpan(), count) + (s, d) => Operations.FromRgba32Bytes(this.Configuration, s, d.GetSpan(), count) ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void ToBgr24Bytes(int count) + public void ToRgba32Bytes(int count) { TPixel[] source = CreatePixelTestData(count); - byte[] expected = new byte[count * 3]; - var bgr = default(Bgr24); + byte[] expected = new byte[count * 4]; + var rgba = default(Rgba32); for (int i = 0; i < count; i++) { - int i3 = i * 3; - source[i].ToBgr24(ref bgr); - expected[i3] = bgr.B; - expected[i3 + 1] = bgr.G; - expected[i3 + 2] = bgr.R; + int i4 = i * 4; + rgba.FromScaledVector4(source[i].ToScaledVector4()); + expected[i4] = rgba.R; + expected[i4 + 1] = rgba.G; + expected[i4 + 2] = rgba.B; + expected[i4 + 3] = rgba.A; } TestOperation( source, expected, - (s, d) => Operations.ToBgr24Bytes(s, d.GetSpan(), count) + (s, d) => Operations.ToRgba32Bytes(this.Configuration, s, d.GetSpan(), count) ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackFromBgra32Bytes(int count) + public void FromRgb48Bytes(int count) { - byte[] source = CreateByteTestData(count * 4); + byte[] source = CreateByteTestData(count * 6); + Span sourceSpan = source.AsSpan(); var expected = new TPixel[count]; for (int i = 0; i < count; i++) { - int i4 = i * 4; - - expected[i].PackFromRgba32(new Rgba32(source[i4 + 2], source[i4 + 1], source[i4 + 0], source[i4 + 3])); + int i6 = i * 6; + expected[i].FromRgb48(MemoryMarshal.Cast(sourceSpan.Slice(i6, 6))[0]); } TestOperation( source, expected, - (s, d) => Operations.PackFromBgra32Bytes(s, d.GetSpan(), count) + (s, d) => Operations.FromRgb48Bytes(this.Configuration, s, d.GetSpan(), count) ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void ToZyxwBytes(int count) + public void ToRgb48Bytes(int count) { TPixel[] source = CreatePixelTestData(count); - byte[] expected = new byte[count * 4]; - var bgra = default(Bgra32); + byte[] expected = new byte[count * 6]; + Rgb48 rgb = default; for (int i = 0; i < count; i++) { - int i4 = i * 4; - source[i].ToBgra32(ref bgra); - expected[i4] = bgra.B; - expected[i4 + 1] = bgra.G; - expected[i4 + 2] = bgra.R; - expected[i4 + 3] = bgra.A; + int i6 = i * 6; + rgb.FromScaledVector4(source[i].ToScaledVector4()); + OctetBytes rgb48Bytes = Unsafe.As(ref rgb); + expected[i6] = rgb48Bytes[0]; + expected[i6 + 1] = rgb48Bytes[1]; + expected[i6 + 2] = rgb48Bytes[2]; + expected[i6 + 3] = rgb48Bytes[3]; + expected[i6 + 4] = rgb48Bytes[4]; + expected[i6 + 5] = rgb48Bytes[5]; } TestOperation( source, expected, - (s, d) => Operations.ToBgra32Bytes(s, d.GetSpan(), count) + (s, d) => Operations.ToRgb48Bytes(this.Configuration, s, d.GetSpan(), count) ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackFromArgb32Bytes(int count) + public void FromRgba64Bytes(int count) { - byte[] source = CreateByteTestData(count * 4); + byte[] source = CreateByteTestData(count * 8); + Span sourceSpan = source.AsSpan(); var expected = new TPixel[count]; for (int i = 0; i < count; i++) { - int i4 = i * 4; - - expected[i].PackFromRgba32(new Rgba32(source[i4 + 1], source[i4 + 2], source[i4 + 3], source[i4 + 0])); + int i8 = i * 8; + expected[i].FromRgba64(MemoryMarshal.Cast(sourceSpan.Slice(i8, 8))[0]); } TestOperation( source, expected, - (s, d) => Operations.PackFromArgb32Bytes(s, d.GetSpan(), count) + (s, d) => Operations.FromRgba64Bytes(this.Configuration, s, d.GetSpan(), count) ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void ToArgb32Bytes(int count) + public void ToRgba64Bytes(int count) { TPixel[] source = CreatePixelTestData(count); - byte[] expected = new byte[count * 4]; - var argb = default(Argb32); + byte[] expected = new byte[count * 8]; + Rgba64 rgba = default; for (int i = 0; i < count; i++) { - int i4 = i * 4; - source[i].ToArgb32(ref argb); - expected[i4] = argb.A; - expected[i4 + 1] = argb.R; - expected[i4 + 2] = argb.G; - expected[i4 + 3] = argb.B; + int i8 = i * 8; + rgba.FromScaledVector4(source[i].ToScaledVector4()); + OctetBytes rgba64Bytes = Unsafe.As(ref rgba); + expected[i8] = rgba64Bytes[0]; + expected[i8 + 1] = rgba64Bytes[1]; + expected[i8 + 2] = rgba64Bytes[2]; + expected[i8 + 3] = rgba64Bytes[3]; + expected[i8 + 4] = rgba64Bytes[4]; + expected[i8 + 5] = rgba64Bytes[5]; + expected[i8 + 6] = rgba64Bytes[6]; + expected[i8 + 7] = rgba64Bytes[7]; } TestOperation( source, expected, - (s, d) => Operations.ToArgb32Bytes(s, d.GetSpan(), count) + (s, d) => Operations.ToRgba64Bytes(this.Configuration, s, d.GetSpan(), count) ); } - private class TestBuffers : IDisposable - where TSource : struct - where TDest : struct + internal static Vector4[] CreateExpectedVector4Data(TPixel[] source) { - public TSource[] SourceBuffer { get; } - public IMemoryOwner ActualDestBuffer { get; } - public TDest[] ExpectedDestBuffer { get; } - - public TestBuffers(TSource[] source, TDest[] expectedDest) - { - this.SourceBuffer = source; - this.ExpectedDestBuffer = expectedDest; - this.ActualDestBuffer = Configuration.Default.MemoryAllocator.Allocate(expectedDest.Length); - } + var expected = new Vector4[source.Length]; - public void Dispose() + for (int i = 0; i < expected.Length; i++) { - this.ActualDestBuffer.Dispose(); + expected[i] = source[i].ToVector4(); } + return expected; + } - private const float Tolerance = 0.0001f; + internal static Vector4[] CreateExpectedScaledVector4Data(TPixel[] source) + { + var expected = new Vector4[source.Length]; - public void Verify() + for (int i = 0; i < expected.Length; i++) { - int count = this.ExpectedDestBuffer.Length; - - if (typeof(TDest) == typeof(Vector4)) - { - - Span expected = MemoryMarshal.Cast(this.ExpectedDestBuffer.AsSpan()); - Span actual = MemoryMarshal.Cast(this.ActualDestBuffer.GetSpan()); - - for (int i = 0; i < count; i++) - { - // ReSharper disable PossibleNullReferenceException - Assert.Equal(expected[i], actual[i], new ApproximateFloatComparer(0.001f)); - // ReSharper restore PossibleNullReferenceException - } - } - else - { - Span expected = this.ExpectedDestBuffer.AsSpan(); - Span actual = this.ActualDestBuffer.GetSpan(); - for (int i = 0; i < count; i++) - { - Assert.Equal(expected[i], actual[i]); - } - } + expected[i] = source[i].ToScaledVector4(); } + return expected; } internal static void TestOperation( @@ -630,7 +534,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats for (int i = 0; i < result.Length; i++) { Vector4 v = GetVector(rnd); - result[i].PackFromVector4(v); + result[i].FromVector4(v); } return result; @@ -645,7 +549,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats for (int i = 0; i < result.Length; i++) { Vector4 v = GetVector(rnd); - result[i].PackFromScaledVector4(v); + result[i].FromScaledVector4(v); } return result; @@ -674,7 +578,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [StructLayout(LayoutKind.Sequential)] - private unsafe struct Rgba64Bytes + internal unsafe struct OctetBytes { public fixed byte Data[8]; @@ -683,10 +587,56 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - ref byte self = ref Unsafe.As(ref this); + ref byte self = ref Unsafe.As(ref this); return Unsafe.Add(ref self, idx); } } } + + private class TestBuffers : IDisposable + where TSource : struct + where TDest : struct + { + public TSource[] SourceBuffer { get; } + public IMemoryOwner ActualDestBuffer { get; } + public TDest[] ExpectedDestBuffer { get; } + + public TestBuffers(TSource[] source, TDest[] expectedDest) + { + this.SourceBuffer = source; + this.ExpectedDestBuffer = expectedDest; + this.ActualDestBuffer = Configuration.Default.MemoryAllocator.Allocate(expectedDest.Length); + } + + public void Dispose() => this.ActualDestBuffer.Dispose(); + + public void Verify() + { + int count = this.ExpectedDestBuffer.Length; + + if (typeof(TDest) == typeof(Vector4)) + { + Span expected = MemoryMarshal.Cast(this.ExpectedDestBuffer.AsSpan()); + Span actual = MemoryMarshal.Cast(this.ActualDestBuffer.GetSpan()); + + var comparer = new ApproximateFloatComparer(0.001f); + for (int i = 0; i < count; i++) + { + // ReSharper disable PossibleNullReferenceException + Assert.Equal(expected[i], actual[i], comparer); + // ReSharper restore PossibleNullReferenceException + } + } + else + { + Span expected = this.ExpectedDestBuffer.AsSpan(); + Span actual = this.ActualDestBuffer.GetSpan(); + for (int i = 0; i < count; i++) + { + Assert.Equal(expected[i], actual[i]); + } + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs index 3a80c3436d..46e5fbc3cd 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rg32Tests.cs @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Rg32_PackFromScaledVector4() + public void Rg32_FromScaledVector4() { // arrange var rg32 = new Rg32(Vector2.One); @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // act Vector4 scaled = rg32.ToScaledVector4(); - pixel.PackFromScaledVector4(scaled); + pixel.FromScaledVector4(scaled); uint actual = pixel.PackedValue; // assert @@ -68,97 +68,5 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(Vector2.Zero, new Rg32(Vector2.One * -1234.0f).ToVector2()); Assert.Equal(Vector2.One, new Rg32(Vector2.One * 1234.0f).ToVector2()); } - - [Fact] - public void Rg32_ToRgb24() - { - // arrange - var rg32 = new Rg32(0.1f, -0.3f); - var actual = default(Rgb24); - var expected = new Rgb24(25, 0, 0); - - // act - rg32.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rg32_ToRgba32() - { - // arrange - var rg32 = new Rg32(0.1f, -0.3f); - var actual = default(Rgba32); - var expected = new Rgba32(25, 0, 0, 255); - - // act - rg32.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rg32_ToBgr24() - { - // arrange - var rg32 = new Rg32(0.1f, -0.3f); - var actual = default(Bgr24); - var expected = new Bgr24(25, 0, 0); - - // act - rg32.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rg32_ToArgb32() - { - // arrange - var rg32 = new Rg32(0.1f, -0.3f); - var actual = default(Argb32); - var expected = new Argb32(25, 0, 0, 255); - - // act - rg32.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rg32_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(Rg32); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 65535, 0); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rg32_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(Rg32); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 65535, 0, 65535); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs index d056bf30d2..92e8d302d4 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb24Tests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.Numerics; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -11,7 +10,12 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats public class Rgb24Tests { public static readonly TheoryData ColorData = - new TheoryData() { { 1, 2, 3 }, { 4, 5, 6 }, { 0, 255, 42 } }; + new TheoryData() + { + { 1, 2, 3 }, + { 4, 5, 6 }, + { 0, 255, 42 } + }; [Theory] [MemberData(nameof(ColorData))] @@ -61,10 +65,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void PackFromRgba32() + public void FromRgba32() { var rgb = default(Rgb24); - rgb.PackFromRgba32(new Rgba32(1, 2, 3, 4)); + rgb.FromRgba32(new Rgba32(1, 2, 3, 4)); Assert.Equal(1, rgb.R); Assert.Equal(2, rgb.G); @@ -78,10 +82,10 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats a / 255f); [Fact] - public void PackFromVector4() + public void FromVector4() { var rgb = default(Rgb24); - rgb.PackFromVector4(Vec(1, 2, 3, 4)); + rgb.FromVector4(Vec(1, 2, 3, 4)); Assert.Equal(1, rgb.R); Assert.Equal(2, rgb.G); @@ -96,80 +100,14 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats 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); - + Rgba32 rgba = default; 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); - } - - [Fact] - public void Rgb24_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(Rgb24); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgb24_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(Rgb24); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 65535); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs index 77d6544f00..d30e498609 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgb48Tests.cs @@ -18,182 +18,45 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(9830, rgb.G); Assert.Equal(19660, rgb.B); Assert.Equal(29491, rgb.A); - - rgb = new Rgba64(5243 / 65535F, 9830 / 65535F, 19660 / 65535F, 29491 / 65535F); - - Assert.Equal(5243, rgb.R); - Assert.Equal(9830, rgb.G); - Assert.Equal(19660, rgb.B); - Assert.Equal(29491, rgb.A); } [Fact] public void Rgb48_ToVector4() - { - Assert.Equal(new Vector4(0, 0, 0, 1), new Rgb48(Vector3.Zero).ToVector4()); - Assert.Equal(Vector4.One, new Rgb48(Vector3.One).ToVector4()); - } + => Assert.Equal(Vector4.One, new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue).ToVector4()); [Fact] public void Rgb48_ToScaledVector4() - { - // arrange - var short2 = new Rgb48(Vector3.One); - - // act - Vector4 actual = short2.ToScaledVector4(); - - // assert - Assert.Equal(1, actual.X); - Assert.Equal(1, actual.Y); - Assert.Equal(1, actual.Z); - Assert.Equal(1, actual.W); - } + => Assert.Equal(Vector4.One, new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue).ToVector4()); [Fact] - public void Rgb48_PackFromScaledVector4() + public void Rgb48_FromScaledVector4() { // arrange var pixel = default(Rgb48); - var short3 = new Rgb48(Vector3.One); - var expected = new Rgb48(Vector3.One); + var short3 = new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue); + var expected = new Rgb48(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue); // act Vector4 scaled = short3.ToScaledVector4(); - pixel.PackFromScaledVector4(scaled); + pixel.FromScaledVector4(scaled); // assert Assert.Equal(expected, pixel); } - [Fact] - public void Rgb48_Clamping() - { - Assert.Equal(new Vector4(0, 0, 0, 1), new Rgb48(Vector3.One * -1234.0f).ToVector4()); - Assert.Equal(Vector4.One, new Rgb48(Vector3.One * 1234.0f).ToVector4()); - } - - [Fact] - public void Rgb48_ToRgb24() - { - // arrange - var rgba48 = new Rgb48(0.08f, 0.15f, 0.30f); - var actual = default(Rgb24); - var expected = new Rgb24(20, 38, 76); - - // act - rgba48.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - [Fact] public void Rgb48_ToRgba32() { // arrange - var rgba48 = new Rgb48(0.08f, 0.15f, 0.30f); - var actual = default(Rgba32); + var rgba48 = new Rgb48(5140, 9766, 19532); var expected = new Rgba32(20, 38, 76, 255); // act + Rgba32 actual = default; rgba48.ToRgba32(ref actual); // assert Assert.Equal(expected, actual); } - - [Fact] - public void Rgb48_ToArgb32() - { - // arrange - var rgba48 = new Rgb48(0.08f, 0.15f, 0.30f); - var actual = default(Argb32); - var expected = new Argb32(20, 38, 76, 255); - - // act - rgba48.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgba64_ToBgr24() - { - // arrange - var rgb48 = new Rgb48(0.08f, 0.15f, 0.30f); - var actual = default(Bgr24); - var expected = new Bgr24(20, 38, 76); - - // act - rgb48.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgb48_ToBgra32() - { - // arrange - var rgba48 = new Rgb48(0.08f, 0.15f, 0.30f); - var actual = default(Bgra32); - var expected = new Bgra32(20, 38, 76, 255); - - // act - rgba48.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgb48_PackFromRgba32_ToRgba32() - { - // arrange - var rgb48 = default(Rgb48); - var actual = default(Rgba32); - var expected = new Rgba32(20, 38, 76, 255); - - // act - rgb48.PackFromRgba32(expected); - rgb48.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgb48_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(Rgb48); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgb48_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(Rgb48); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 65535); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs index fcb3b6c7c8..a897dd4cdb 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba1010102Tests.cs @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Rgba1010102_PackFromScaledVector4() + public void Rgba1010102_FromScaledVector4() { // arrange var rgba = new Rgba1010102(Vector4.One); @@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // act Vector4 scaled = rgba.ToScaledVector4(); - actual.PackFromScaledVector4(scaled); + actual.FromScaledVector4(scaled); // assert Assert.Equal(expected, actual.PackedValue); @@ -71,144 +71,19 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(Vector4.One, new Rgba1010102(Vector4.One * 1234.0f).ToVector4()); } - [Fact] - public void Rgba1010102_ToRgb24() - { - // arrange - var rgba = new Rgba1010102(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Rgb24); - var expected = new Rgb24(25, 0, 128); - - // act - rgba.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - [Fact] public void Rgba1010102_ToRgba32() { // arrange var rgba = new Rgba1010102(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Rgba32); var expected = new Rgba32(25, 0, 128, 0); // act + Rgba32 actual = default; rgba.ToRgba32(ref actual); // assert Assert.Equal(expected, actual); } - - [Fact] - public void Rgba1010102_ToBgr24() - { - // arrange - var rgba = new Rgba1010102(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Bgr24); - var expected = new Bgr24(25, 0, 128); - - // act - rgba.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgba1010102_ToBgra32() - { - // arrange - var rgba = new Rgba1010102(0.1f, -0.3f, 0.5f, -0.7f); - var actual = default(Bgra32); - var expected = new Bgra32(25, 0, 128, 0); - - // act - rgba.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgba1010102_PackFromRgba32_ToRgba32() - { - // arrange - var rgba = default(Rgba1010102); - var expected = new Rgba32(25, 0, 128, 0); - var actual = default(Rgba32); - - // act - rgba.PackFromRgba32(expected); - rgba.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgba1010102_PackFromBgra32_ToBgra32() - { - // arrange - var rgba = default(Rgba1010102); - var expected = new Bgra32(25, 0, 128, 0); - var actual = default(Bgra32); - - // act - rgba.PackFromBgra32(expected); - rgba.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgba1010102_PackFromArgb32_ToArgb32() - { - // arrange - var rgba = default(Rgba1010102); - var expected = new Argb32(25, 0, 128, 0); - var actual = default(Argb32); - - // act - rgba.PackFromArgb32(expected); - rgba.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgba1010102_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(Rgba1010102); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgba1010102_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(Rgba1010102); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 65535); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs index 4b2c187765..ad1d137406 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba32Tests.cs @@ -5,6 +5,7 @@ using System; using System.Numerics; using SixLabors.ImageSharp.PixelFormats; using Xunit; +using Xunit.Abstractions; namespace SixLabors.ImageSharp.Tests.PixelFormats { @@ -19,12 +20,12 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Fact] public void AreEqual() { - Rgba32 color1 = new Rgba32(0, 0, 0); - Rgba32 color2 = new Rgba32(0, 0, 0, 1F); - Rgba32 color3 = Rgba32.FromHex("#000"); - Rgba32 color4 = Rgba32.FromHex("#000F"); - Rgba32 color5 = Rgba32.FromHex("#000000"); - Rgba32 color6 = Rgba32.FromHex("#000000FF"); + var color1 = new Rgba32(0, 0, 0); + var color2 = new Rgba32(0, 0, 0, 1F); + var color3 = Rgba32.FromHex("#000"); + var color4 = Rgba32.FromHex("#000F"); + var color5 = Rgba32.FromHex("#000000"); + var color6 = Rgba32.FromHex("#000000FF"); Assert.Equal(color1, color2); Assert.Equal(color1, color3); @@ -39,11 +40,11 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Fact] public void AreNotEqual() { - Rgba32 color1 = new Rgba32(255, 0, 0, 255); - Rgba32 color2 = new Rgba32(0, 0, 0, 255); - Rgba32 color3 = Rgba32.FromHex("#000"); - Rgba32 color4 = Rgba32.FromHex("#000000"); - Rgba32 color5 = Rgba32.FromHex("#FF000000"); + var color1 = new Rgba32(255, 0, 0, 255); + var color2 = new Rgba32(0, 0, 0, 255); + var color3 = Rgba32.FromHex("#000"); + var color4 = Rgba32.FromHex("#000000"); + var color5 = Rgba32.FromHex("#FF000000"); Assert.NotEqual(color1, color2); Assert.NotEqual(color1, color3); @@ -57,25 +58,25 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Fact] public void ConstructorAssignsProperties() { - Rgba32 color1 = new Rgba32(1, .1f, .133f, .864f); + var color1 = new Rgba32(1, .1f, .133f, .864f); Assert.Equal(255, color1.R); Assert.Equal((byte)Math.Round(.1f * 255), color1.G); Assert.Equal((byte)Math.Round(.133f * 255), color1.B); Assert.Equal((byte)Math.Round(.864f * 255), color1.A); - Rgba32 color2 = new Rgba32(1, .1f, .133f); + var color2 = new Rgba32(1, .1f, .133f); Assert.Equal(255, color2.R); Assert.Equal(Math.Round(.1f * 255), color2.G); Assert.Equal(Math.Round(.133f * 255), color2.B); Assert.Equal(255, color2.A); - Rgba32 color4 = new Rgba32(new Vector3(1, .1f, .133f)); + var color4 = new Rgba32(new Vector3(1, .1f, .133f)); Assert.Equal(255, color4.R); Assert.Equal(Math.Round(.1f * 255), color4.G); Assert.Equal(Math.Round(.133f * 255), color4.B); Assert.Equal(255, color4.A); - Rgba32 color5 = new Rgba32(new Vector4(1, .1f, .133f, .5f)); + var color5 = new Rgba32(new Vector4(1, .1f, .133f, .5f)); Assert.Equal(255, color5.R); Assert.Equal(Math.Round(.1f * 255), color5.G); Assert.Equal(Math.Round(.133f * 255), color5.B); @@ -112,7 +113,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Fact] public unsafe void ByteLayout() { - Rgba32 color = new Rgba32(1, 2, 3, 4); + var color = new Rgba32(1, 2, 3, 4); byte* colorBase = (byte*)&color; Assert.Equal(1, colorBase[0]); Assert.Equal(2, colorBase[1]); @@ -159,7 +160,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Rgba32_PackFromScaledVector4() + public void Rgba32_FromScaledVector4() { // arrange var rgba = new Rgba32(Vector4.One); @@ -168,7 +169,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // act Vector4 scaled = rgba.ToScaledVector4(); - actual.PackFromScaledVector4(scaled); + actual.FromScaledVector4(scaled); // assert Assert.Equal(expected, actual.PackedValue); @@ -181,23 +182,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(Vector4.One, new Rgba32(Vector4.One * +1234.0f).ToVector4()); } - [Fact] - public void Rgba32_ToRgb24() - { - // arrange - var rgba = new Rgba32(+0.1f, -0.3f, +0.5f, -0.7f); - var actual = default(Rgb24); - var expected = new Rgb24(0x1a, 0, 0x80); - - // act - rgba.ToRgb24(ref actual); - - // assert - Assert.Equal(expected.R, actual.R); - Assert.Equal(expected.G, actual.G); - Assert.Equal(expected.B, actual.B); - } - [Fact] public void Rgba32_ToRgba32() { @@ -207,59 +191,14 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var expected = new Rgba32(0x1a, 0, 0x80, 0); // act - rgba.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgba32_ToBgr24() - { - // arrange - var rgba = new Rgba32(+0.1f, -0.3f, +0.5f, -0.7f); - var actual = default(Bgr24); - var expected = new Bgr24(0x1a, 0, 0x80); - - // act - rgba.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgba32_ToBgra32() - { - // arrange - var rgba = new Rgba32(+0.1f, -0.3f, +0.5f, -0.7f); - var actual = default(Bgra32); - var expected = new Bgra32(0x1a, 0, 0x80, 0); - - // act - rgba.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgba32_ToArgb32() - { - // arrange - var rgba = new Rgba32(+0.1f, -0.3f, +0.5f, -0.7f); - var actual = default(Argb32); - var expected = new Argb32(0x1a, 0, 0x80, 0); - - // act - rgba.ToArgb32(ref actual); + actual.FromRgba32(rgba); // assert Assert.Equal(expected, actual); } [Fact] - public void Rgba32_PackFromRgba32_ToRgba32() + public void Rgba32_FromRgba32_ToRgba32() { // arrange var rgba = default(Rgba32); @@ -267,15 +206,15 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var expected = new Rgba32(0x1a, 0, 0x80, 0); // act - rgba.PackFromRgba32(expected); - rgba.ToRgba32(ref actual); + rgba.FromRgba32(expected); + actual.FromRgba32(rgba); // assert Assert.Equal(expected, actual); } [Fact] - public void Rgba32_PackFromBgra32_ToRgba32() + public void Rgba32_FromBgra32_ToRgba32() { // arrange var rgba = default(Rgba32); @@ -283,15 +222,15 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var expected = new Bgra32(0x1a, 0, 0x80, 0); // act - rgba.PackFromBgra32(expected); - rgba.ToBgra32(ref actual); + rgba.FromBgra32(expected); + actual.FromRgba32(rgba); // assert Assert.Equal(expected, actual); } [Fact] - public void Rgba32_PackFromArgb32_ToArgb32() + public void Rgba32_FromArgb32_ToArgb32() { // arrange var rgba = default(Rgba32); @@ -299,15 +238,15 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var expected = new Argb32(0x1a, 0, 0x80, 0); // act - rgba.PackFromArgb32(expected); - rgba.ToArgb32(ref actual); + rgba.FromArgb32(expected); + actual.FromRgba32(rgba); // assert Assert.Equal(expected, actual); } [Fact] - public void Rgba32_PackFromRgb48_ToRgb48() + public void Rgba32_FromRgb48() { // arrange var input = default(Rgba32); @@ -315,15 +254,15 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var expected = new Rgb48(65535, 0, 65535); // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); + input.FromRgb48(expected); + actual.FromScaledVector4(input.ToScaledVector4()); // assert Assert.Equal(expected, actual); } [Fact] - public void Rgba32_PackFromRgba64_ToRgba64() + public void Rgba32_FromRgba64() { // arrange var input = default(Rgba32); @@ -331,8 +270,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var expected = new Rgba64(65535, 0, 65535, 0); // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); + input.FromRgba64(expected); + actual.FromScaledVector4(input.ToScaledVector4()); // assert Assert.Equal(expected, actual); diff --git a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs index 92b36a1c62..3e5d7a56ed 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Rgba64Tests.cs @@ -13,33 +13,31 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats public void Rgba64_PackedValues() { Assert.Equal((ulong)0x73334CCC2666147B, new Rgba64(5243, 9830, 19660, 29491).PackedValue); - Assert.Equal((ulong)0x73334CCC2666147B, new Rgba64(0.08f, 0.15f, 0.30f, 0.45f).PackedValue); - var rgba = new Rgba64(0x73334CCC2666147B); - Assert.Equal(5243, rgba.R); - Assert.Equal(9830, rgba.G); - Assert.Equal(19660, rgba.B); - Assert.Equal(29491, rgba.A); // Test the limits. - Assert.Equal((ulong)0x0, new Rgba64(Vector4.Zero).PackedValue); - Assert.Equal(0xFFFFFFFFFFFFFFFF, new Rgba64(Vector4.One).PackedValue); + Assert.Equal((ulong)0x0, new Rgba64(0, 0, 0, 0).PackedValue); + Assert.Equal(0xFFFFFFFFFFFFFFFF, new Rgba64( + ushort.MaxValue, + ushort.MaxValue, + ushort.MaxValue, + ushort.MaxValue).PackedValue); + // Test data ordering - Assert.Equal(0xC7AD8F5C570A1EB8, new Rgba64(((float)0x1EB8) / 0xffff, ((float)0x570A) / 0xffff, ((float)0x8F5C) / 0xffff, ((float)0xC7AD) / 0xffff).PackedValue); - Assert.Equal(0xC7AD8F5C570A1EB8, new Rgba64(0.12f, 0.34f, 0.56f, 0.78f).PackedValue); + Assert.Equal(0xC7AD8F5C570A1EB8, new Rgba64(0x1EB8, 0x570A, 0x8F5C, 0xC7AD).PackedValue); } [Fact] public void Rgba64_ToVector4() { - Assert.Equal(Vector4.Zero, new Rgba64(Vector4.Zero).ToVector4()); - Assert.Equal(Vector4.One, new Rgba64(Vector4.One).ToVector4()); + Assert.Equal(Vector4.Zero, new Rgba64(0, 0, 0, 0).ToVector4()); + Assert.Equal(Vector4.One, new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue).ToVector4()); } [Fact] public void Rgba64_ToScaledVector4() { // arrange - var short2 = new Rgba64(Vector4.One); + var short2 = new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue); // act Vector4 actual = short2.ToScaledVector4(); @@ -52,16 +50,16 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Rgba64_PackFromScaledVector4() + public void Rgba64_FromScaledVector4() { // arrange var pixel = default(Rgba64); - var short4 = new Rgba64(Vector4.One); + var short4 = new Rgba64(ushort.MaxValue, ushort.MaxValue, ushort.MaxValue, ushort.MaxValue); const ulong expected = 0xFFFFFFFFFFFFFFFF; // act Vector4 scaled = short4.ToScaledVector4(); - pixel.PackFromScaledVector4(scaled); + pixel.FromScaledVector4(scaled); ulong actual = pixel.PackedValue; // assert @@ -71,30 +69,19 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Fact] public void Rgba64_Clamping() { - Assert.Equal(Vector4.Zero, new Rgba64(Vector4.One * -1234.0f).ToVector4()); - Assert.Equal(Vector4.One, new Rgba64(Vector4.One * 1234.0f).ToVector4()); - } - - [Fact] - public void Rgba64_ToRgb24() - { - // arrange - var rgba64 = new Rgba64(0.08f, 0.15f, 0.30f, 0.45f); - var actual = default(Rgb24); - var expected = new Rgb24(20, 38, 76); - - // act - rgba64.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); + var zero = default(Rgba64); + var one = default(Rgba64); + zero.FromVector4(Vector4.One * -1234.0f); + one.FromVector4(Vector4.One * 1234.0f); + Assert.Equal(Vector4.Zero, zero.ToVector4()); + Assert.Equal(Vector4.One, one.ToVector4()); } [Fact] public void Rgba64_ToRgba32() { // arrange - var rgba64 = new Rgba64(0.08f, 0.15f, 0.30f, 0.45f); + var rgba64 = new Rgba64(5140, 9766, 19532, 29555); var actual = default(Rgba32); var expected = new Rgba32(20, 38, 76, 115); @@ -104,98 +91,5 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats // assert Assert.Equal(expected, actual); } - - [Fact] - public void Rgba64_ToArgb32() - { - // arrange - var rgba64 = new Rgba64(0.08f, 0.15f, 0.30f, 0.45f); - var actual = default(Argb32); - var expected = new Argb32(20, 38, 76, 115); - - // act - rgba64.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgba64_ToBgr24() - { - // arrange - var rgba64 = new Rgba64(0.08f, 0.15f, 0.30f, 0.45f); - var actual = default(Bgr24); - var expected = new Bgr24(20, 38, 76); - - // act - rgba64.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgba64_ToBgra32() - { - // arrange - var rgba64 = new Rgba64(0.08f, 0.15f, 0.30f, 0.45f); - var actual = default(Bgra32); - var expected = new Bgra32(20, 38, 76, 115); - - // act - rgba64.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgba64_PackFromRgba32_ToRgba32() - { - // arrange - var rgba64 = default(Rgba64); - var actual = default(Rgba32); - var expected = new Rgba32(20, 38, 76, 115); - - // act - rgba64.PackFromRgba32(expected); - rgba64.ToRgba32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgb48_PackFromRgb48_ToRgb48() - { - // arrange - var input = default(Rgba64); - var actual = default(Rgb48); - var expected = new Rgb48(65535, 0, 65535); - - // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Rgba64_PackFromRgba64_ToRgba64() - { - // arrange - var input = default(Rgba64); - var actual = default(Rgba64); - var expected = new Rgba64(65535, 0, 65535, 0); - - // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); - - // assert - Assert.Equal(expected, actual); - } } } diff --git a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs index a21b647a74..e880e38517 100644 --- a/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/RgbaVectorTests.cs @@ -19,12 +19,12 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Fact] public void AreEqual() { - RgbaVector color1 = new RgbaVector(0, 0, 0F); - RgbaVector color2 = new RgbaVector(0, 0, 0, 1F); - RgbaVector color3 = RgbaVector.FromHex("#000"); - RgbaVector color4 = RgbaVector.FromHex("#000F"); - RgbaVector color5 = RgbaVector.FromHex("#000000"); - RgbaVector color6 = RgbaVector.FromHex("#000000FF"); + var color1 = new RgbaVector(0, 0, 0F); + var color2 = new RgbaVector(0, 0, 0, 1F); + var color3 = RgbaVector.FromHex("#000"); + var color4 = RgbaVector.FromHex("#000F"); + var color5 = RgbaVector.FromHex("#000000"); + var color6 = RgbaVector.FromHex("#000000FF"); Assert.Equal(color1, color2); Assert.Equal(color1, color3); @@ -39,11 +39,11 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Fact] public void AreNotEqual() { - RgbaVector color1 = new RgbaVector(1, 0, 0, 1); - RgbaVector color2 = new RgbaVector(0, 0, 0, 1); - RgbaVector color3 = RgbaVector.FromHex("#000"); - RgbaVector color4 = RgbaVector.FromHex("#000000"); - RgbaVector color5 = RgbaVector.FromHex("#FF000000"); + var color1 = new RgbaVector(1, 0, 0, 1); + var color2 = new RgbaVector(0, 0, 0, 1); + var color3 = RgbaVector.FromHex("#000"); + var color4 = RgbaVector.FromHex("#000000"); + var color5 = RgbaVector.FromHex("#FF000000"); Assert.NotEqual(color1, color2); Assert.NotEqual(color1, color3); @@ -57,29 +57,17 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Fact] public void ConstructorAssignsProperties() { - RgbaVector color1 = new RgbaVector(1, .1F, .133F, .864F); + var color1 = new RgbaVector(1, .1F, .133F, .864F); Assert.Equal(1F, color1.R); Assert.Equal(.1F, color1.G); Assert.Equal(.133F, color1.B); Assert.Equal(.864F, color1.A); - RgbaVector color2 = new RgbaVector(1, .1f, .133f); + var color2 = new RgbaVector(1, .1f, .133f); Assert.Equal(1F, color2.R); Assert.Equal(.1F, color2.G); Assert.Equal(.133F, color2.B); Assert.Equal(1F, color2.A); - - RgbaVector color4 = new RgbaVector(new Vector3(1, .1f, .133f)); - Assert.Equal(1F, color4.R); - Assert.Equal(.1F, color4.G); - Assert.Equal(.133F, color4.B); - Assert.Equal(1F, color4.A); - - RgbaVector color5 = new RgbaVector(new Vector4(1, .1f, .133f, .5f)); - Assert.Equal(1F, color5.R); - Assert.Equal(.1F, color5.G); - Assert.Equal(.133F, color5.B); - Assert.Equal(.5F, color5.A); } /// @@ -88,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Fact] public void FromAndToHex() { - RgbaVector color = RgbaVector.FromHex("#AABBCCDD"); + var color = RgbaVector.FromHex("#AABBCCDD"); Assert.Equal(170 / 255F, color.R); Assert.Equal(187 / 255F, color.G); Assert.Equal(204 / 255F, color.B); @@ -116,7 +104,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats [Fact] public void FloatLayout() { - RgbaVector color = new RgbaVector(1F, 2, 3, 4); + var color = new RgbaVector(1F, 2, 3, 4); Vector4 colorBase = Unsafe.As(ref Unsafe.Add(ref color, 0)); float[] ordered = new float[4]; colorBase.CopyTo(ordered); @@ -128,7 +116,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void RgbaVector_PackFromRgb48_ToRgb48() + public void RgbaVector_FromRgb48() { // arrange var input = default(RgbaVector); @@ -136,15 +124,15 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var expected = new Rgb48(65535, 0, 65535); // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); + input.FromRgb48(expected); + actual.FromScaledVector4(input.ToScaledVector4()); // assert Assert.Equal(expected, actual); } [Fact] - public void RgbaVector_PackFromRgba64_ToRgba64() + public void RgbaVector_FromRgba64() { // arrange var input = default(RgbaVector); @@ -152,8 +140,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var expected = new Rgba64(65535, 0, 65535, 0); // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); + input.FromRgba64(expected); + actual.FromScaledVector4(input.ToScaledVector4()); // assert Assert.Equal(expected, actual); diff --git a/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs index 5c75fcbbbc..c9a3b33c9a 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short2Tests.cs @@ -63,37 +63,22 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Short2_PackFromScaledVector4() + public void Short2_FromScaledVector4() { // arrange var pixel = default(Short2); var short2 = new Short2(Vector2.One * 0x7FFF); - ulong expected = 0x7FFF7FFF; + const ulong expected = 0x7FFF7FFF; // act Vector4 scaled = short2.ToScaledVector4(); - pixel.PackFromScaledVector4(scaled); + pixel.FromScaledVector4(scaled); uint actual = pixel.PackedValue; // assert Assert.Equal(expected, actual); } - [Fact] - public void Short2_ToRgb24() - { - // arrange - var short2 = new Short2(127.5f, -5.3f); - var actual = default(Rgb24); - var expected = new Rgb24(128, 127, 0); - - // act - short2.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - [Fact] public void Short2_ToRgba32() { @@ -110,52 +95,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Short2_ToBgr24() - { - // arrange - var short2 = new Short2(127.5f, -5.3f); - var actual = default(Bgr24); - var expected = new Bgr24(128, 127, 0); - - // act - short2.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Short2_ToArgb32() - { - // arrange - var short2 = new Short2(127.5f, -5.3f); - var actual = default(Argb32); - var expected = new Argb32(128, 127, 0, 255); - - // act - short2.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Short2_ToBgra32() - { - // arrange - var short2 = new Short2(127.5f, -5.3f); - var actual = default(Bgra32); - var expected = new Bgra32(128, 127, 0, 255); - - // act - short2.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Short2_PackFromRgba32_ToRgba32() + public void Short2_FromRgba32_ToRgba32() { // arrange var short2 = default(Short2); @@ -163,7 +103,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var expected = new Rgba32(20, 38, 0, 255); // act - short2.PackFromRgba32(expected); + short2.FromRgba32(expected); short2.ToRgba32(ref actual); // assert @@ -171,7 +111,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Short2_PackFromRgb48_ToRgb48() + public void Short2_FromRgb48() { // arrange var input = default(Short2); @@ -179,15 +119,15 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var expected = new Rgb48(65535, 65535, 0); // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); + input.FromRgb48(expected); + actual.FromScaledVector4(input.ToScaledVector4()); // assert Assert.Equal(expected, actual); } [Fact] - public void Short2_PackFromRgba64_ToRgba64() + public void Short2_FromRgba64() { // arrange var input = default(Short2); @@ -195,8 +135,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var expected = new Rgba64(65535, 65535, 0, 65535); // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); + input.FromRgba64(expected); + actual.FromScaledVector4(input.ToScaledVector4()); // assert Assert.Equal(expected, actual); diff --git a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs index 59dc385d4d..247342a053 100644 --- a/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/Short4Tests.cs @@ -51,16 +51,16 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Short4_PackFromScaledVector4() + public void Short4_FromScaledVector4() { // arrange var short4 = new Short4(Vector4.One * 0x7FFF); Vector4 scaled = short4.ToScaledVector4(); - long expected = 0x7FFF7FFF7FFF7FFF; + const long expected = 0x7FFF7FFF7FFF7FFF; // act var pixel = default(Short4); - pixel.PackFromScaledVector4(scaled); + pixel.FromScaledVector4(scaled); ulong actual = pixel.PackedValue; // assert @@ -83,36 +83,6 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats Assert.Equal(Vector4.One * -0x8000, vector2); } - [Fact] - public void Short4_ToRgb24() - { - // arrange - var shortValue = new Short4(11547, 12653, 29623, 193); - var actual = default(Rgb24); - var expected = new Rgb24(172, 177, 243); - - // act - shortValue.ToRgb24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Short4_ToBgr24() - { - // arrange - var shortValue = new Short4(11547, 12653, 29623, 193); - var actual = default(Bgr24); - var expected = new Bgr24(172, 177, 243); - - // act - shortValue.ToBgr24(ref actual); - - // assert - Assert.Equal(expected, actual); - } - [Fact] public void Short4_ToRgba32() { @@ -129,37 +99,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Short4_ToBgra32() - { - // arrange - var shortValue = new Short4(11547, 12653, 29623, 193); - var actual = default(Bgra32); - var expected = new Bgra32(172, 177, 243, 128); - - // act - shortValue.ToBgra32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Short4_ToArgb32() - { - // arrange - var shortValue = new Short4(11547, 12653, 29623, 193); - var actual = default(Argb32); - var expected = new Argb32(172, 177, 243, 128); - - // act - shortValue.ToArgb32(ref actual); - - // assert - Assert.Equal(expected, actual); - } - - [Fact] - public void Short4_PackFromRgba32_ToRgba32() + public void Short4_FromRgba32_ToRgba32() { // arrange var short4 = default(Short4); @@ -167,7 +107,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var expected = new Rgba32(20, 38, 0, 255); // act - short4.PackFromRgba32(expected); + short4.FromRgba32(expected); short4.ToRgba32(ref actual); // assert @@ -175,7 +115,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats } [Fact] - public void Short4_PackFromBgra32_ToRgba32() + public void Short4_FromBgra32_ToRgba32() { // arrange var short4 = default(Short4); @@ -183,15 +123,17 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var expected = new Bgra32(20, 38, 0, 255); // act - short4.PackFromBgra32(expected); - short4.ToBgra32(ref actual); + short4.FromBgra32(expected); + Rgba32 temp = default; + short4.ToRgba32(ref temp); + actual.FromRgba32(temp); // assert Assert.Equal(expected, actual); } [Fact] - public void Short4_PackFromArgb32_ToRgba32() + public void Short4_FromArgb32_ToRgba32() { // arrange var short4 = default(Short4); @@ -199,15 +141,17 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var expected = new Argb32(20, 38, 0, 255); // act - short4.PackFromArgb32(expected); - short4.ToArgb32(ref actual); + short4.FromArgb32(expected); + Rgba32 temp = default; + short4.ToRgba32(ref temp); + actual.FromRgba32(temp); // assert Assert.Equal(expected, actual); } [Fact] - public void Short4_PackFromRgb48_ToRgb48() + public void Short4_FromRgb48_ToRgb48() { // arrange var input = default(Short4); @@ -215,15 +159,15 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var expected = new Rgb48(65535, 0, 65535); // act - input.PackFromRgb48(expected); - input.ToRgb48(ref actual); + input.FromRgb48(expected); + actual.FromScaledVector4(input.ToScaledVector4()); // assert Assert.Equal(expected, actual); } [Fact] - public void Short4_PackFromRgba64_ToRgba64() + public void Short4_FromRgba64_ToRgba64() { // arrange var input = default(Short4); @@ -231,8 +175,8 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats var expected = new Rgba64(65535, 0, 65535, 0); // act - input.PackFromRgba64(expected); - input.ToRgba64(ref actual); + input.FromRgba64(expected); + actual.FromScaledVector4(input.ToScaledVector4()); // assert Assert.Equal(expected, actual); diff --git a/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs b/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs index 1fca398fcd..bd8c647421 100644 --- a/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/UnPackedPixelTests.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Colors public void Color_Types_From_Bytes_Produce_Equal_Scaled_Component_OutPut() { var color = new Rgba32(24, 48, 96, 192); - var colorVector = new RgbaVector(24, 48, 96, 192); + var colorVector = new RgbaVector(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); Assert.Equal(color.R, (byte)(colorVector.R * 255)); Assert.Equal(color.G, (byte)(colorVector.G * 255)); @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Tests.Colors public void Color_Types_From_Vector4_Produce_Equal_Scaled_Component_OutPut() { var color = new Rgba32(new Vector4(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F)); - var colorVector = new RgbaVector(new Vector4(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F)); + var colorVector = new RgbaVector(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); Assert.Equal(color.R, (byte)(colorVector.R * 255)); Assert.Equal(color.G, (byte)(colorVector.G * 255)); @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Colors public void Color_Types_From_Vector3_Produce_Equal_Scaled_Component_OutPut() { var color = new Rgba32(new Vector3(24 / 255F, 48 / 255F, 96 / 255F)); - var colorVector = new RgbaVector(new Vector3(24 / 255F, 48 / 255F, 96 / 255F)); + var colorVector = new RgbaVector(24 / 255F, 48 / 255F, 96 / 255F); Assert.Equal(color.R, (byte)(colorVector.R * 255)); Assert.Equal(color.G, (byte)(colorVector.G * 255)); @@ -73,76 +73,30 @@ namespace SixLabors.ImageSharp.Tests.Colors public void Color_Types_To_Vector4_Produce_Equal_OutPut() { var color = new Rgba32(24, 48, 96, 192); - var colorVector = new RgbaVector(24, 48, 96, 192); + var colorVector = new RgbaVector(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); Assert.Equal(color.ToVector4(), colorVector.ToVector4()); } - [Fact] - public void Color_Types_To_RgbBytes_Produce_Equal_OutPut() - { - var color = new Rgba32(24, 48, 96, 192); - var colorVector = new RgbaVector(24, 48, 96, 192); - - var rgb = default(Rgb24); - var rgbVector = default(Rgb24); - - color.ToRgb24(ref rgb); - colorVector.ToRgb24(ref rgbVector); - - Assert.Equal(rgb, rgbVector); - } - [Fact] public void Color_Types_To_RgbaBytes_Produce_Equal_OutPut() { var color = new Rgba32(24, 48, 96, 192); - var colorVector = new RgbaVector(24, 48, 96, 192); - - var rgba = default(Rgba32); - var rgbaVector = default(Rgba32); + var colorVector = new RgbaVector(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); + Rgba32 rgba = default; + Rgba32 rgbaVector = default; color.ToRgba32(ref rgba); colorVector.ToRgba32(ref rgbaVector); Assert.Equal(rgba, rgbaVector); } - [Fact] - public void Color_Types_To_BgrBytes_Produce_Equal_OutPut() - { - var color = new Rgba32(24, 48, 96, 192); - var colorVector = new RgbaVector(24, 48, 96, 192); - - var bgr = default(Bgr24); - var bgrVector = default(Bgr24); - - color.ToBgr24(ref bgr); - colorVector.ToBgr24(ref bgrVector); - - Assert.Equal(bgr, bgrVector); - } - - [Fact] - public void Color_Types_To_BgraBytes_Produce_Equal_OutPut() - { - var color = new Rgba32(24, 48, 96, 192); - var colorVector = new RgbaVector(24, 48, 96, 192); - - var bgra = default(Bgra32); - var bgraVector = default(Bgra32); - - color.ToBgra32(ref bgra); - colorVector.ToBgra32(ref bgraVector); - - Assert.Equal(bgra, bgraVector); - } - [Fact] public void Color_Types_To_Hex_Produce_Equal_OutPut() { var color = new Rgba32(24, 48, 96, 192); - var colorVector = new RgbaVector(24, 48, 96, 192); + var colorVector = new RgbaVector(24 / 255F, 48 / 255F, 96 / 255F, 192 / 255F); // 183060C0 Assert.Equal(color.ToHex(), colorVector.ToHex()); diff --git a/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs b/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs index 7d161d35f7..fa4862293f 100644 --- a/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs +++ b/tests/ImageSharp.Tests/Primitives/DenseMatrixTests.cs @@ -3,6 +3,7 @@ using System; using SixLabors.ImageSharp.Primitives; +using SixLabors.Primitives; using Xunit; namespace SixLabors.ImageSharp.Tests.Primitives @@ -59,6 +60,7 @@ namespace SixLabors.ImageSharp.Tests.Primitives Assert.True(dense.Rows == FloydSteinbergMatrix.GetLength(0)); Assert.Equal(3, dense.Columns); Assert.Equal(2, dense.Rows); + Assert.Equal(new Size(3, 2), dense.Size); } [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs index a32239d96f..de72f6d09e 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -10,8 +10,6 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { - using SixLabors.ImageSharp.Advanced; - public class DetectEdgesTest : FileTestBase { private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0456F); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Quantization/OctreeQuantizerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Quantization/OctreeQuantizerTests.cs new file mode 100644 index 0000000000..b3900325db --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Quantization/OctreeQuantizerTests.cs @@ -0,0 +1,58 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Quantization; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization +{ + public class OctreeQuantizerTests + { + [Fact] + public void OctreeQuantizerConstructor() + { + var quantizer = new OctreeQuantizer(128); + + Assert.Equal(128, quantizer.MaxColors); + Assert.Equal(KnownDiffusers.FloydSteinberg, quantizer.Diffuser); + + quantizer = new OctreeQuantizer(false); + Assert.Equal(QuantizerConstants.MaxColors, quantizer.MaxColors); + Assert.Null(quantizer.Diffuser); + + quantizer = new OctreeQuantizer(KnownDiffusers.Atkinson); + Assert.Equal(QuantizerConstants.MaxColors, quantizer.MaxColors); + Assert.Equal(KnownDiffusers.Atkinson, quantizer.Diffuser); + + quantizer = new OctreeQuantizer(KnownDiffusers.Atkinson, 128); + Assert.Equal(128, quantizer.MaxColors); + Assert.Equal(KnownDiffusers.Atkinson, quantizer.Diffuser); + } + + [Fact] + public void OctreeQuantizerCanCreateFrameQuantizer() + { + var quantizer = new OctreeQuantizer(); + IFrameQuantizer frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); + + Assert.NotNull(frameQuantizer); + Assert.True(frameQuantizer.Dither); + Assert.Equal(KnownDiffusers.FloydSteinberg, frameQuantizer.Diffuser); + + quantizer = new OctreeQuantizer(false); + frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); + + Assert.NotNull(frameQuantizer); + Assert.False(frameQuantizer.Dither); + Assert.Null(frameQuantizer.Diffuser); + + quantizer = new OctreeQuantizer(KnownDiffusers.Atkinson); + frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); + Assert.NotNull(frameQuantizer); + Assert.True(frameQuantizer.Dither); + Assert.Equal(KnownDiffusers.Atkinson, frameQuantizer.Diffuser); + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs new file mode 100644 index 0000000000..a4e6edd53e --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Quantization; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization +{ + public class PaletteQuantizerTests + { + private static readonly Rgba32[] Rgb = new Rgba32[] { Rgba32.Red, Rgba32.Green, Rgba32.Blue }; + + [Fact] + public void PaletteQuantizerConstructor() + { + var quantizer = new PaletteQuantizer(Rgb); + + Assert.Equal(Rgb, quantizer.Palette); + Assert.Equal(KnownDiffusers.FloydSteinberg, quantizer.Diffuser); + + quantizer = new PaletteQuantizer(Rgb, false); + Assert.Equal(Rgb, quantizer.Palette); + Assert.Null(quantizer.Diffuser); + + quantizer = new PaletteQuantizer(Rgb, KnownDiffusers.Atkinson); + Assert.Equal(Rgb, quantizer.Palette); + Assert.Equal(KnownDiffusers.Atkinson, quantizer.Diffuser); + } + + [Fact] + public void PaletteQuantizerCanCreateFrameQuantizer() + { + var quantizer = new PaletteQuantizer(Rgb); + IFrameQuantizer frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); + + Assert.NotNull(frameQuantizer); + Assert.True(frameQuantizer.Dither); + Assert.Equal(KnownDiffusers.FloydSteinberg, frameQuantizer.Diffuser); + + quantizer = new PaletteQuantizer(Rgb, false); + frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); + + Assert.NotNull(frameQuantizer); + Assert.False(frameQuantizer.Dither); + Assert.Null(frameQuantizer.Diffuser); + + quantizer = new PaletteQuantizer(Rgb, KnownDiffusers.Atkinson); + frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); + Assert.NotNull(frameQuantizer); + Assert.True(frameQuantizer.Dither); + Assert.Equal(KnownDiffusers.Atkinson, frameQuantizer.Diffuser); + } + + [Fact] + public void PaletteQuantizerThrowsOnInvalidGenericMethodCall() + { + var quantizer = new PaletteQuantizer(Rgb); + + Assert.Throws(() => ((IQuantizer)quantizer).CreateFrameQuantizer(Configuration.Default)); + } + + [Fact] + public void KnownQuantizersWebSafeTests() + { + IQuantizer quantizer = KnownQuantizers.WebSafe; + Assert.Equal(KnownDiffusers.FloydSteinberg, quantizer.Diffuser); + } + + [Fact] + public void KnownQuantizersWernerTests() + { + IQuantizer quantizer = KnownQuantizers.Werner; + Assert.Equal(KnownDiffusers.FloydSteinberg, quantizer.Diffuser); + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Quantization/WuQuantizerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Quantization/WuQuantizerTests.cs new file mode 100644 index 0000000000..625043c7f1 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Quantization/WuQuantizerTests.cs @@ -0,0 +1,58 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Quantization; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization +{ + public class WuQuantizerTests + { + [Fact] + public void WuQuantizerConstructor() + { + var quantizer = new WuQuantizer(128); + + Assert.Equal(128, quantizer.MaxColors); + Assert.Equal(KnownDiffusers.FloydSteinberg, quantizer.Diffuser); + + quantizer = new WuQuantizer(false); + Assert.Equal(QuantizerConstants.MaxColors, quantizer.MaxColors); + Assert.Null(quantizer.Diffuser); + + quantizer = new WuQuantizer(KnownDiffusers.Atkinson); + Assert.Equal(QuantizerConstants.MaxColors, quantizer.MaxColors); + Assert.Equal(KnownDiffusers.Atkinson, quantizer.Diffuser); + + quantizer = new WuQuantizer(KnownDiffusers.Atkinson, 128); + Assert.Equal(128, quantizer.MaxColors); + Assert.Equal(KnownDiffusers.Atkinson, quantizer.Diffuser); + } + + [Fact] + public void WuQuantizerCanCreateFrameQuantizer() + { + var quantizer = new WuQuantizer(); + IFrameQuantizer frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); + + Assert.NotNull(frameQuantizer); + Assert.True(frameQuantizer.Dither); + Assert.Equal(KnownDiffusers.FloydSteinberg, frameQuantizer.Diffuser); + + quantizer = new WuQuantizer(false); + frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); + + Assert.NotNull(frameQuantizer); + Assert.False(frameQuantizer.Dither); + Assert.Null(frameQuantizer.Diffuser); + + quantizer = new WuQuantizer(KnownDiffusers.Atkinson); + frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); + Assert.NotNull(frameQuantizer); + Assert.True(frameQuantizer.Dither); + Assert.Equal(KnownDiffusers.Atkinson, frameQuantizer.Diffuser); + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs index c154c8ff3c..c01c3b1bd3 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/CropTest.cs @@ -1,24 +1,33 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.Primitives; + using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { + [GroupOutput("Transforms")] public class CropTest : FileTestBase { [Theory] - [WithFileCollection(nameof(DefaultFiles), DefaultPixelType)] - public void ImageShouldCrop(TestImageProvider provider) + [WithTestPatternImages(70, 30, PixelTypes.Rgba32, 0, 0, 70, 30)] + [WithTestPatternImages(30, 70, PixelTypes.Rgba32, 7, 13, 20, 50)] + public void Crop(TestImageProvider provider, int x, int y, int w, int h) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Crop(image.Width / 2, image.Height / 2)); - image.DebugSave(provider); - } + var rect = new Rectangle(x, y, w, h); + FormattableString info = $"X{x}Y{y}.W{w}H{h}"; + provider.RunValidatingProcessorTest( + ctx => ctx.Crop(rect), + info, + appendPixelTypeToFileName: false, + comparer: ImageComparer.Exact); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs index d7e7a724c4..3c932bfaa6 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/FlipTests.cs @@ -5,23 +5,24 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using Xunit; + // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { - using SixLabors.ImageSharp.Processing; - + [GroupOutput("Transforms")] public class FlipTests { - public static readonly TheoryData FlipValues - = new TheoryData - { - { FlipMode.None }, - { FlipMode.Vertical }, - { FlipMode.Horizontal }, - }; + public static readonly TheoryData FlipValues = + new TheoryData + { + FlipMode.None, + FlipMode.Vertical, + FlipMode.Horizontal, + }; [Theory] + [WithTestPatternImages(nameof(FlipValues), 20, 37, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(FlipValues), 53, 37, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(FlipValues), 17, 32, PixelTypes.Rgba32)] public void Flip(TestImageProvider provider, FlipMode flipMode) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs new file mode 100644 index 0000000000..7d842c4e1e --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs @@ -0,0 +1,111 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; +using System.Linq; + +using SixLabors.ImageSharp.Processing.Processors.Transforms; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public partial class ResizeKernelMapTests + { + /// + /// Simplified reference implementation for functionality. + /// + internal class ReferenceKernelMap + { + private readonly ReferenceKernel[] kernels; + + public ReferenceKernelMap(ReferenceKernel[] kernels) + { + this.kernels = kernels; + } + + public int DestinationSize => this.kernels.Length; + + public ReferenceKernel GetKernel(int destinationIndex) => this.kernels[destinationIndex]; + + public static ReferenceKernelMap Calculate(IResampler sampler, int destinationSize, int sourceSize, bool normalize = true) + { + double ratio = (double)sourceSize / destinationSize; + double scale = ratio; + + if (scale < 1F) + { + scale = 1F; + } + + TolerantMath tolerantMath = TolerantMath.Default; + + double radius = tolerantMath.Ceiling(scale * sampler.Radius); + + var result = new List(); + + for (int i = 0; i < destinationSize; i++) + { + double center = ((i + .5) * ratio) - .5; + + // Keep inside bounds. + int left = (int)tolerantMath.Ceiling(center - radius); + if (left < 0) + { + left = 0; + } + + int right = (int)tolerantMath.Floor(center + radius); + if (right > sourceSize - 1) + { + right = sourceSize - 1; + } + + double sum = 0; + + double[] values = new double[right - left + 1]; + + for (int j = left; j <= right; j++) + { + double weight = sampler.GetValue((float)((j - center) / scale)); + sum += weight; + + values[j - left] = weight; + } + + if (sum > 0 && normalize) + { + for (int w = 0; w < values.Length; w++) + { + values[w] /= sum; + } + } + + float[] floatVals = values.Select(v => (float)v).ToArray(); + + result.Add(new ReferenceKernel(left, floatVals)); + } + + return new ReferenceKernelMap(result.ToArray()); + } + } + + internal struct ReferenceKernel + { + public ReferenceKernel(int left, float[] values) + { + this.Left = left; + this.Values = values; + } + + public int Left { get; } + + public float[] Values { get; } + + public int Length => this.Values.Length; + + public static implicit operator ReferenceKernel(ResizeKernel orig) + { + return new ReferenceKernel(orig.Left, orig.Values.ToArray()); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs new file mode 100644 index 0000000000..08b2949139 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs @@ -0,0 +1,241 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Transforms; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + public partial class ResizeKernelMapTests + { + private ITestOutputHelper Output { get; } + + public ResizeKernelMapTests(ITestOutputHelper output) + { + this.Output = output; + } + + /// + /// resamplerName, srcSize, destSize + /// + public static readonly TheoryData KernelMapData = new TheoryData + { + { nameof(KnownResamplers.Bicubic), 15, 10 }, + { nameof(KnownResamplers.Bicubic), 10, 15 }, + { nameof(KnownResamplers.Bicubic), 20, 20 }, + { nameof(KnownResamplers.Bicubic), 50, 40 }, + { nameof(KnownResamplers.Bicubic), 40, 50 }, + { nameof(KnownResamplers.Bicubic), 500, 200 }, + { nameof(KnownResamplers.Bicubic), 200, 500 }, + + { nameof(KnownResamplers.Bicubic), 10, 25 }, + + { nameof(KnownResamplers.Lanczos3), 16, 12 }, + { nameof(KnownResamplers.Lanczos3), 12, 16 }, + { nameof(KnownResamplers.Lanczos3), 12, 9 }, + { nameof(KnownResamplers.Lanczos3), 9, 12 }, + { nameof(KnownResamplers.Lanczos3), 6, 8 }, + { nameof(KnownResamplers.Lanczos3), 8, 6 }, + { nameof(KnownResamplers.Lanczos3), 20, 12 }, + + { nameof(KnownResamplers.Lanczos3), 5, 25 }, + { nameof(KnownResamplers.Lanczos3), 5, 50 }, + + { nameof(KnownResamplers.Lanczos3), 25, 5 }, + { nameof(KnownResamplers.Lanczos3), 50, 5 }, + { nameof(KnownResamplers.Lanczos3), 49, 5 }, + { nameof(KnownResamplers.Lanczos3), 31, 5 }, + + { nameof(KnownResamplers.Lanczos8), 500, 200 }, + { nameof(KnownResamplers.Lanczos8), 100, 10 }, + { nameof(KnownResamplers.Lanczos8), 100, 80 }, + { nameof(KnownResamplers.Lanczos8), 10, 100 }, + + // Resize_WorksWithAllResamplers_Rgba32_CalliphoraPartial_Box-0.5: + { nameof(KnownResamplers.Box), 378, 149 }, + { nameof(KnownResamplers.Box), 349, 174 }, + + // Accuracy-related regression-test cases cherry-picked from GeneratedImageResizeData + { nameof(KnownResamplers.Box), 201, 100 }, + { nameof(KnownResamplers.Box), 199, 99 }, + { nameof(KnownResamplers.Box), 10, 299 }, + { nameof(KnownResamplers.Box), 299, 10 }, + { nameof(KnownResamplers.Box), 301, 300 }, + { nameof(KnownResamplers.Box), 1180, 480 }, + + { nameof(KnownResamplers.Lanczos2), 3264, 3032 }, + + { nameof(KnownResamplers.Bicubic), 1280, 2240 }, + { nameof(KnownResamplers.Bicubic), 1920, 1680 }, + { nameof(KnownResamplers.Bicubic), 3072, 2240 }, + + { nameof(KnownResamplers.Welch), 300, 2008 }, + + // ResizeKernel.Length -related regression tests cherry-picked from GeneratedImageResizeData + { nameof(KnownResamplers.Bicubic), 10, 50 }, + { nameof(KnownResamplers.Bicubic), 49, 301 }, + { nameof(KnownResamplers.Bicubic), 301, 49 }, + { nameof(KnownResamplers.Bicubic), 1680, 1200 }, + { nameof(KnownResamplers.Box), 13, 299 }, + { nameof(KnownResamplers.Lanczos5), 3032, 600 }, + }; + + public static TheoryData GeneratedImageResizeData = + GenerateImageResizeData(); + + + [Theory(Skip = "Only for debugging and development")] + [MemberData(nameof(KernelMapData))] + public void PrintNonNormalizedKernelMap(string resamplerName, int srcSize, int destSize) + { + IResampler resampler = TestUtils.GetResampler(resamplerName); + + var kernelMap = ReferenceKernelMap.Calculate(resampler, destSize, srcSize, false); + + this.Output.WriteLine($"Actual KernelMap:\n{PrintKernelMap(kernelMap)}\n"); + } + + [Theory] + [MemberData(nameof(KernelMapData))] + public void KernelMapContentIsCorrect(string resamplerName, int srcSize, int destSize) + { + this.VerifyKernelMapContentIsCorrect(resamplerName, srcSize, destSize); + } + + // Comprehensive but expensive tests, for ResizeKernelMap. + // Enabling them can kill you, but sometimes you have to wear the burden! + // AppVeyor will never follow you to these shadows of Mordor. +#if false + [Theory] + [MemberData(nameof(GeneratedImageResizeData))] + public void KernelMapContentIsCorrect_ExtendedGeneratedValues(string resamplerName, int srcSize, int destSize) + { + this.VerifyKernelMapContentIsCorrect(resamplerName, srcSize, destSize); + } +#endif + + private void VerifyKernelMapContentIsCorrect(string resamplerName, int srcSize, int destSize) + { + IResampler resampler = TestUtils.GetResampler(resamplerName); + + var referenceMap = ReferenceKernelMap.Calculate(resampler, destSize, srcSize); + var kernelMap = ResizeKernelMap.Calculate(resampler, destSize, srcSize, Configuration.Default.MemoryAllocator); + +#if DEBUG + this.Output.WriteLine($"Expected KernelMap:\n{PrintKernelMap(referenceMap)}\n"); + this.Output.WriteLine($"Actual KernelMap:\n{PrintKernelMap(kernelMap)}\n"); +#endif + var comparer = new ApproximateFloatComparer(1e-6f); + + for (int i = 0; i < kernelMap.DestinationLength; i++) + { + ResizeKernel kernel = kernelMap.GetKernel(i); + + ReferenceKernel referenceKernel = referenceMap.GetKernel(i); + + Assert.True( + referenceKernel.Length == kernel.Length, + $"referenceKernel.Length != kernel.Length: {referenceKernel.Length} != {kernel.Length}"); + Assert.True( + referenceKernel.Left == kernel.Left, + $"referenceKernel.Left != kernel.Left: {referenceKernel.Left} != {kernel.Left}"); + float[] expectedValues = referenceKernel.Values; + Span actualValues = kernel.Values; + + Assert.Equal(expectedValues.Length, actualValues.Length); + + + + for (int x = 0; x < expectedValues.Length; x++) + { + Assert.True( + comparer.Equals(expectedValues[x], actualValues[x]), + $"{expectedValues[x]} != {actualValues[x]} @ (Row:{i}, Col:{x})"); + } + } + } + + private static string PrintKernelMap(ResizeKernelMap kernelMap) => + PrintKernelMap(kernelMap, km => km.DestinationLength, (km, i) => km.GetKernel(i)); + + private static string PrintKernelMap(ReferenceKernelMap kernelMap) => + PrintKernelMap(kernelMap, km => km.DestinationSize, (km, i) => km.GetKernel(i)); + + private static string PrintKernelMap( + TKernelMap kernelMap, + Func getDestinationSize, + Func getKernel) + { + var bld = new StringBuilder(); + + if (kernelMap is ResizeKernelMap actualMap) + { + bld.AppendLine(actualMap.Info); + } + + int destinationSize = getDestinationSize(kernelMap); + + for (int i = 0; i < destinationSize; i++) + { + ReferenceKernel kernel = getKernel(kernelMap, i); + bld.Append($"[{i:D3}] (L{kernel.Left:D3}) || "); + Span span = kernel.Values; + + for (int j = 0; j < kernel.Length; j++) + { + float value = span[j]; + bld.Append($"{value,8:F5}"); + bld.Append(" | "); + } + + bld.AppendLine(); + } + + return bld.ToString(); + } + + + private static TheoryData GenerateImageResizeData() + { + var result = new TheoryData(); + + string[] resamplerNames = typeof(KnownResamplers).GetProperties(BindingFlags.Public | BindingFlags.Static) + .Select(p => p.Name) + .Where(name => name != nameof(KnownResamplers.NearestNeighbor)) + .ToArray(); + + int[] dimensionVals = + { + // Arbitrary, small dimensions: + 9, 10, 11, 13, 49, 50, 53, 99, 100, 199, 200, 201, 299, 300, 301, + + // Typical image sizes: + 640, 480, 800, 600, 1024, 768, 1280, 960, 1536, 1180, 1600, 1200, 2048, 1536, 2240, 1680, 2560, + 1920, 3032, 2008, 3072, 2304, 3264, 2448 + }; + + IOrderedEnumerable<(int s, int d)> source2Dest = dimensionVals + .SelectMany(s => dimensionVals.Select(d => (s, d))) + .OrderBy(x => x.s + x.d); + + foreach (string resampler in resamplerNames) + { + foreach ((int s, int d) x in source2Dest) + { + result.Add(resampler, x.s, x.d); + } + } + + return result; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs deleted file mode 100644 index d5f015404d..0000000000 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.IO; -using System.Text; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Transforms; - -using SixLabors.Primitives; -using Xunit.Abstractions; - -namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms -{ - public class ResizeProfilingBenchmarks : MeasureFixture - { - public ResizeProfilingBenchmarks(ITestOutputHelper output) - : base(output) - { - } - - public int ExecutionCount { get; set; } = 50; - - // [Theory] // Benchmark, enable manually! - // [InlineData(100, 100)] - // [InlineData(2000, 2000)] - public void ResizeBicubic(int width, int height) - { - this.Measure(this.ExecutionCount, - () => - { - using (var image = new Image(width, height)) - { - image.Mutate(x => x.Resize(width / 4, height / 4)); - } - }); - } - - // [Fact] - public void PrintWeightsData() - { - var size = new Size(500, 500); - var proc = new ResizeProcessor(KnownResamplers.Bicubic, 200, 200, size); - - WeightsBuffer weights = proc.PrecomputeWeights(Configuration.Default.MemoryAllocator, proc.Width, size.Width); - - var bld = new StringBuilder(); - - foreach (WeightsWindow window in weights.Weights) - { - Span span = window.GetWindowSpan(); - for (int i = 0; i < window.Length; i++) - { - float value = span[i]; - bld.Append(value); - bld.Append("| "); - } - - bld.AppendLine(); - } - - File.WriteAllText("BicubicWeights.MD", bld.ToString()); - - // this.Output.WriteLine(bld.ToString()); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 746d8da16e..b4aff53e82 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -11,15 +11,17 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.Primitives; using Xunit; +// ReSharper disable InconsistentNaming + namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { public class ResizeTests : FileTestBase { public static readonly string[] CommonTestImages = { TestImages.Png.CalliphoraPartial }; - private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.069F); - - public static readonly TheoryData AllReSamplers = + private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F); + + public static readonly TheoryData AllResamplers = new TheoryData { { "Bicubic", KnownResamplers.Bicubic }, @@ -39,9 +41,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms }; [Theory] - [WithTestPatternImages(nameof(AllReSamplers), 100, 100, DefaultPixelType, 0.5f)] - [WithFileCollection(nameof(CommonTestImages), nameof(AllReSamplers), DefaultPixelType, 0.5f)] - [WithFileCollection(nameof(CommonTestImages), nameof(AllReSamplers), DefaultPixelType, 0.3f)] + [WithTestPatternImages(nameof(AllResamplers), 100, 100, DefaultPixelType, 0.5f)] + [WithFileCollection(nameof(CommonTestImages), nameof(AllResamplers), DefaultPixelType, 0.5f)] + [WithFileCollection(nameof(CommonTestImages), nameof(AllResamplers), DefaultPixelType, 0.3f)] public void Resize_WorksWithAllResamplers(TestImageProvider provider, string name, IResampler sampler, float ratio) where TPixel : struct, IPixel { @@ -52,10 +54,30 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms FormattableString details = $"{name}-{ratio.ToString(System.Globalization.CultureInfo.InvariantCulture)}"; image.DebugSave(provider, details); - image.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.005f), provider, details); + image.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.02f), provider, details); } } + [Theory] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType, 1)] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType, 4)] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType, 8)] + [WithFileCollection(nameof(CommonTestImages), DefaultPixelType, -1)] + public void Resize_WorksWithAllParallelismLevels(TestImageProvider provider, int maxDegreeOfParallelism) + where TPixel : struct, IPixel + { + provider.Configuration.MaxDegreeOfParallelism = + maxDegreeOfParallelism > 0 ? maxDegreeOfParallelism : Environment.ProcessorCount; + + FormattableString details = $"MDP{maxDegreeOfParallelism}"; + + provider.RunValidatingProcessorTest( + x => x.Resize(x.GetCurrentSize() / 2), + details, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + } + [Theory] [WithTestPatternImages(100, 100, DefaultPixelType)] public void Resize_Compand(TestImageProvider provider) @@ -75,16 +97,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms public void Resize_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2, true)); - - image.DebugSave(provider); - image.CompareToReferenceOutput(ValidatorComparer, provider); - } + provider.RunValidatingProcessorTest(x => x.Resize(x.GetCurrentSize() / 2), comparer: ValidatorComparer); } - [Theory] [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] public void Resize_ThrowsForWrappedMemoryImage(TestImageProvider provider) @@ -105,20 +120,21 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms } } - [Theory] - [WithFile(TestImages.Png.Kaboom, DefaultPixelType)] - public void Resize_DoesNotBleedAlphaPixels(TestImageProvider provider) + [WithFile(TestImages.Png.Kaboom, DefaultPixelType, false)] + [WithFile(TestImages.Png.Kaboom, DefaultPixelType, true)] + public void Resize_DoesNotBleedAlphaPixels(TestImageProvider provider, bool compand) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Resize(image.Width / 2, image.Height / 2)); - image.DebugSave(provider); - image.CompareToReferenceOutput(ValidatorComparer, provider); - } - } + string details = compand ? "Compand" : ""; + provider.RunValidatingProcessorTest( + x => x.Resize(x.GetCurrentSize() / 2, compand), + details, + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: false); + } + [Theory] [WithFile(TestImages.Gif.Giphy, DefaultPixelType)] public void Resize_IsAppliedToAllFrames(TestImageProvider provider) @@ -178,6 +194,32 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms } } + [Theory] + [WithTestPatternImages(100, 10, DefaultPixelType)] + public void ResizeWidthCannotKeepAspectKeepsOnePixel(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Resize(5, 0)); + Assert.Equal(5, image.Width); + Assert.Equal(1, image.Height); + } + } + + [Theory] + [WithTestPatternImages(10, 100, DefaultPixelType)] + public void ResizeHeightCannotKeepAspectKeepsOnePixel(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(x => x.Resize(0, 5)); + Assert.Equal(1, image.Width); + Assert.Equal(5, image.Height); + } + } + [Theory] [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] public void ResizeWithCropWidthMode(TestImageProvider provider) @@ -324,7 +366,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms [InlineData(2, 0)] public static void BicubicWindowOscillatesCorrectly(float x, float expected) { - var sampler = KnownResamplers.Bicubic; + IResampler sampler = KnownResamplers.Bicubic; float result = sampler.GetValue(x); Assert.Equal(result, expected); @@ -338,7 +380,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms [InlineData(2, 0)] public static void TriangleWindowOscillatesCorrectly(float x, float expected) { - var sampler = KnownResamplers.Triangle; + IResampler sampler = KnownResamplers.Triangle; float result = sampler.GetValue(x); Assert.Equal(result, expected); @@ -352,7 +394,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms [InlineData(2, 0)] public static void Lanczos3WindowOscillatesCorrectly(float x, float expected) { - var sampler = KnownResamplers.Lanczos3; + IResampler sampler = KnownResamplers.Lanczos3; float result = sampler.GetValue(x); Assert.Equal(result, expected); @@ -366,7 +408,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms [InlineData(4, 0)] public static void Lanczos5WindowOscillatesCorrectly(float x, float expected) { - var sampler = KnownResamplers.Lanczos5; + IResampler sampler = KnownResamplers.Lanczos5; float result = sampler.GetValue(x); Assert.Equal(result, expected); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs index c0db205f9e..7801c71432 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs @@ -7,7 +7,8 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { - public class RotateTests : FileTestBase + [GroupOutput("Transforms")] + public class RotateTests { public static readonly TheoryData RotateAngles = new TheoryData @@ -25,29 +26,21 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms }; [Theory] - [WithTestPatternImages(nameof(RotateAngles), 100, 50, DefaultPixelType)] - [WithTestPatternImages(nameof(RotateAngles), 50, 100, DefaultPixelType)] + [WithTestPatternImages(nameof(RotateAngles), 100, 50, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(RotateAngles), 50, 100, PixelTypes.Rgba32)] public void Rotate_WithAngle(TestImageProvider provider, float value) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Rotate(value)); - image.DebugSave(provider, value); - } + provider.RunValidatingProcessorTest(ctx => ctx.Rotate(value), value, appendPixelTypeToFileName: false); } [Theory] - [WithTestPatternImages(nameof(RotateEnumValues), 100, 50, DefaultPixelType)] - [WithTestPatternImages(nameof(RotateEnumValues), 50, 100, DefaultPixelType)] + [WithTestPatternImages(nameof(RotateEnumValues), 100, 50, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(RotateEnumValues), 50, 100, PixelTypes.Rgba32)] public void Rotate_WithRotateTypeEnum(TestImageProvider provider, RotateMode value) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.Rotate(value)); - image.DebugSave(provider, value); - } + provider.RunValidatingProcessorTest(ctx => ctx.Rotate(value), value, appendPixelTypeToFileName: false); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs index d1d2ea0771..29c51543fc 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { foreach (string resamplerName in ResamplerNames) { - IResampler sampler = GetResampler(resamplerName); + IResampler sampler = TestUtils.GetResampler(resamplerName); using (Image image = provider.GetImage()) { image.Mutate(i => i.Skew(x, y, sampler)); @@ -68,17 +68,5 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms } } } - - private static IResampler GetResampler(string name) - { - PropertyInfo property = typeof(KnownResamplers).GetTypeInfo().GetProperty(name); - - if (property is null) - { - throw new Exception($"No resampler named '{name}"); - } - - return (IResampler)property.GetValue(null); - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs new file mode 100644 index 0000000000..70159e18ac --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformBuilderTests.cs @@ -0,0 +1,71 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class AffineTransformBuilderTests : TransformBuilderTestBase + { + protected override AffineTransformBuilder CreateBuilder(Rectangle rectangle) => new AffineTransformBuilder(); + + protected override void AppendRotationDegrees(AffineTransformBuilder builder, float degrees) + => builder.AppendRotationDegrees(degrees); + + protected override void AppendRotationDegrees(AffineTransformBuilder builder, float degrees, Vector2 origin) + => builder.AppendRotationDegrees(degrees, origin); + + protected override void AppendRotationRadians(AffineTransformBuilder builder, float radians) + => builder.AppendRotationRadians(radians); + + protected override void AppendRotationRadians(AffineTransformBuilder builder, float radians, Vector2 origin) + => builder.AppendRotationRadians(radians, origin); + + protected override void AppendScale(AffineTransformBuilder builder, SizeF scale) + => builder.AppendScale(scale); + + protected override void AppendSkewDegrees(AffineTransformBuilder builder, float degreesX, float degreesY) + => builder.AppendSkewDegrees(degreesX, degreesY); + + protected override void AppendSkewDegrees(AffineTransformBuilder builder, float degreesX, float degreesY, Vector2 origin) + => builder.AppendSkewDegrees(degreesX, degreesY, origin); + + protected override void AppendSkewRadians(AffineTransformBuilder builder, float radiansX, float radiansY) + => builder.AppendSkewRadians(radiansX, radiansY); + + protected override void AppendSkewRadians(AffineTransformBuilder builder, float radiansX, float radiansY, Vector2 origin) + => builder.AppendSkewRadians(radiansX, radiansY, origin); + + protected override void AppendTranslation(AffineTransformBuilder builder, PointF translate) + => builder.AppendTranslation(translate); + + protected override void PrependRotationRadians(AffineTransformBuilder builder, float radians) + => builder.PrependRotationRadians(radians); + + protected override void PrependRotationRadians(AffineTransformBuilder builder, float radians, Vector2 origin) + => builder.PrependRotationRadians(radians, origin); + + protected override void PrependScale(AffineTransformBuilder builder, SizeF scale) + => builder.PrependScale(scale); + + protected override void PrependSkewRadians(AffineTransformBuilder builder, float radiansX, float radiansY) + => builder.PrependSkewRadians(radiansX, radiansY); + + protected override void PrependSkewRadians(AffineTransformBuilder builder, float radiansX, float radiansY, Vector2 origin) + => builder.PrependSkewRadians(radiansX, radiansY, origin); + + protected override void PrependTranslation(AffineTransformBuilder builder, PointF translate) + => builder.PrependTranslation(translate); + + protected override Vector2 Execute( + AffineTransformBuilder builder, + Rectangle rectangle, + Vector2 sourcePoint) + { + Matrix3x2 matrix = builder.BuildMatrix(rectangle); + return Vector2.Transform(sourcePoint, matrix); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs index edc6994e7a..ed6d3ef2bc 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/AffineTransformTests.cs @@ -17,7 +17,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms { private readonly ITestOutputHelper Output; - private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0085f, 3); + // 1 byte difference on one color component. + private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0134F, 3); /// /// angleDeg, sx, sy, tx, ty @@ -65,10 +66,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms nameof(KnownResamplers.Lanczos8), }; - public AffineTransformTests(ITestOutputHelper output) - { - this.Output = output; - } + public AffineTransformTests(ITestOutputHelper output) => this.Output = output; /// /// The output of an "all white" image should be "all white" or transparent, regardless of the transformation and the resampler. @@ -81,15 +79,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms IResampler resampler = GetResampler(resamplerName); using (Image image = provider.GetImage()) { - var rotate = Matrix3x2.CreateRotation((float)Math.PI / 4F, new Vector2(5 / 2F, 5 / 2F)); - var translate = Matrix3x2.CreateTranslation((7 - 5) / 2F, (7 - 5) / 2F); - - Rectangle sourceRectangle = image.Bounds(); - Matrix3x2 matrix = rotate * translate; + AffineTransformBuilder builder = new AffineTransformBuilder() + .AppendRotationDegrees(30); - Rectangle destRectangle = TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, matrix); - - image.Mutate(c => c.Transform(matrix, resampler, destRectangle)); + image.Mutate(c => c.Transform(builder, resampler)); image.DebugSave(provider, resamplerName); VerifyAllPixelsAreWhiteOrTransparent(image); @@ -107,14 +100,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms { using (Image image = provider.GetImage()) { - Matrix3x2 rotate = Matrix3x2Extensions.CreateRotationDegrees(angleDeg); - var translate = Matrix3x2.CreateTranslation(tx, ty); - var scale = Matrix3x2.CreateScale(sx, sy); - Matrix3x2 m = rotate * scale * translate; + image.DebugSave(provider, $"_original"); + AffineTransformBuilder builder = new AffineTransformBuilder() + .AppendRotationDegrees(angleDeg) + .AppendScale(new SizeF(sx, sy)) + .AppendTranslation(new PointF(tx, ty)); - this.PrintMatrix(m); + this.PrintMatrix(builder.BuildMatrix(image.Size())); - image.Mutate(i => i.Transform(m, KnownResamplers.Bicubic)); + image.Mutate(i => i.Transform(builder, KnownResamplers.Bicubic)); FormattableString testOutputDetails = $"R({angleDeg})_S({sx},{sy})_T({tx},{ty})"; image.DebugSave(provider, testOutputDetails); @@ -129,9 +123,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms { using (Image image = provider.GetImage()) { - Matrix3x2 m = this.MakeManuallyCenteredMatrix(angleDeg, s, image); + AffineTransformBuilder builder = new AffineTransformBuilder() + .AppendRotationDegrees(angleDeg) + .AppendScale(new SizeF(s, s)); - image.Mutate(i => i.Transform(m, KnownResamplers.Bicubic)); + image.Mutate(i => i.Transform(builder, KnownResamplers.Bicubic)); FormattableString testOutputDetails = $"R({angleDeg})_S({s})"; image.DebugSave(provider, testOutputDetails); @@ -158,13 +154,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void Transform_FromSourceRectangle1(TestImageProvider provider) where TPixel : struct, IPixel { - var rectangle = new Rectangle(48, 0, 96, 36); + var rectangle = new Rectangle(48, 0, 48, 24); using (Image image = provider.GetImage()) { - var m = Matrix3x2.CreateScale(2.0F, 1.5F); + image.DebugSave(provider, $"_original"); + AffineTransformBuilder builder = new AffineTransformBuilder() + .AppendScale(new SizeF(2, 1.5F)); - image.Mutate(i => i.Transform(m, KnownResamplers.Spline, rectangle)); + image.Mutate(i => i.Transform(rectangle, builder, KnownResamplers.Spline)); image.DebugSave(provider); image.CompareToReferenceOutput(ValidatorComparer, provider); @@ -176,13 +174,14 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void Transform_FromSourceRectangle2(TestImageProvider provider) where TPixel : struct, IPixel { - var rectangle = new Rectangle(0, 24, 48, 48); + var rectangle = new Rectangle(0, 24, 48, 24); using (Image image = provider.GetImage()) { - var m = Matrix3x2.CreateScale(1.0F, 2.0F); + AffineTransformBuilder builder = new AffineTransformBuilder() + .AppendScale(new SizeF(1F, 2F)); - image.Mutate(i => i.Transform(m, KnownResamplers.Spline, rectangle)); + image.Mutate(i => i.Transform(rectangle, builder, KnownResamplers.Spline)); image.DebugSave(provider); image.CompareToReferenceOutput(ValidatorComparer, provider); @@ -197,33 +196,17 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms IResampler sampler = GetResampler(resamplerName); using (Image image = provider.GetImage()) { - Matrix3x2 m = this.MakeManuallyCenteredMatrix(50, 0.6f, image); + AffineTransformBuilder builder = new AffineTransformBuilder() + .AppendRotationDegrees(50) + .AppendScale(new SizeF(.6F, .6F)); - image.Mutate(i => - { - i.Transform(m, sampler); - }); + image.Mutate(i => i.Transform(builder, sampler)); image.DebugSave(provider, resamplerName); image.CompareToReferenceOutput(ValidatorComparer, provider, resamplerName); } } - private Matrix3x2 MakeManuallyCenteredMatrix(float angleDeg, float s, Image image) - where TPixel : struct, IPixel - { - Matrix3x2 rotate = Matrix3x2Extensions.CreateRotationDegrees(angleDeg); - Vector2 toCenter = 0.5f * new Vector2(image.Width, image.Height); - var translate = Matrix3x2.CreateTranslation(-toCenter); - var translateBack = Matrix3x2.CreateTranslation(toCenter); - var scale = Matrix3x2.CreateScale(s); - - Matrix3x2 m = translate * rotate * scale * translateBack; - - this.PrintMatrix(m); - return m; - } - private static IResampler GetResampler(string name) { PropertyInfo property = typeof(KnownResamplers).GetTypeInfo().GetProperty(name); @@ -240,12 +223,15 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms where TPixel : struct, IPixel { Span data = image.Frames.RootFrame.GetPixelSpan(); - var rgba = default(Rgba32); var white = new Rgb24(255, 255, 255); foreach (TPixel pixel in data) { + Rgba32 rgba = default; pixel.ToRgba32(ref rgba); - if (rgba.A == 0) continue; + if (rgba.A == 0) + { + continue; + } Assert.Equal(white, rgba.Rgb); } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs index 154167f15f..6731debd36 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; @@ -33,5 +34,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms Assert.Equal(cropRectangle, processor.CropRectangle); } + + [Fact] + public void CropRectangleWithInvalidBoundsThrowsException() + { + var cropRectangle = Rectangle.Inflate(this.SourceBounds(), 5, 5); + Assert.Throws(() => this.operations.Crop(cropRectangle)); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs new file mode 100644 index 0000000000..d82cd1689d --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformBuilderTests.cs @@ -0,0 +1,62 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Numerics; +using SixLabors.ImageSharp.Processing; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class ProjectiveTransformBuilderTests : TransformBuilderTestBase + { + protected override ProjectiveTransformBuilder CreateBuilder(Rectangle rectangle) => new ProjectiveTransformBuilder(); + + protected override void AppendRotationDegrees(ProjectiveTransformBuilder builder, float degrees) => builder.AppendRotationDegrees(degrees); + + protected override void AppendRotationDegrees(ProjectiveTransformBuilder builder, float degrees, Vector2 origin) => builder.AppendRotationDegrees(degrees, origin); + + protected override void AppendRotationRadians(ProjectiveTransformBuilder builder, float radians) => builder.AppendRotationRadians(radians); + + protected override void AppendRotationRadians(ProjectiveTransformBuilder builder, float radians, Vector2 origin) => builder.AppendRotationRadians(radians, origin); + + protected override void AppendScale(ProjectiveTransformBuilder builder, SizeF scale) => builder.AppendScale(scale); + + protected override void AppendSkewDegrees(ProjectiveTransformBuilder builder, float degreesX, float degreesY) + => builder.AppendSkewDegrees(degreesX, degreesY); + + protected override void AppendSkewDegrees(ProjectiveTransformBuilder builder, float degreesX, float degreesY, Vector2 origin) + => builder.AppendSkewDegrees(degreesX, degreesY, origin); + + protected override void AppendSkewRadians(ProjectiveTransformBuilder builder, float radiansX, float radiansY) + => builder.AppendSkewRadians(radiansX, radiansY); + + protected override void AppendSkewRadians(ProjectiveTransformBuilder builder, float radiansX, float radiansY, Vector2 origin) + => builder.AppendSkewRadians(radiansX, radiansY, origin); + + protected override void AppendTranslation(ProjectiveTransformBuilder builder, PointF translate) => builder.AppendTranslation(translate); + + protected override void PrependRotationRadians(ProjectiveTransformBuilder builder, float radians) => builder.PrependRotationRadians(radians); + + protected override void PrependScale(ProjectiveTransformBuilder builder, SizeF scale) => builder.PrependScale(scale); + + protected override void PrependSkewRadians(ProjectiveTransformBuilder builder, float radiansX, float radiansY) + => builder.PrependSkewRadians(radiansX, radiansY); + + protected override void PrependSkewRadians(ProjectiveTransformBuilder builder, float radiansX, float radiansY, Vector2 origin) + => builder.PrependSkewRadians(radiansX, radiansY, origin); + + protected override void PrependTranslation(ProjectiveTransformBuilder builder, PointF translate) => builder.PrependTranslation(translate); + + protected override void PrependRotationRadians(ProjectiveTransformBuilder builder, float radians, Vector2 origin) => + builder.PrependRotationRadians(radians, origin); + + protected override Vector2 Execute( + ProjectiveTransformBuilder builder, + Rectangle rectangle, + Vector2 sourcePoint) + { + Matrix4x4 matrix = builder.BuildMatrix(rectangle); + return Vector2.Transform(sourcePoint, matrix); + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs index 8cf9dd62f5..1da660d222 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs @@ -60,10 +60,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms }; - public ProjectiveTransformTests(ITestOutputHelper output) - { - this.Output = output; - } + public ProjectiveTransformTests(ITestOutputHelper output) => this.Output = output; [Theory] [WithTestPatternImages(nameof(ResamplerNames), 150, 150, PixelTypes.Rgba32)] @@ -73,9 +70,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms IResampler sampler = GetResampler(resamplerName); using (Image image = provider.GetImage()) { - Matrix4x4 m = ProjectiveTransformHelper.CreateTaperMatrix(image.Size(), TaperSide.Right, TaperCorner.Both, .5F); + ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder() + .AppendTaper(TaperSide.Right, TaperCorner.Both, .5F); - image.Mutate(i => { i.Transform(m, sampler); }); + image.Mutate(i => i.Transform(builder, sampler)); image.DebugSave(provider, resamplerName); image.CompareToReferenceOutput(ValidatorComparer, provider, resamplerName); @@ -89,8 +87,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms { using (Image image = provider.GetImage()) { - Matrix4x4 m = ProjectiveTransformHelper.CreateTaperMatrix(image.Size(), taperSide, taperCorner, .5F); - image.Mutate(i => { i.Transform(m); }); + ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder() + .AppendTaper(taperSide, taperCorner, .5F); + + image.Mutate(i => i.Transform(builder)); FormattableString testOutputDetails = $"{taperSide}-{taperCorner}"; image.DebugSave(provider, testOutputDetails); @@ -110,10 +110,13 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms // https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/transforms/non-affine using (Image image = provider.GetImage()) { - Matrix4x4 m = Matrix4x4.Identity; - m.M13 = 0.01F; + Matrix4x4 matrix = Matrix4x4.Identity; + matrix.M13 = 0.01F; + + ProjectiveTransformBuilder builder = new ProjectiveTransformBuilder() + .AppendMatrix(matrix); - image.Mutate(i => { i.Transform(m); }); + image.Mutate(i => i.Transform(builder)); image.DebugSave(provider); image.CompareToReferenceOutput(TolerantComparer, provider); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs new file mode 100644 index 0000000000..71e3b71797 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs @@ -0,0 +1,275 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.ImageSharp.Processing.Processors.Transforms; +using SixLabors.Primitives; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public abstract class TransformBuilderTestBase + { + private static readonly ApproximateFloatComparer Comparer = new ApproximateFloatComparer(1e-6f); + + public static readonly TheoryData ScaleTranslate_Data = + new TheoryData + { + // scale, translate, source, expectedDest + { Vector2.One, Vector2.Zero, Vector2.Zero, Vector2.Zero }, + { Vector2.One, Vector2.Zero, new Vector2(10, 20), new Vector2(10, 20) }, + { Vector2.One, new Vector2(3, 1), new Vector2(10, 20), new Vector2(13, 21) }, + { new Vector2(2, 0.5f), new Vector2(3, 1), new Vector2(10, 20), new Vector2(23, 11) }, + }; + + [Theory] + [MemberData(nameof(ScaleTranslate_Data))] + public void _1Scale_2Translate(Vector2 scale, Vector2 translate, Vector2 source, Vector2 expectedDest) + { + // These operations should be size-agnostic: + var size = new Size(123, 321); + TBuilder builder = this.CreateBuilder(size); + + this.AppendScale(builder, new SizeF(scale)); + this.AppendTranslation(builder, translate); + + Vector2 actualDest = this.Execute(builder, new Rectangle(Point.Empty, size), source); + Assert.True(Comparer.Equals(expectedDest, actualDest)); + } + + public static readonly TheoryData TranslateScale_Data = + new TheoryData + { + // translate, scale, source, expectedDest + { Vector2.Zero, Vector2.One, Vector2.Zero, Vector2.Zero }, + { Vector2.Zero, Vector2.One, new Vector2(10, 20), new Vector2(10, 20) }, + { new Vector2(3, 1), new Vector2(2, 0.5f), new Vector2(10, 20), new Vector2(26, 10.5f) }, + }; + + [Theory] + [MemberData(nameof(TranslateScale_Data))] + public void _1Translate_2Scale(Vector2 translate, Vector2 scale, Vector2 source, Vector2 expectedDest) + { + // Translate ans scale are size-agnostic: + var size = new Size(456, 432); + TBuilder builder = this.CreateBuilder(size); + + this.AppendTranslation(builder, translate); + this.AppendScale(builder, new SizeF(scale)); + + Vector2 actualDest = this.Execute(builder, new Rectangle(Point.Empty, size), source); + Assert.Equal(expectedDest, actualDest, Comparer); + } + + [Theory] + [InlineData(10, 20)] + [InlineData(-20, 10)] + public void LocationOffsetIsPrepended(int locationX, int locationY) + { + var rectangle = new Rectangle(locationX, locationY, 10, 10); + TBuilder builder = this.CreateBuilder(rectangle); + + this.AppendScale(builder, new SizeF(2, 2)); + + Vector2 actual = this.Execute(builder, rectangle, Vector2.One); + Vector2 expected = new Vector2(-locationX + 1, -locationY + 1) * 2; + + Assert.Equal(actual, expected, Comparer); + } + + [Theory] + [InlineData(200, 100, 10, 42, 84)] + [InlineData(200, 100, 100, 42, 84)] + [InlineData(100, 200, -10, 42, 84)] + public void AppendRotationDegrees_WithoutSpecificRotationCenter_RotationIsCenteredAroundImageCenter( + int width, + int height, + float degrees, + float x, + float y) + { + var size = new Size(width, height); + TBuilder builder = this.CreateBuilder(size); + + this.AppendRotationDegrees(builder, degrees); + + // TODO: We should also test CreateRotationMatrixDegrees() (and all TransformUtils stuff!) for correctness + Matrix3x2 matrix = TransformUtils.CreateRotationMatrixDegrees(degrees, size); + + var position = new Vector2(x, y); + var expected = Vector2.Transform(position, matrix); + Vector2 actual = this.Execute(builder, new Rectangle(Point.Empty, size), position); + + Assert.Equal(actual, expected, Comparer); + } + + [Theory] + [InlineData(200, 100, 10, 30, 61, 42, 84)] + [InlineData(200, 100, 100, 30, 10, 20, 84)] + [InlineData(100, 200, -10, 30, 20, 11, 84)] + public void AppendRotationDegrees_WithRotationCenter( + int width, + int height, + float degrees, + float cx, + float cy, + float x, + float y) + { + var size = new Size(width, height); + TBuilder builder = this.CreateBuilder(size); + + var centerPoint = new Vector2(cx, cy); + this.AppendRotationDegrees(builder, degrees, centerPoint); + + var matrix = Matrix3x2.CreateRotation(GeometryUtilities.DegreeToRadian(degrees), centerPoint); + + var position = new Vector2(x, y); + var expected = Vector2.Transform(position, matrix); + Vector2 actual = this.Execute(builder, new Rectangle(Point.Empty, size), position); + + Assert.Equal(actual, expected, Comparer); + } + + [Theory] + [InlineData(200, 100, 10, 10, 42, 84)] + [InlineData(200, 100, 100, 100, 42, 84)] + [InlineData(100, 200, -10, -10, 42, 84)] + public void AppendSkewDegrees_WithoutSpecificSkewCenter_SkewIsCenteredAroundImageCenter( + int width, + int height, + float degreesX, + float degreesY, + float x, + float y) + { + var size = new Size(width, height); + TBuilder builder = this.CreateBuilder(size); + + this.AppendSkewDegrees(builder, degreesX, degreesY); + + Matrix3x2 matrix = TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, size); + + var position = new Vector2(x, y); + var expected = Vector2.Transform(position, matrix); + Vector2 actual = this.Execute(builder, new Rectangle(Point.Empty, size), position); + Assert.Equal(actual, expected, Comparer); + } + + [Theory] + [InlineData(200, 100, 10, 10, 30, 61, 42, 84)] + [InlineData(200, 100, 100, 100, 30, 10, 20, 84)] + [InlineData(100, 200, -10, -10, 30, 20, 11, 84)] + public void AppendSkewDegrees_WithSkewCenter( + int width, + int height, + float degreesX, + float degreesY, + float cx, + float cy, + float x, + float y) + { + var size = new Size(width, height); + TBuilder builder = this.CreateBuilder(size); + + var centerPoint = new Vector2(cx, cy); + this.AppendSkewDegrees(builder, degreesX, degreesY, centerPoint); + + var matrix = Matrix3x2.CreateSkew(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), centerPoint); + + var position = new Vector2(x, y); + var expected = Vector2.Transform(position, matrix); + Vector2 actual = this.Execute(builder, new Rectangle(Point.Empty, size), position); + + Assert.Equal(actual, expected, Comparer); + } + + [Fact] + public void AppendPrependOpposite() + { + var rectangle = new Rectangle(-1, -1, 3, 3); + TBuilder b1 = this.CreateBuilder(rectangle); + TBuilder b2 = this.CreateBuilder(rectangle); + + const float pi = (float)Math.PI; + + // Forwards + this.AppendRotationRadians(b1, pi); + this.AppendSkewRadians(b1, pi, pi); + this.AppendScale(b1, new SizeF(2, 0.5f)); + this.AppendRotationRadians(b1, pi / 2, new Vector2(-0.5f, -0.1f)); + this.AppendSkewRadians(b1, pi, pi / 2, new Vector2(-0.5f, -0.1f)); + this.AppendTranslation(b1, new PointF(123, 321)); + + // Backwards + this.PrependTranslation(b2, new PointF(123, 321)); + this.PrependSkewRadians(b2, pi, pi / 2, new Vector2(-0.5f, -0.1f)); + this.PrependRotationRadians(b2, pi / 2, new Vector2(-0.5f, -0.1f)); + this.PrependScale(b2, new SizeF(2, 0.5f)); + this.PrependSkewRadians(b2, pi, pi); + this.PrependRotationRadians(b2, pi); + + Vector2 p1 = this.Execute(b1, rectangle, new Vector2(32, 65)); + Vector2 p2 = this.Execute(b2, rectangle, new Vector2(32, 65)); + + Assert.Equal(p1, p2, Comparer); + } + + [Theory] + [InlineData(0, 1)] + [InlineData(1, 0)] + [InlineData(-1, 0)] + public void ThrowsForInvalidSizes(int width, int height) + { + var size = new Size(width, height); + + Assert.ThrowsAny( + () => + { + TBuilder builder = this.CreateBuilder(size); + this.Execute(builder, new Rectangle(Point.Empty, size), Vector2.Zero); + }); + } + + protected TBuilder CreateBuilder(Size size) => this.CreateBuilder(new Rectangle(Point.Empty, size)); + + protected abstract TBuilder CreateBuilder(Rectangle rectangle); + + protected abstract void AppendRotationDegrees(TBuilder builder, float degrees); + + protected abstract void AppendRotationDegrees(TBuilder builder, float degrees, Vector2 origin); + + protected abstract void AppendRotationRadians(TBuilder builder, float radians); + + protected abstract void AppendRotationRadians(TBuilder builder, float radians, Vector2 origin); + + protected abstract void AppendScale(TBuilder builder, SizeF scale); + + protected abstract void AppendSkewDegrees(TBuilder builder, float degreesX, float degreesY); + + protected abstract void AppendSkewDegrees(TBuilder builder, float degreesX, float degreesY, Vector2 origin); + + protected abstract void AppendSkewRadians(TBuilder builder, float radiansX, float radiansY); + + protected abstract void AppendSkewRadians(TBuilder builder, float radiansX, float radiansY, Vector2 origin); + + protected abstract void AppendTranslation(TBuilder builder, PointF translate); + + protected abstract void PrependRotationRadians(TBuilder builder, float radians); + + protected abstract void PrependRotationRadians(TBuilder builder, float radians, Vector2 origin); + + protected abstract void PrependScale(TBuilder builder, SizeF scale); + + protected abstract void PrependSkewRadians(TBuilder builder, float radiansX, float radiansY); + + protected abstract void PrependSkewRadians(TBuilder builder, float radiansX, float radiansY, Vector2 origin); + + protected abstract void PrependTranslation(TBuilder builder, PointF translate); + + protected abstract Vector2 Execute(TBuilder builder, Rectangle rectangle, Vector2 sourcePoint); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs index 146ed62304..909e505357 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms Assert.Equal(ExifDataType.Long, profile.GetValue(ExifTag.PixelXDimension).DataType); Assert.Equal(ExifDataType.Long, profile.GetValue(ExifTag.PixelYDimension).DataType); - TransformHelpers.UpdateDimensionalMetData(img); + TransformProcessorHelpers.UpdateDimensionalMetData(img); Assert.Equal(ExifDataType.Short, profile.GetValue(ExifTag.PixelXDimension).DataType); Assert.Equal(ExifDataType.Short, profile.GetValue(ExifTag.PixelYDimension).DataType); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs similarity index 54% rename from tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs rename to tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs index 32538090dc..65989556d2 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs @@ -13,7 +13,7 @@ using SixLabors.ImageSharp.PixelFormats; using Xunit; using Xunit.Abstractions; -namespace SixLabors.ImageSharp.Tests.Formats.Jpg +namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks { public class JpegProfilingBenchmarks : MeasureFixture { @@ -22,18 +22,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { } - public static readonly TheoryData DecodeJpegData = new TheoryData() + public static readonly TheoryData DecodeJpegData = new TheoryData { - TestImages.Jpeg.Baseline.Cmyk, - TestImages.Jpeg.Baseline.Ycck, - TestImages.Jpeg.Baseline.Calliphora, - TestImages.Jpeg.Baseline.Jpeg400, - TestImages.Jpeg.Baseline.Jpeg420Exif, - TestImages.Jpeg.Baseline.Jpeg444, + TestImages.Jpeg.BenchmarkSuite.Jpeg400_SmallMonochrome, + TestImages.Jpeg.BenchmarkSuite.Jpeg420Exif_MidSizeYCbCr, + TestImages.Jpeg.BenchmarkSuite.Lake_Small444YCbCr, + TestImages.Jpeg.BenchmarkSuite.MissingFF00ProgressiveBedroom159_MidSize420YCbCr, + TestImages.Jpeg.BenchmarkSuite.BadRstProgressive518_Large444YCbCr, + TestImages.Jpeg.BenchmarkSuite.ExifGetString750Transform_Huge420YCbCr, }; - // [Theory] // Benchmark, enable manually - // [MemberData(nameof(DecodeJpegData))] + [Theory(Skip = ProfilingSetup.SkipProfilingTests)] + [MemberData(nameof(DecodeJpegData))] public void DecodeJpeg(string fileName) { this.DecodeJpegBenchmarkImpl(fileName, new JpegDecoder()); @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg return; } - const int ExecutionCount = 30; + const int ExecutionCount = 20; if (!Vector.IsHardwareAccelerated) { @@ -62,17 +62,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg () => { var img = Image.Load(bytes, decoder); + img.Dispose(); }, // ReSharper disable once ExplicitCallerInfoArgument $"Decode {fileName}"); } // Benchmark, enable manually! - // [Theory] - // [InlineData(1, 75, JpegSubsample.Ratio420)] - // [InlineData(30, 75, JpegSubsample.Ratio420)] - // [InlineData(30, 75, JpegSubsample.Ratio444)] - // [InlineData(30, 100, JpegSubsample.Ratio444)] + [Theory(Skip = ProfilingSetup.SkipProfilingTests)] + [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) { // do not run this on CI even by accident @@ -82,31 +83,32 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } string[] testFiles = TestImages.Bmp.All - .Concat(new[] { TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk }) - .ToArray(); + .Concat(new[] { TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk }).ToArray(); - Image[] testImages = - testFiles.Select( - tf => TestImageProvider.File(tf, pixelTypeOverride: PixelTypes.Rgba32).GetImage()) - .ToArray(); + Image[] testImages = testFiles.Select( + tf => TestImageProvider.File(tf, pixelTypeOverride: PixelTypes.Rgba32).GetImage()).ToArray(); using (var ms = new MemoryStream()) { - this.Measure(executionCount, + this.Measure( + executionCount, () => - { - foreach (Image img in testImages) { - var options = new JpegEncoder { Quality = quality, Subsample = subsample }; - img.Save(ms, options); - ms.Seek(0, SeekOrigin.Begin); - } - }, + foreach (Image img in testImages) + { + var options = new JpegEncoder { Quality = quality, Subsample = subsample }; + img.Save(ms, options); + ms.Seek(0, SeekOrigin.Begin); + } + }, // ReSharper disable once ExplicitCallerInfoArgument - $@"Encode {testFiles.Length} images" - ); + $@"Encode {testFiles.Length} images"); } - } + foreach (Image image in testImages) + { + image.Dispose(); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveProfilingBenchmarks.cs b/tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveProfilingBenchmarks.cs new file mode 100644 index 0000000000..95fe4e48f1 --- /dev/null +++ b/tests/ImageSharp.Tests/ProfilingBenchmarks/LoadResizeSaveProfilingBenchmarks.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; + +using SixLabors.ImageSharp.Processing; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks +{ + public class LoadResizeSaveProfilingBenchmarks : MeasureFixture + { + public LoadResizeSaveProfilingBenchmarks(ITestOutputHelper output) + : base(output) + { + } + + [Theory(Skip = ProfilingSetup.SkipProfilingTests)] + [InlineData(TestImages.Jpeg.Baseline.Jpeg420Exif)] + public void LoadResizeSave(string imagePath) + { + var configuration = Configuration.CreateDefaultInstance(); + configuration.MaxDegreeOfParallelism = 1; + + byte[] imageBytes = TestFile.Create(imagePath).Bytes; + + using (var ms = new MemoryStream()) + { + this.Measure(30, + () => + { + using (var image = Image.Load(configuration, imageBytes)) + { + image.Mutate(x => x.Resize(image.Size() / 4)); + image.SaveAsJpeg(ms); + } + ms.Seek(0, SeekOrigin.Begin); + }); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ProfilingBenchmarks/ProfilingSetup.cs b/tests/ImageSharp.Tests/ProfilingBenchmarks/ProfilingSetup.cs new file mode 100644 index 0000000000..f9a68d4e7c --- /dev/null +++ b/tests/ImageSharp.Tests/ProfilingBenchmarks/ProfilingSetup.cs @@ -0,0 +1,18 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +// Uncomment to enable local profiling benchmarks. DO NOT PUSH TO MAIN! +// #define PROFILING + +namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks +{ + public static class ProfilingSetup + { + public const string SkipProfilingTests = +#if PROFILING + null; +#else + "Profiling benchmark, enable manually!"; +#endif + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ProfilingBenchmarks/ResizeProfilingBenchmarks.cs b/tests/ImageSharp.Tests/ProfilingBenchmarks/ResizeProfilingBenchmarks.cs new file mode 100644 index 0000000000..8b93559381 --- /dev/null +++ b/tests/ImageSharp.Tests/ProfilingBenchmarks/ResizeProfilingBenchmarks.cs @@ -0,0 +1,40 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.ProfilingBenchmarks +{ + public class ResizeProfilingBenchmarks : MeasureFixture + { + private readonly Configuration configuration = Configuration.CreateDefaultInstance(); + + public ResizeProfilingBenchmarks(ITestOutputHelper output) + : base(output) + { + this.configuration.MaxDegreeOfParallelism = 1; + } + + public int ExecutionCount { get; set; } = 50; + + [Theory(Skip = ProfilingSetup.SkipProfilingTests)] + [InlineData(100, 100)] + [InlineData(2000, 2000)] + public void ResizeBicubic(int width, int height) + { + this.Measure(this.ExecutionCount, + () => + { + using (var image = new Image(this.configuration, width, height)) + { + image.Mutate(x => x.Resize(width / 5, height / 5)); + } + }); + } + + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs index c2b1c26c54..a0d7869e39 100644 --- a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs +++ b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs @@ -10,48 +10,33 @@ namespace SixLabors.ImageSharp.Tests { public class QuantizedImageTests { + private Configuration Configuration => Configuration.Default; + [Fact] public void QuantizersDitherByDefault() { - var palette = new PaletteQuantizer(); + var werner = new WernerPaletteQuantizer(); + var websafe = new WebSafePaletteQuantizer(); var octree = new OctreeQuantizer(); var wu = new WuQuantizer(); - Assert.NotNull(palette.Diffuser); + Assert.NotNull(werner.Diffuser); + Assert.NotNull(websafe.Diffuser); Assert.NotNull(octree.Diffuser); Assert.NotNull(wu.Diffuser); - Assert.True(palette.CreateFrameQuantizer().Dither); - Assert.True(octree.CreateFrameQuantizer().Dither); - Assert.True(wu.CreateFrameQuantizer().Dither); - } - - [Theory] - [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, true)] - [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, false)] - public void PaletteQuantizerYieldsCorrectTransparentPixel(TestImageProvider provider, bool dither) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - Assert.True(image[0, 0].Equals(default(TPixel))); - - var quantizer = new PaletteQuantizer(dither); - - foreach (ImageFrame frame in image.Frames) - { - QuantizedFrame quantized = quantizer.CreateFrameQuantizer().QuantizeFrame(frame); - - int index = this.GetTransparentIndex(quantized); - Assert.Equal(index, quantized.GetPixelSpan()[0]); - } - } + Assert.True(werner.CreateFrameQuantizer(this.Configuration).Dither); + Assert.True(websafe.CreateFrameQuantizer(this.Configuration).Dither); + Assert.True(octree.CreateFrameQuantizer(this.Configuration).Dither); + Assert.True(wu.CreateFrameQuantizer(this.Configuration).Dither); } [Theory] [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, true)] [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32, false)] - public void OctreeQuantizerYieldsCorrectTransparentPixel(TestImageProvider provider, bool dither) + public void OctreeQuantizerYieldsCorrectTransparentPixel( + TestImageProvider provider, + bool dither) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) @@ -62,7 +47,8 @@ namespace SixLabors.ImageSharp.Tests foreach (ImageFrame frame in image.Frames) { - QuantizedFrame quantized = quantizer.CreateFrameQuantizer().QuantizeFrame(frame); + QuantizedFrame quantized = + quantizer.CreateFrameQuantizer(this.Configuration).QuantizeFrame(frame); int index = this.GetTransparentIndex(quantized); Assert.Equal(index, quantized.GetPixelSpan()[0]); @@ -84,7 +70,8 @@ namespace SixLabors.ImageSharp.Tests foreach (ImageFrame frame in image.Frames) { - QuantizedFrame quantized = quantizer.CreateFrameQuantizer().QuantizeFrame(frame); + QuantizedFrame quantized = + quantizer.CreateFrameQuantizer(this.Configuration).QuantizeFrame(frame); int index = this.GetTransparentIndex(quantized); Assert.Equal(index, quantized.GetPixelSpan()[0]); @@ -97,12 +84,12 @@ namespace SixLabors.ImageSharp.Tests { // Transparent pixels are much more likely to be found at the end of a palette int index = -1; - var trans = default(Rgba32); + Rgba32 trans = default; for (int i = quantized.Palette.Length - 1; i >= 0; i--) { quantized.Palette[i].ToRgba32(ref trans); - if (trans.Equals(default(Rgba32))) + if (trans.Equals(default)) { index = i; } diff --git a/tests/ImageSharp.Tests/RunExtendedTests.cmd b/tests/ImageSharp.Tests/RunExtendedTests.cmd index 481e5fb3d8..c2f4b9f537 100644 --- a/tests/ImageSharp.Tests/RunExtendedTests.cmd +++ b/tests/ImageSharp.Tests/RunExtendedTests.cmd @@ -5,3 +5,5 @@ dotnet xunit -nobuild -c Release -f net47 dotnet xunit -nobuild -c Release -f net47 -x86 dotnet xunit -nobuild -c Release -f net471 dotnet xunit -nobuild -c Release -f net471 -x86 +dotnet xunit -nobuild -c Release -f net472 +dotnet xunit -nobuild -c Release -f net472 -x86 diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs index 35ffa2bbb6..b037a7a9af 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataProfiles.cs @@ -14,20 +14,12 @@ namespace SixLabors.ImageSharp.Tests public static readonly byte[] Header_Random_Id_Array = { -#if !NETSTANDARD1_1 - 0x84, 0xA8, 0xD4, 0x60, 0xC7, 0x16, 0xB6, 0xF3, 0x9B, 0x0E, 0x4C, 0x3D, 0xAB, 0x95, 0xF8, 0x38, -#else - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -#endif + 0x84, 0xA8, 0xD4, 0x60, 0xC7, 0x16, 0xB6, 0xF3, 0x9B, 0x0E, 0x4C, 0x3D, 0xAB, 0x95, 0xF8, 0x38, }; public static readonly byte[] Profile_Random_Id_Array = { -#if !NETSTANDARD1_1 - 0x91, 0x7D, 0x6D, 0xE6, 0x84, 0xC9, 0x58, 0xD1, 0x3B, 0xB0, 0xF5, 0xBB, 0xAD, 0xD1, 0x13, 0x4F, -#else - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -#endif + 0x91, 0x7D, 0x6D, 0xE6, 0x84, 0xC9, 0x58, 0xD1, 0x3B, 0xB0, 0xF5, 0xBB, 0xAD, 0xD1, 0x13, 0x4F, }; public static readonly IccProfileHeader Header_Random_Write = CreateHeaderRandomValue( @@ -35,13 +27,7 @@ namespace SixLabors.ImageSharp.Tests new IccProfileId(1, 2, 3, 4), // should be overwritten "ijkl"); // should be overwritten to "acsp" - public static readonly IccProfileHeader Header_Random_Read = CreateHeaderRandomValue(132, -#if !NETSTANDARD1_1 - Header_Random_Id_Value, -#else - IccProfileId.Zero, -#endif - "acsp"); + public static readonly IccProfileHeader Header_Random_Read = CreateHeaderRandomValue(132, Header_Random_Id_Value, "acsp"); public static readonly byte[] Header_Random_Array = CreateHeaderRandomArray(132, 0, Header_Random_Id_Array); @@ -120,11 +106,7 @@ namespace SixLabors.ImageSharp.Tests ); public static readonly IccProfile Profile_Random_Val = new IccProfile(CreateHeaderRandomValue(168, -#if !NETSTANDARD1_1 Profile_Random_Id_Value, -#else - IccProfileId.Zero, -#endif "acsp"), new IccTagDataEntry[] { @@ -239,4 +221,4 @@ namespace SixLabors.ImageSharp.Tests new object[] { Header_Random_Array, true }, }; } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index acfad042eb..1144a3f7c0 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -45,6 +45,10 @@ namespace SixLabors.ImageSharp.Tests public const string Kaboom = "Png/kaboom.png"; public const string PDSrc = "Png/pd-source.png"; public const string PDDest = "Png/pd-dest.png"; + public const string Gray1BitTrans = "Png/gray-1-trns.png"; + public const string Gray2BitTrans = "Png/gray-2-tRNS.png"; + public const string Gray4BitTrans = "Png/gray-4-tRNS.png"; + public const string Gray8BitTrans = "Png/gray-8-tRNS.png"; // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html public const string Filter0 = "Png/filter0.png"; @@ -155,9 +159,27 @@ namespace SixLabors.ImageSharp.Tests public const string ExifDecodeOutOfRange694 = "Jpg/issues/Issue694-Decode-Exif-OutOfRange.jpg"; public const string InvalidEOI695 = "Jpg/issues/Issue695-Invalid-EOI.jpg"; public const string ExifResizeOutOfRange696 = "Jpg/issues/Issue696-Resize-Exif-OutOfRange.jpg"; + public const string InvalidAPP0721 = "Jpg/issues/Issue721-InvalidAPP0.jpg"; + public const string OrderedInterleavedProgressive723A = "Jpg/issues/Issue723-Ordered-Interleaved-Progressive-A.jpg"; + public const string OrderedInterleavedProgressive723B = "Jpg/issues/Issue723-Ordered-Interleaved-Progressive-B.jpg"; + public const string OrderedInterleavedProgressive723C = "Jpg/issues/Issue723-Ordered-Interleaved-Progressive-C.jpg"; + public const string ExifGetString750Transform = "Jpg/issues/issue750-exif-tranform.jpg"; + public const string ExifGetString750Load = "Jpg/issues/issue750-exif-load.jpg"; } public static readonly string[] All = Baseline.All.Concat(Progressive.All).ToArray(); + + public static class BenchmarkSuite + { + public const string Jpeg400_SmallMonochrome = Baseline.Jpeg400; + public const string Jpeg420Exif_MidSizeYCbCr = Baseline.Jpeg420Exif; + public const string Lake_Small444YCbCr = Baseline.Lake; + + // A few large images from the "issues" set are actually very useful for benchmarking: + public const string MissingFF00ProgressiveBedroom159_MidSize420YCbCr = Issues.MissingFF00ProgressiveBedroom159; + public const string BadRstProgressive518_Large444YCbCr = Issues.BadRstProgressive518; + public const string ExifGetString750Transform_Huge420YCbCr = Issues.ExifGetString750Transform; + } } public static class Bmp diff --git a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs index 102a629be9..47ca6cccb2 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ApproximateFloatComparer.cs @@ -1,96 +1,50 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.Collections.Generic; using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation.RgbColorSapce; namespace SixLabors.ImageSharp.Tests { - internal struct ApproximateFloatComparer : + /// + /// Allows the approximate comparison of single precision floating point values. + /// + internal readonly struct ApproximateFloatComparer : IEqualityComparer, IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer + IEqualityComparer { - private readonly float Eps; + private readonly float Epsilon; - public ApproximateFloatComparer(float eps = 1f) - { - this.Eps = eps; - } + /// + /// Initializes a new instance of the class. + /// + /// The comparison error difference epsilon to use. + public ApproximateFloatComparer(float epsilon = 1f) => this.Epsilon = epsilon; + /// public bool Equals(float x, float y) { float d = x - y; - return d >= -this.Eps && d <= this.Eps; + return d >= -this.Epsilon && d <= this.Epsilon; } - public int GetHashCode(float obj) - { - throw new InvalidOperationException(); - } + /// + public int GetHashCode(float obj) => obj.GetHashCode(); - public bool Equals(Vector4 a, Vector4 b) - { - return this.Equals(a.X, b.X) && this.Equals(a.Y, b.Y) && this.Equals(a.Z, b.Z) && this.Equals(a.W, b.W); - } + /// + public bool Equals(Vector4 a, Vector4 b) => this.Equals(a.X, b.X) && this.Equals(a.Y, b.Y) && this.Equals(a.Z, b.Z) && this.Equals(a.W, b.W); - public int GetHashCode(Vector4 obj) - { - throw new InvalidOperationException(); - } + /// + public int GetHashCode(Vector4 obj) => obj.GetHashCode(); - public bool Equals(CieXyChromaticityCoordinates x, CieXyChromaticityCoordinates y) - { - return this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y); - } - - public int GetHashCode(CieXyChromaticityCoordinates obj) - { - throw new NotImplementedException(); - } - - public bool Equals(RgbPrimariesChromaticityCoordinates x, RgbPrimariesChromaticityCoordinates y) - { - return this.Equals(x.R, y.R) && this.Equals(x.G, y.G) && this.Equals(x.B, y.B); - } - - public int GetHashCode(RgbPrimariesChromaticityCoordinates obj) - { - throw new NotImplementedException(); - } - - public bool Equals(CieXyz x, CieXyz y) - { - return this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y) && this.Equals(x.Z, y.Z); - } - - public int GetHashCode(CieXyz obj) - { - throw new NotImplementedException(); - } - - public bool Equals(RgbWorkingSpace x, RgbWorkingSpace y) - { - if (x is RgbWorkingSpace g1 && y is RgbWorkingSpace g2) - { - return this.Equals(g1.WhitePoint, g2.WhitePoint) - && this.Equals(g1.ChromaticityCoordinates, g2.ChromaticityCoordinates); - } - - return this.Equals(x.WhitePoint, y.WhitePoint) - && this.Equals(x.ChromaticityCoordinates, y.ChromaticityCoordinates); - } + /// + public bool Equals(Vector2 a, Vector2 b) => this.Equals(a.X, b.X) && this.Equals(a.Y, b.Y); - public int GetHashCode(RgbWorkingSpace obj) + public int GetHashCode(Vector2 obj) { - throw new NotImplementedException(); + throw new System.NotImplementedException(); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/FloatRoundingComparer.cs b/tests/ImageSharp.Tests/TestUtilities/FloatRoundingComparer.cs deleted file mode 100644 index 27c675823f..0000000000 --- a/tests/ImageSharp.Tests/TestUtilities/FloatRoundingComparer.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Collections.Generic; -using System.Numerics; - -namespace SixLabors.ImageSharp.Tests -{ - /// - /// Allows the comparison of single-precision floating point values by precision. - /// - public struct FloatRoundingComparer : IEqualityComparer, IEqualityComparer - { - /// - /// Initializes a new instance of the struct. - /// - /// The number of decimal places (valid values: 0-7) - public FloatRoundingComparer(int precision) - { - Guard.MustBeBetweenOrEqualTo(precision, 0, 7, nameof(precision)); - this.Precision = precision; - } - - /// - /// Gets the number of decimal places (valid values: 0-7) - /// - public int Precision { get; } - - /// - public bool Equals(float x, float y) - { - float xp = (float)Math.Round(x, this.Precision, MidpointRounding.AwayFromZero); - float yp = (float)Math.Round(y, this.Precision, MidpointRounding.AwayFromZero); - - // ReSharper disable once CompareOfFloatsByEqualityOperator - return xp == yp; - } - - /// - public bool Equals(Vector4 x, Vector4 y) - { - return this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y) && this.Equals(x.Z, y.Z) && this.Equals(x.W, y.W); - } - - /// - public int GetHashCode(float obj) - { - unchecked - { - int hashCode = obj.GetHashCode(); - hashCode = (hashCode * 397) ^ this.Precision.GetHashCode(); - return hashCode; - } - } - - /// - public int GetHashCode(Vector4 obj) - { - unchecked - { - int hashCode = obj.GetHashCode(); - hashCode = (hashCode * 397) ^ this.Precision.GetHashCode(); - return hashCode; - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs index 8dca11caeb..462782ba5e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs @@ -28,14 +28,15 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison var bBuffer = new Rgba64[width]; var differences = new List(); + Configuration configuration = expected.Configuration; for (int y = 0; y < actual.Height; y++) { Span aSpan = expected.GetPixelRowSpan(y); Span bSpan = actual.GetPixelRowSpan(y); - PixelOperations.Instance.ToRgba64(aSpan, aBuffer, width); - PixelOperations.Instance.ToRgba64(bSpan, bBuffer, width); + PixelOperations.Instance.ToRgba64(configuration, aSpan, aBuffer); + PixelOperations.Instance.ToRgba64(configuration, bSpan, bBuffer); for (int x = 0; x < width; x++) { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs index 674603380f..be12f56211 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs @@ -74,14 +74,15 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison float totalDifference = 0F; var differences = new List(); + Configuration configuration = expected.Configuration; for (int y = 0; y < actual.Height; y++) { Span aSpan = expected.GetPixelRowSpan(y); Span bSpan = actual.GetPixelRowSpan(y); - PixelOperations.Instance.ToRgba64(aSpan, aBuffer, width); - PixelOperations.Instance.ToRgba64(bSpan, bBuffer, width); + PixelOperations.Instance.ToRgba64(configuration, aSpan, aBuffer); + PixelOperations.Instance.ToRgba64(configuration, bSpan, bBuffer); for (int x = 0; x < width; x++) { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs index 97ed30b997..d68c37a768 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests { Image image = base.GetImage(); TPixel color = default(TPixel); - color.PackFromRgba32(new Rgba32(this.r, this.g, this.b, this.a)); + color.FromRgba32(new Rgba32(this.r, this.g, this.b, this.a)); image.Mutate(x => x.Fill(color)); return image; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 30ac0856c7..5b5e4740a3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Tests public virtual string SourceFileOrDescription => ""; - public Configuration Configuration { get; set; } = Configuration.Default.Clone(); + public Configuration Configuration { get; set; } = Configuration.CreateDefaultInstance(); /// /// Utility instance to provide informations about the test image & manage input/output diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index 71ae60fabc..cc09dc0573 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -163,20 +163,20 @@ namespace SixLabors.ImageSharp.Tests { blue.W = red.W = green.W = (float)x / (float)right; - c.PackFromVector4(red); + c.FromVector4(red); int topBand = top; for (int y = topBand; y < top + height; y++) { pixels[x, y] = c; } topBand = topBand + height; - c.PackFromVector4(green); + c.FromVector4(green); for (int y = topBand; y < topBand + height; y++) { pixels[x, y] = c; } topBand = topBand + height; - c.PackFromVector4(blue); + c.FromVector4(blue); for (int y = topBand; y < bottom; y++) { pixels[x, y] = c; @@ -207,7 +207,7 @@ namespace SixLabors.ImageSharp.Tests t.PackedValue += stepsPerPixel; Vector4 v = t.ToVector4(); //v.W = (x - left) / (float)left; - c.PackFromVector4(v); + c.FromVector4(v); pixels[x, y] = c; } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index 65b32e0880..c91ef56a1a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -72,7 +72,10 @@ namespace SixLabors.ImageSharp.Tests extension = '.' + extension; } - if (fn != string.Empty) fn = '_' + fn; + if (fn != string.Empty) + { + fn = '_' + fn; + } string pixName = ""; @@ -274,17 +277,14 @@ namespace SixLabors.ImageSharp.Tests } public static void ModifyPixel(Image img, int x, int y, byte perChannelChange) - where TPixel : struct, IPixel - { - ModifyPixel(img.Frames.RootFrame, x, y, perChannelChange); - } + where TPixel : struct, IPixel => ModifyPixel(img.Frames.RootFrame, x, y, perChannelChange); public static void ModifyPixel(ImageFrame img, int x, int y, byte perChannelChange) where TPixel : struct, IPixel { TPixel pixel = img[x, y]; Rgba64 rgbaPixel = default; - pixel.ToRgba64(ref rgbaPixel); + rgbaPixel.FromScaledVector4(pixel.ToScaledVector4()); ushort change = (ushort)Math.Round((perChannelChange / 255F) * 65535F); if (rgbaPixel.R + perChannelChange <= 255) @@ -323,7 +323,7 @@ namespace SixLabors.ImageSharp.Tests rgbaPixel.A -= perChannelChange; } - pixel.PackFromRgba64(rgbaPixel); + pixel.FromRgba64(rgbaPixel); img[x, y] = pixel; } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index 8cfc2472f5..3dd330e4d3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -29,16 +29,24 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { if (magickImage.Depth == 8) { - byte[] data = pixels.ToByteArray("RGBA"); - - PixelOperations.Instance.PackFromRgba32Bytes(data, resultPixels, resultPixels.Length); + byte[] data = pixels.ToByteArray(PixelMapping.RGBA); + + PixelOperations.Instance.FromRgba32Bytes( + configuration, + data, + resultPixels, + resultPixels.Length); } else if (magickImage.Depth == 16) { - ushort[] data = pixels.ToShortArray("RGBA"); + ushort[] data = pixels.ToShortArray(PixelMapping.RGBA); Span bytes = MemoryMarshal.Cast(data.AsSpan()); - PixelOperations.Instance.PackFromRgba64Bytes(bytes, resultPixels, resultPixels.Length); + PixelOperations.Instance.FromRgba64Bytes( + configuration, + bytes, + resultPixels, + resultPixels.Length); } else { diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs index d06f5630f4..7e87c23db5 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingBridge.cs @@ -33,7 +33,9 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs if (bmp.PixelFormat != PixelFormat.Format32bppArgb) { - throw new ArgumentException($"{nameof(From32bppArgbSystemDrawingBitmap)} : pixel format should be {PixelFormat.Format32bppArgb}!", nameof(bmp)); + throw new ArgumentException( + $"{nameof(From32bppArgbSystemDrawingBitmap)} : pixel format should be {PixelFormat.Format32bppArgb}!", + nameof(bmp)); } BitmapData data = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat); @@ -43,6 +45,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs long destRowByteCount = w * sizeof(Bgra32); var image = new Image(w, h); + Configuration configuration = image.GetConfiguration(); using (IMemoryOwner workBuffer = Configuration.Default.MemoryAllocator.Allocate(w)) { @@ -55,7 +58,10 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs byte* sourcePtr = sourcePtrBase + (data.Stride * y); Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); - PixelOperations.Instance.PackFromBgra32(workBuffer.GetSpan(), row, row.Length); + PixelOperations.Instance.FromBgra32( + configuration, + workBuffer.GetSpan().Slice(0, w), + row); } } } @@ -79,7 +85,9 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs if (bmp.PixelFormat != PixelFormat.Format24bppRgb) { - throw new ArgumentException($"{nameof(From24bppRgbSystemDrawingBitmap)}: pixel format should be {PixelFormat.Format24bppRgb}!", nameof(bmp)); + throw new ArgumentException( + $"{nameof(From24bppRgbSystemDrawingBitmap)}: pixel format should be {PixelFormat.Format24bppRgb}!", + nameof(bmp)); } BitmapData data = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat); @@ -89,6 +97,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs long destRowByteCount = w * sizeof(Bgr24); var image = new Image(w, h); + Configuration configuration = image.GetConfiguration(); using (IMemoryOwner workBuffer = Configuration.Default.MemoryAllocator.Allocate(w)) { @@ -101,7 +110,10 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs byte* sourcePtr = sourcePtrBase + (data.Stride * y); Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); - PixelOperations.Instance.PackFromBgr24(workBuffer.GetSpan(), row, row.Length); + PixelOperations.Instance.FromBgr24( + configuration, + workBuffer.GetSpan().Slice(0, w), + row); } } } @@ -112,6 +124,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs internal static unsafe Bitmap To32bppArgbSystemDrawingBitmap(Image image) where TPixel : struct, IPixel { + Configuration configuration = image.GetConfiguration(); int w = image.Width; int h = image.Height; @@ -130,7 +143,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs for (int y = 0; y < h; y++) { Span row = image.Frames.RootFrame.GetPixelRowSpan(y); - PixelOperations.Instance.ToBgra32(row, workBuffer.GetSpan(), row.Length); + PixelOperations.Instance.ToBgra32(configuration, row, workBuffer.GetSpan()); byte* destPtr = destPtrBase + (data.Stride * y); Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); diff --git a/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs b/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs index 9eb051e7a7..e3d8bf3806 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs @@ -1,32 +1,104 @@ -using System; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; namespace SixLabors.ImageSharp.Tests { + /// + /// Helper methods that allow the creation of random test data. + /// internal static class TestDataGenerator { + /// + /// Creates an of the given length consisting of random values between the two ranges. + /// + /// The pseudo-random number generator. + /// The length. + /// The minimum value. + /// The maximum value. + /// The . public static float[] GenerateRandomFloatArray(this Random rnd, int length, float minVal, float maxVal) { float[] values = new float[length]; for (int i = 0; i < length; i++) { - values[i] = (float)rnd.NextDouble() * (maxVal - minVal) + minVal; + values[i] = GetRandomFloat(rnd, minVal, maxVal); } return values; } - public static float[] GenerateRandomRoundedFloatArray(this Random rnd, int length, int minVal, int maxValExclusive) + /// + /// Creates an of the given length consisting of random values between the two ranges. + /// + /// The pseudo-random number generator. + /// The length. + /// The minimum value. + /// The maximum value. + /// The . + public static Vector4[] GenerateRandomVectorArray(this Random rnd, int length, float minVal, float maxVal) + { + var values = new Vector4[length]; + + for (int i = 0; i < length; i++) + { + ref Vector4 v = ref values[i]; + v.X = GetRandomFloat(rnd, minVal, maxVal); + v.Y = GetRandomFloat(rnd, minVal, maxVal); + v.Z = GetRandomFloat(rnd, minVal, maxVal); + v.W = GetRandomFloat(rnd, minVal, maxVal); + } + + return values; + } + + /// + /// Creates an of the given length consisting of rounded random values between the two ranges. + /// + /// The pseudo-random number generator. + /// The length. + /// The minimum value. + /// The maximum value. + /// The . + public static float[] GenerateRandomRoundedFloatArray(this Random rnd, int length, float minVal, float maxVal) { float[] values = new float[length]; for (int i = 0; i < length; i++) { - int val = rnd.Next(minVal, maxValExclusive); - values[i] = (float)val; + values[i] = (float)Math.Round(rnd.GetRandomFloat(minVal, maxVal)); + } + + return values; + } + + /// + /// Creates an of the given length consisting of random values. + /// + /// The pseudo-random number generator. + /// The length. + /// The . + public static byte[] GenerateRandomByteArray(this Random rnd, int length) + { + byte[] values = new byte[length]; + rnd.NextBytes(values); + return values; + } + + public static short[] GenerateRandomInt16Array(this Random rnd, int length, short minVal, short maxVal) + { + short[] values = new short[length]; + for (int i = 0; i < values.Length; i++) + { + values[i] = (short)rnd.Next(minVal, maxVal); } return values; } + + private static float GetRandomFloat(this Random rnd, float minVal, float maxVal) => ((float)rnd.NextDouble() * (maxVal - minVal)) + minVal; } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index a935873670..29d39596b7 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -30,27 +30,29 @@ namespace SixLabors.ImageSharp.Tests { MemoryAllocator memoryAllocator = ctx.MemoryAllocator; - ctx.Apply(img => - { - using (Buffer2D temp = memoryAllocator.Allocate2D(img.Width, img.Height)) - { - Span tempSpan = temp.GetSpan(); - foreach (ImageFrame frame in img.Frames) + ctx.Apply( + img => { - Span pixelSpan = frame.GetPixelSpan(); + Configuration configuration = img.GetConfiguration(); + using (Buffer2D temp = memoryAllocator.Allocate2D(img.Width, img.Height)) + { + Span tempSpan = temp.GetSpan(); + foreach (ImageFrame frame in img.Frames) + { + Span pixelSpan = frame.GetPixelSpan(); - PixelOperations.Instance.ToScaledVector4(pixelSpan, tempSpan, pixelSpan.Length); + PixelOperations.Instance.ToScaledVector4(configuration, pixelSpan, tempSpan); - for (int i = 0; i < tempSpan.Length; i++) - { - ref Vector4 v = ref tempSpan[i]; - v.W = 1F; - } + for (int i = 0; i < tempSpan.Length; i++) + { + ref Vector4 v = ref tempSpan[i]; + v.W = 1F; + } - PixelOperations.Instance.PackFromScaledVector4(tempSpan, pixelSpan, pixelSpan.Length); - } - } - }); + PixelOperations.Instance.FromScaledVector4(configuration, tempSpan, pixelSpan); + } + } + }); } public static Image DebugSave( @@ -441,14 +443,23 @@ namespace SixLabors.ImageSharp.Tests { Span actualPixels = image.GetPixelSpan(); - Assert.True(expectedPixels.Length == actualPixels.Length, "Buffer sizes are not equal!"); + CompareBuffers(expectedPixels, actualPixels); - for (int i = 0; i < expectedPixels.Length; i++) + return image; + } + + public static void CompareBuffers(Span expected, Span actual) + where T : struct, IEquatable + { + Assert.True(expected.Length == actual.Length, "Buffer sizes are not equal!"); + + for (int i = 0; i < expected.Length; i++) { - Assert.True(expectedPixels[i].Equals(actualPixels[i]), $"Pixels are different on position {i}!"); - } + T x = expected[i]; + T a = actual[i]; - return image; + Assert.True(x.Equals(a), $"Buffers differ at position {i}! Expected: {x} | Actual: {a}"); + } } /// @@ -669,7 +680,7 @@ namespace SixLabors.ImageSharp.Tests { float value = bufferSpan[i] * scale; var v = new Vector4(value, value, value, 1f); - pixels[i].PackFromVector4(v); + pixels[i].FromVector4(v); } return image; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs index 7ce892edb3..e998ccd3dc 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestPixel.cs @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities public TPixel AsPixel() { TPixel pix = default(TPixel); - pix.PackFromVector4(new System.Numerics.Vector4(this.Red, this.Green, this.Blue, this.Alpha)); + pix.FromVector4(new System.Numerics.Vector4(this.Red, this.Green, this.Blue, this.Alpha)); return pix; } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs index 5a14f2e26e..5ea0bccf06 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs @@ -10,6 +10,7 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.Primitives; @@ -80,10 +81,13 @@ namespace SixLabors.ImageSharp.Tests } else { - ca.ToRgb24(ref rgb1); - cb.ToRgb24(ref rgb2); + Rgba32 rgba = default; + ca.ToRgba32(ref rgba); + rgb1 = rgba.Rgb; + cb.ToRgba32(ref rgba); + rgb2 = rgba.Rgb; - if (rgb1.R != rgb2.R || rgb1.G != rgb2.G || rgb1.B != rgb2.B) + if (!rgb1.Equals(rgb2)) { return false; } @@ -94,10 +98,7 @@ namespace SixLabors.ImageSharp.Tests return true; } - public static string ToCsv(this IEnumerable items, string separator = ",") - { - return String.Join(separator, items.Select(o => String.Format(CultureInfo.InvariantCulture, "{0}", o))); - } + public static string ToCsv(this IEnumerable items, string separator = ",") => string.Join(separator, items.Select(o => string.Format(CultureInfo.InvariantCulture, "{0}", o))); public static Type GetClrType(this PixelTypes pixelType) => PixelTypes2ClrTypes[pixelType]; @@ -141,10 +142,7 @@ namespace SixLabors.ImageSharp.Tests internal static PixelTypes[] GetAllPixelTypes() => (PixelTypes[])Enum.GetValues(typeof(PixelTypes)); internal static TPixel GetPixelOfNamedColor(string colorName) - where TPixel : struct, IPixel - { - return (TPixel)typeof(NamedColors).GetTypeInfo().GetField(colorName).GetValue(null); - } + where TPixel : struct, IPixel => (TPixel)typeof(NamedColors).GetTypeInfo().GetField(colorName).GetValue(null); /// /// Utility for testing image processor extension methods: @@ -287,5 +285,17 @@ namespace SixLabors.ImageSharp.Tests } public static string AsInvariantString(this FormattableString formattable) => System.FormattableString.Invariant(formattable); + + public static IResampler GetResampler(string name) + { + PropertyInfo property = typeof(KnownResamplers).GetTypeInfo().GetProperty(name); + + if (property is null) + { + throw new Exception($"No resampler named '{name}"); + } + + return (IResampler)property.GetValue(null); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs index db651886f2..b60439b488 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs @@ -14,10 +14,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests public class MagickReferenceCodecTests { - public MagickReferenceCodecTests(ITestOutputHelper output) - { - this.Output = output; - } + public MagickReferenceCodecTests(ITestOutputHelper output) => this.Output = output; private ITestOutputHelper Output { get; } @@ -61,6 +58,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests [WithBlankImages(1, 1, PixelTypesToTest48, TestImages.Png.Rgb48Bpp)] [WithBlankImages(1, 1, PixelTypesToTest48, TestImages.Png.Rgb48BppInterlaced)] [WithBlankImages(1, 1, PixelTypesToTest48, TestImages.Png.Rgb48BppTrans)] + [WithBlankImages(1, 1, PixelTypesToTest48, TestImages.Png.Gray16Bit)] public void MagickDecode_16BitDepthImage_IsApproximatelyEquivalentTo_SystemDrawingResult(TestImageProvider dummyProvider, string testImage) where TPixel : struct, IPixel { @@ -71,7 +69,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests // 1020 == 4 * 255 (Equivalent to manhattan distance of 1+1+1+1=4 in Rgba32 space) var comparer = ImageComparer.TolerantPercentage(1, 1020); - + using (var mImage = Image.Load(path, magickDecoder)) using (var sdImage = Image.Load(path, sdDecoder)) { diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs index 8a3e69059f..30bb16c2a0 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs @@ -3,6 +3,8 @@ using System; using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats; diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 5305eb2ba3..a8140e39d4 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -2,36 +2,26 @@ // Licensed under the Apache License, Version 2.0. using System; -using SixLabors.ImageSharp.PixelFormats; -using Xunit; -using Xunit.Abstractions; - using System.Collections.Concurrent; using System.IO; - using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using Xunit; +using Xunit.Abstractions; namespace SixLabors.ImageSharp.Tests { - using SixLabors.Memory; - public class TestImageProviderTests { - public TestImageProviderTests(ITestOutputHelper output) - { - this.Output = output; - } + public TestImageProviderTests(ITestOutputHelper output) => this.Output = output; private ITestOutputHelper Output { get; } [Theory] [WithBlankImages(1, 1, PixelTypes.Rgba32)] public void NoOutputSubfolderIsPresentByDefault(TestImageProvider provider) - where TPixel : struct, IPixel - { - Assert.Empty(provider.Utility.OutputSubfolderName); - } + where TPixel : struct, IPixel => Assert.Empty(provider.Utility.OutputSubfolderName); [Theory] [WithBlankImages(42, 666, PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.HalfSingle, "hello")] @@ -64,10 +54,7 @@ namespace SixLabors.ImageSharp.Tests [WithBlankImages(1, 1, PixelTypes.Alpha8, PixelTypes.Alpha8)] [WithBlankImages(1, 1, PixelTypes.Argb32, PixelTypes.Argb32)] public void PixelType_PropertyValueIsCorrect(TestImageProvider provider, PixelTypes expected) - where TPixel : struct, IPixel - { - Assert.Equal(expected, provider.PixelType); - } + where TPixel : struct, IPixel => Assert.Equal(expected, provider.PixelType); [Theory] [WithFile(TestImages.Bmp.Car, PixelTypes.All, 88)] @@ -96,7 +83,7 @@ namespace SixLabors.ImageSharp.Tests // Couldn't make xUnit happy without this hackery: - private static ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); private string callerName = null; @@ -160,7 +147,7 @@ namespace SixLabors.ImageSharp.Tests return new Image(42, 42); } - private static ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary invocationCounts = new ConcurrentDictionary(); private string callerName = null; @@ -287,9 +274,8 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal(10, img.Width); Assert.Equal(20, img.Height); - var rgba = default(Rgba32); - Buffer2D pixels = img.GetRootFramePixelBuffer(); + Rgba32 rgba = default; for (int y = 0; y < pixels.Height; y++) { for (int x = 0; x < pixels.Width; x++) @@ -311,10 +297,7 @@ namespace SixLabors.ImageSharp.Tests /// /// public static Image CreateTestImage() - where TPixel : struct, IPixel - { - return new Image(3, 3); - } + where TPixel : struct, IPixel => new Image(3, 3); [Theory] [WithMemberFactory(nameof(CreateTestImage), PixelTypes.All)] diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs index 655f5b032c..301d0cebe6 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestUtilityExtensionsTests.cs @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Tests v /= 10; var color = default(TPixel); - color.PackFromVector4(v); + color.FromVector4(v); pixels[i, j] = color; } diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Calliphora.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Calliphora.jpg.txt new file mode 100644 index 0000000000..dc889ab105 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Calliphora.jpg.txt @@ -0,0 +1,331 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Calliphora.jpg] + Filesize: [254766] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 96 x 96 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: COM (Comment) (xFFFE) *** + OFFSET: 0x00000014 + Comment length = 80 + Comment=File source: http://commons.wikimedia.org/wiki/File:Calliphora_sp_Portrait.jpg + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000066 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 6 4 4 6 10 16 20 24 + DQT, Row #1: 5 5 6 8 10 23 24 22 + DQT, Row #2: 6 5 6 10 16 23 28 22 + DQT, Row #3: 6 7 9 12 20 35 32 25 + DQT, Row #4: 7 9 15 22 27 44 41 31 + DQT, Row #5: 10 14 22 26 32 42 45 37 + DQT, Row #6: 20 26 31 35 41 48 48 40 + DQT, Row #7: 29 37 38 39 45 40 41 40 + Approx quality factor = 79.94 (scaling=40.12 variance=1.43) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x000000AB + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 7 7 10 19 40 40 40 40 + DQT, Row #1: 7 8 10 26 40 40 40 40 + DQT, Row #2: 10 10 22 40 40 40 40 40 + DQT, Row #3: 19 26 40 40 40 40 40 40 + DQT, Row #4: 40 40 40 40 40 40 40 40 + DQT, Row #5: 40 40 40 40 40 40 40 40 + DQT, Row #6: 40 40 40 40 40 40 40 40 + DQT, Row #7: 40 40 40 40 40 40 40 40 + Approx quality factor = 79.87 (scaling=40.26 variance=0.36) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x000000F0 + Frame header length = 17 + Precision = 8 + Number of Lines = 1198 + Samples per Line = 804 + Image Size = 804 x 1198 + Raw Image Orientation = Portrait + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000103 + Huffman table length = 28 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 04 + Codes of length 03 bits (005 total): 00 01 02 03 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000121 + Huffman table length = 79 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 05 12 21 + Codes of length 06 bits (002 total): 06 31 + Codes of length 07 bits (004 total): 13 22 41 51 + Codes of length 08 bits (004 total): 14 32 61 71 + Codes of length 09 bits (003 total): 07 81 91 + Codes of length 10 bits (006 total): 15 23 42 52 A1 B1 + Codes of length 11 bits (003 total): 33 62 C1 + Codes of length 12 bits (007 total): 16 24 43 72 82 D1 F0 + Codes of length 13 bits (002 total): 25 E1 + Codes of length 14 bits (003 total): 34 53 92 + Codes of length 15 bits (002 total): A2 F1 + Codes of length 16 bits (015 total): 08 63 B2 26 44 C2 D2 73 27 35 55 74 84 93 A3 + Total number of codes: 060 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000172 + Huffman table length = 26 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 007 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000018E + Huffman table length = 59 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (002 total): 02 11 + Codes of length 04 bits (001 total): 21 + Codes of length 05 bits (002 total): 03 31 + Codes of length 06 bits (004 total): 12 41 51 F0 + Codes of length 07 bits (003 total): 04 61 71 + Codes of length 08 bits (008 total): 13 22 81 91 A1 B1 C1 D1 + Codes of length 09 bits (002 total): 32 E1 + Codes of length 10 bits (002 total): 42 F1 + Codes of length 11 bits (002 total): 05 23 + Codes of length 12 bits (002 total): 52 62 + Codes of length 13 bits (002 total): 14 33 + Codes of length 14 bits (001 total): 72 + Codes of length 15 bits (004 total): 24 82 92 A2 + Codes of length 16 bits (003 total): 43 B2 E2 + Total number of codes: 040 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000001CB + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x000001D9 + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x0003E32C.0 + + Compression stats: + Compression Ratio: 11.36:1 + Bits per pixel: 2.11:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 2628 ( 17%) + # codes of length 03 bits: 10491 ( 69%) + # codes of length 04 bits: 1319 ( 9%) + # codes of length 05 bits: 611 ( 4%) + # codes of length 06 bits: 101 ( 1%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 23843 ( 79%) + # codes of length 03 bits: 3770 ( 12%) + # codes of length 04 bits: 1945 ( 6%) + # codes of length 05 bits: 653 ( 2%) + # codes of length 06 bits: 89 ( 0%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 118632 ( 45%) + # codes of length 03 bits: 34447 ( 13%) + # codes of length 04 bits: 57131 ( 22%) + # codes of length 05 bits: 27139 ( 10%) + # codes of length 06 bits: 8648 ( 3%) + # codes of length 07 bits: 9574 ( 4%) + # codes of length 08 bits: 4195 ( 2%) + # codes of length 09 bits: 1503 ( 1%) + # codes of length 10 bits: 1711 ( 1%) + # codes of length 11 bits: 386 ( 0%) + # codes of length 12 bits: 470 ( 0%) + # codes of length 13 bits: 66 ( 0%) + # codes of length 14 bits: 62 ( 0%) + # codes of length 15 bits: 38 ( 0%) + # codes of length 16 bits: 58 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 58553 ( 58%) + # codes of length 03 bits: 21076 ( 21%) + # codes of length 04 bits: 4270 ( 4%) + # codes of length 05 bits: 6075 ( 6%) + # codes of length 06 bits: 6016 ( 6%) + # codes of length 07 bits: 2009 ( 2%) + # codes of length 08 bits: 2750 ( 3%) + # codes of length 09 bits: 429 ( 0%) + # codes of length 10 bits: 213 ( 0%) + # codes of length 11 bits: 91 ( 0%) + # codes of length 12 bits: 44 ( 0%) + # codes of length 13 bits: 22 ( 0%) + # codes of length 14 bits: 5 ( 0%) + # codes of length 15 bits: 9 ( 0%) + # codes of length 16 bits: 3 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[119] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 1008, 0, 0] RGB=[254,254,254] @ MCU[ 35, 37] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x0003E32C.0 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0003E32C + + +*** Searching Compression Signatures *** + + Signature: 01DC499064BA9264D591FDE9071DFD89 + Signature (Rotated): 0175BAF3251040E0EFB2930B73328E7F + File Offset: 0 bytes + Chroma subsampling: 1x1 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C2000Z ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C40Z,D40Z ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C700UZ ] [ ] No + CAM:[SONY ] [DSC-H9 ] [ ] No + SW :[Apple ImageIO.framework ] [050 (Normal) ] + SW :[IJG Library ] [080 ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [080 ] + SW :[IrfanView ] [080 ] + SW :[idImager ] [080 ] + SW :[FastStone Image Viewer ] [080 ] + SW :[NeatImage ] [080 ] + SW :[Paint.NET ] [080 ] + SW :[Photomatix ] [080 ] + SW :[XnView ] [080 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Floorplan.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Floorplan.jpg.txt new file mode 100644 index 0000000000..2c03157afe --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Floorplan.jpg.txt @@ -0,0 +1,266 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Floorplan.jpg] + Filesize: [161577] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 300 x 300 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000014 + Length = 13464 + Identifier = [Exif] + Identifier TIFF = 0x[4D4D002A 00000008] + Endian = Motorola (big) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000026 + Dir Length = 0x000A + [Model ] = "Photosmart Plus B209a-m" + [Orientation ] = 1 = Row 0: top, Col 0: left + [XResolution ] = 300/1 + [YResolution ] = 300/1 + [ResolutionUnit ] = Inch + [Software ] = "Windows Photo Editor 10.0.10011.16384" + [DateTime ] = "2016:01:02 20:17:37" + [ExifOffset ] = @ 0x091A + Offset to Next IFD = 0x000011B6 + + EXIF IFD1 @ Absolute 0x000011D4 + Dir Length = 0x0006 + [Compression ] = JPEG + [XResolution ] = 96/1 + [YResolution ] = 96/1 + [ResolutionUnit ] = Inch + [JpegIFOffset ] = @ +0x1214 = @ 0x1232 + [JpegIFByteCount ] = 0x[0000227C] / 8828 + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x00000938 + Dir Length = 0x0008 + [DateTimeOriginal ] = "2016:01:02 19:22:28" + [DateTimeDigitized ] = "2016:01:02 19:22:28" + [SubSecTimeOriginal ] = "00" + [SubSecTimeDigitized ] = "00" + [ColorSpace ] = sRGB + [ExifImageWidth ] = 0x[00000922] / 2338 + [ExifImageHeight ] = 0x[000008C9] / 2249 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x000034AE + Length = 12772 + Identifier = [http://ns.adobe.com/xap/1.0/] + XMP = + | + |Windows Photo Editor 10.0.10011.163842016-01-02T19:22:28 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00006694 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 3 2 2 3 5 8 10 12 + DQT, Row #1: 2 2 3 4 5 12 12 11 + DQT, Row #2: 3 3 3 5 8 11 14 11 + DQT, Row #3: 3 3 4 6 10 17 16 12 + DQT, Row #4: 4 4 7 11 14 22 21 15 + DQT, Row #5: 5 7 11 13 16 21 23 18 + DQT, Row #6: 10 13 16 17 21 24 24 20 + DQT, Row #7: 14 18 19 20 22 20 21 20 + Approx quality factor = 90.06 (scaling=19.88 variance=1.14) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x000066D9 + Frame header length = 11 + Precision = 8 + Number of Lines = 645 + Samples per Line = 976 + Image Size = 976 x 645 + Raw Image Orientation = Landscape + Number of Img components = 1 + Component[1]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000066E6 + Huffman table length = 31 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (005 total): 01 02 03 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (001 total): 0A + Codes of length 09 bits (001 total): 0B + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00006707 + Huffman table length = 181 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 05 12 21 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 06 13 51 61 + Codes of length 08 bits (003 total): 07 22 71 + Codes of length 09 bits (005 total): 14 32 81 91 A1 + Codes of length 10 bits (005 total): 08 23 42 B1 C1 + Codes of length 11 bits (004 total): 15 52 D1 F0 + Codes of length 12 bits (004 total): 24 33 62 72 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (001 total): 82 + Codes of length 16 bits (125 total): 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 + 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 + 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 + 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 + 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 + B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA + D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 + E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000067BE + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x000067C8 + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x00027727.0 + + Compression stats: + Compression Ratio: 4.66:1 + Bits per pixel: 1.72:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 3571 ( 36%) + # codes of length 03 bits: 4320 ( 44%) + # codes of length 04 bits: 925 ( 9%) + # codes of length 05 bits: 456 ( 5%) + # codes of length 06 bits: 313 ( 3%) + # codes of length 07 bits: 291 ( 3%) + # codes of length 08 bits: 6 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 78118 ( 44%) + # codes of length 03 bits: 22349 ( 13%) + # codes of length 04 bits: 35264 ( 20%) + # codes of length 05 bits: 18811 ( 11%) + # codes of length 06 bits: 4312 ( 2%) + # codes of length 07 bits: 8245 ( 5%) + # codes of length 08 bits: 4682 ( 3%) + # codes of length 09 bits: 1584 ( 1%) + # codes of length 10 bits: 1900 ( 1%) + # codes of length 11 bits: 324 ( 0%) + # codes of length 12 bits: 116 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 6 ( 0%) + # codes of length 16 bits: 639 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[231] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 1017, 0, 0] RGB=[255,255,255] @ MCU[ 7, 0] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x00027726.4 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x00027727 + + +*** Searching Compression Signatures *** + + Signature: 015C645021E37D3469A6B652789383DB + Signature (Rotated): 01D400C125EB43B05762A66347B271F7 + File Offset: 0 bytes + Chroma subsampling: Gray + EXIF Make/Model: OK [???] [Photosmart Plus B209a-m] + EXIF Makernotes: NONE + EXIF Software: OK [Windows Photo Editor 10.0.10011.16384] + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + SW :[IJG Library ] [090 Gray ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [090 Gray ] + SW :[IrfanView ] [090 Gray ] + SW :[idImager ] [090 Gray ] + SW :[FastStone Image Viewer ] [090 Gray ] + SW :[NeatImage ] [090 Gray ] + SW :[Paint.NET ] [090 Gray ] + SW :[Photomatix ] [090 Gray ] + SW :[XnView ] [090 Gray ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 2 - Image has high probability of being processed/edited + + diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Hiyamugi.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Hiyamugi.jpg.txt new file mode 100644 index 0000000000..8538e13c86 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Hiyamugi.jpg.txt @@ -0,0 +1,319 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Hiyamugi.jpg] + Filesize: [540458] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.0] + density = 96 x 96 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000014 + Length = 65110 + Identifier = [JFXX] + Not known APP0 type. Skipping remainder. + +*** Marker: COM (Comment) (xFFFE) *** + OFFSET: 0x0000FE6C + Comment length = 31 + Comment=LEAD Technologies Inc. V1.01. + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x0000FE8D + Table length = 132 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 2 2 2 2 2 2 2 2 + DQT, Row #1: 2 2 2 2 2 2 2 2 + DQT, Row #2: 2 2 2 2 2 2 2 2 + DQT, Row #3: 2 2 2 2 2 3 3 2 + DQT, Row #4: 2 2 2 2 2 4 4 3 + DQT, Row #5: 2 2 2 2 3 4 4 3 + DQT, Row #6: 2 2 3 3 4 4 4 4 + DQT, Row #7: 2 3 3 3 4 4 4 3 + Approx quality factor = 96.75 (scaling=6.50 variance=21.01) + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 2 2 2 2 3 3 3 3 + DQT, Row #1: 2 2 2 2 3 3 3 3 + DQT, Row #2: 2 2 2 3 3 3 3 3 + DQT, Row #3: 2 2 3 3 3 3 3 3 + DQT, Row #4: 3 3 3 3 3 3 3 3 + DQT, Row #5: 3 3 3 3 3 3 3 3 + DQT, Row #6: 3 3 3 3 3 3 3 3 + DQT, Row #7: 3 3 3 3 3 3 3 3 + Approx quality factor = 98.06 (scaling=3.88 variance=4.78) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000FF13 + Huffman table length = 418 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (005 total): 01 02 03 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (001 total): 0A + Codes of length 09 bits (001 total): 0B + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (001 total): 07 + Codes of length 08 bits (001 total): 08 + Codes of length 09 bits (001 total): 09 + Codes of length 10 bits (001 total): 0A + Codes of length 11 bits (001 total): 0B + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 05 12 21 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 06 13 51 61 + Codes of length 08 bits (003 total): 07 22 71 + Codes of length 09 bits (005 total): 14 32 81 91 A1 + Codes of length 10 bits (005 total): 08 23 42 B1 C1 + Codes of length 11 bits (004 total): 15 52 D1 F0 + Codes of length 12 bits (004 total): 24 33 62 72 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (001 total): 82 + Codes of length 16 bits (125 total): 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 + 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 + 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 + 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 + 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 + B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA + D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 + E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (002 total): 03 11 + Codes of length 05 bits (004 total): 04 05 21 31 + Codes of length 06 bits (004 total): 06 12 41 51 + Codes of length 07 bits (003 total): 07 61 71 + Codes of length 08 bits (004 total): 13 22 32 81 + Codes of length 09 bits (007 total): 08 14 42 91 A1 B1 C1 + Codes of length 10 bits (005 total): 09 23 33 52 F0 + Codes of length 11 bits (004 total): 15 62 72 D1 + Codes of length 12 bits (004 total): 0A 16 24 34 + Codes of length 13 bits (000 total): + Codes of length 14 bits (001 total): E1 + Codes of length 15 bits (002 total): 25 F1 + Codes of length 16 bits (119 total): 17 18 19 1A 26 27 28 29 2A 35 36 37 38 39 3A 43 + 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 + 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 82 + 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 + 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 + B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 + D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 + F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x000100B7 + Frame header length = 17 + Precision = 8 + Number of Lines = 794 + Samples per Line = 1123 + Image Size = 1123 x 794 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000100CA + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x000100D8 + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x00083F28.0 + + Compression stats: + Compression Ratio: 5.64:1 + Bits per pixel: 4.26:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 727 ( 5%) + # codes of length 03 bits: 7443 ( 52%) + # codes of length 04 bits: 2171 ( 15%) + # codes of length 05 bits: 1627 ( 11%) + # codes of length 06 bits: 1355 ( 10%) + # codes of length 07 bits: 785 ( 6%) + # codes of length 08 bits: 92 ( 1%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 2590 ( 36%) + # codes of length 03 bits: 1357 ( 19%) + # codes of length 04 bits: 1187 ( 17%) + # codes of length 05 bits: 856 ( 12%) + # codes of length 06 bits: 616 ( 9%) + # codes of length 07 bits: 346 ( 5%) + # codes of length 08 bits: 109 ( 2%) + # codes of length 09 bits: 39 ( 1%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 223973 ( 44%) + # codes of length 03 bits: 69375 ( 14%) + # codes of length 04 bits: 93550 ( 19%) + # codes of length 05 bits: 58421 ( 12%) + # codes of length 06 bits: 13137 ( 3%) + # codes of length 07 bits: 22630 ( 4%) + # codes of length 08 bits: 9176 ( 2%) + # codes of length 09 bits: 6545 ( 1%) + # codes of length 10 bits: 3947 ( 1%) + # codes of length 11 bits: 1890 ( 0%) + # codes of length 12 bits: 1162 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 77 ( 0%) + # codes of length 16 bits: 1763 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 44319 ( 35%) + # codes of length 03 bits: 21048 ( 17%) + # codes of length 04 bits: 24019 ( 19%) + # codes of length 05 bits: 17303 ( 14%) + # codes of length 06 bits: 9470 ( 7%) + # codes of length 07 bits: 2699 ( 2%) + # codes of length 08 bits: 3432 ( 3%) + # codes of length 09 bits: 2092 ( 2%) + # codes of length 10 bits: 717 ( 1%) + # codes of length 11 bits: 679 ( 1%) + # codes of length 12 bits: 85 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 150 ( 0%) + # codes of length 15 bits: 75 ( 0%) + # codes of length 16 bits: 425 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[117] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 812, 102, -218] RGB=[189,244,250] @ MCU[ 19, 16] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x00083F27.7 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x00083F28 + + +*** Searching Compression Signatures *** + + Signature: 0193B6220463E5A621ED25A53EC2FE7D + Signature (Rotated): 010D9693F4FC34B402EFA979BED34733 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + SW :[LEAD Technologies Inc ] [002 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Lake.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Lake.jpg.txt new file mode 100644 index 0000000000..900f52cb78 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Lake.jpg.txt @@ -0,0 +1,683 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Lake.jpg] + Filesize: [206342] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000002 + Length = 10392 + Identifier = [Exif] + Identifier TIFF = 0x[49492A00 08000000] + Endian = Intel (little) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000014 + Dir Length = 0x0008 + [Make ] = "Canon" + [Model ] = "Canon EOS DIGITAL REBEL XSi" + [XResolution ] = 300/1 + [YResolution ] = 300/1 + [ResolutionUnit ] = Inch + [Software ] = "Adobe Photoshop Camera Raw 9.8 (Windows)" + [DateTime ] = "2016:12:29 12:57:54" + [ExifOffset ] = @ 0x00DE + Offset to Next IFD = 0x000002D6 + + EXIF IFD1 @ Absolute 0x000002E2 + Dir Length = 0x0006 + [Compression ] = JPEG + [XResolution ] = 72/1 + [YResolution ] = 72/1 + [ResolutionUnit ] = Inch + [JpegIFOffset ] = @ +0x0334 = @ 0x0340 + [JpegIFByteCount ] = 0x[0000255C] / 9564 + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x000000EA + Dir Length = 0x001B + [ExposureTime ] = 1/640 s + [FNumber ] = F8.0 + [ExposureProgram ] = Normal program + [ISOSpeedRatings ] = 200 + [ExifVersion ] = 02.30 + [DateTimeOriginal ] = "2009:07:19 17:00:36" + [DateTimeDigitized ] = "2009:07:19 17:00:36" + [ShutterSpeedValue ] = 9321928/1000000 + [ApertureValue ] = 6/1 + [ExposureBiasValue ] = 0.00 eV + [MaxApertureValue ] = 4/1 + [MeteringMode ] = Pattern + [Flash ] = Flash did not fire + [FocalLength ] = 200 mm + [SubSecTimeOriginal ] = "73" + [SubSecTimeDigitized ] = "73" + [ColorSpace ] = Uncalibrated + [FocalPlaneXResolution ] = 4272000/878 + [FocalPlaneYResolution ] = 2848000/584 + [FocalPlaneResolutionUnit ] = Inch + [CustomRendered ] = Normal process + [ExposureMode ] = Auto exposure + [WhiteBalance ] = Auto white balance + [SceneCaptureType ] = Standard + +*** Marker: APP13 (xFFED) *** + OFFSET: 0x0000289C + Length = 9752 + Identifier = [Photoshop 3.0] + 8BIM: [0x03ED] Name="" Len=[0x0010] DefinedName="ResolutionInfo structure" + Horizontal resolution = 300 pixels per inch + Width unit = inch + Vertical resolution = 300 pixels per inch + Height unit = inch + 8BIM: [0x0404] Name="" Len=[0x003F] DefinedName="IPTC-NAA record" + IPTC [001:090] Coded Character Set = "%G" + IPTC [002:000] Record Version = 4 + IPTC [002:055] Date Created = "20090719" + IPTC [002:060] Time Created = "170036" + IPTC [002:062] Digital Creation Date = "20090719" + IPTC [002:063] Digital Creation Time = "170036" + 8BIM: [0x040C] Name="" Len=[0x2578] DefinedName="Thumbnail resources" + Format = 1 + Width of thumbnail = 256 pixels + Height of thumbnail = 171 pixels + Widthbytes = 768 bytes + Total size = 131328 bytes + Size after compression = 9564 bytes + Bits per pixel = 24 bits + Number of planes = 1 + JFIF data @ 0x0000293E + 8BIM: [0x0425] Name="" Len=[0x0010] DefinedName="Caption digest" + Caption digest = | 0x52 C5 4C EC 1E FE 25 B8 CA 88 F7 0D 2B 5F 09 F5 | R.L...%.....+_.. + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x00004EB6 + Length = 576 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 1 of 1 + Profile Size : 560 bytes + Preferred CMM Type : 'ADBE' (0x41444245) + Profile Version : 0.2.1.0 (0x02100000) + Profile Device/Class : Display Device profile ('mntr' (0x6D6E7472)) + Data Colour Space : rgbData ('RGB ' (0x52474220)) + Profile connection space (PCS) : 'XYZ ' (0x58595A20) + Profile creation date : 1999-06-03 00:00:00 + Profile file signature : 'acsp' (0x61637370) + Primary platform : Apple Computer, Inc. ('APPL' (0x4150504C)) + Profile flags : 0x00000000 + Profile flags > Profile not embedded + Profile flags > Profile can't be used independently of embedded + Device Manufacturer : 'none' (0x6E6F6E65) + Device Model : '....' (0x00000000) + Device attributes : 0x00000000_00000000 + Device attributes > Reflective + Device attributes > Glossy + Device attributes > Media polarity = negative + Device attributes > Black & white media + Rendering intent : Perceptual + Profile creator : 'ADBE' (0x41444245) + Profile ID : 0x00000000_00000000_00000000_00000000 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x000050F8 + Length = 10738 + Identifier = [http://ns.adobe.com/xap/1.0/] + XMP = + | + | + | + | + | + | + | + | + | + | + | + | + | + | 0, 0 + | 32, 22 + | 64, 56 + | 128, 128 + | 192, 196 + | 255, 255 + | + | + | + | + | 0, 0 + | 255, 255 + | + | + | + | + | 0, 0 + | 255, 255 + | + | + | + | + | 0, 0 + | 255, 255 + | + | + | + | + | 0, 0 + | 255, 255 + | + | + | + | + | 0, 0 + | 255, 255 + | + | + | + | + | 0, 0 + | 255, 255 + | + | + | + | + | 0, 0 + | 255, 255 + | + | + | + | + | + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00007AEC + Table length = 132 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 6 4 4 6 9 11 12 16 + DQT, Row #1: 4 5 5 6 8 10 12 12 + DQT, Row #2: 4 5 5 6 10 12 12 12 + DQT, Row #3: 6 6 6 11 12 12 12 12 + DQT, Row #4: 9 8 10 12 12 12 12 12 + DQT, Row #5: 11 10 12 12 12 12 12 12 + DQT, Row #6: 12 12 12 12 12 12 12 12 + DQT, Row #7: 16 12 12 12 12 12 12 12 + Approx quality factor = 88.28 (scaling=23.43 variance=111.68) + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 7 7 13 24 20 20 17 17 + DQT, Row #1: 7 12 16 14 14 12 12 12 + DQT, Row #2: 13 16 14 14 12 12 12 12 + DQT, Row #3: 24 14 14 12 12 12 12 12 + DQT, Row #4: 20 14 12 12 12 12 12 12 + DQT, Row #5: 20 12 12 12 12 12 12 12 + DQT, Row #6: 17 12 12 12 12 12 12 12 + DQT, Row #7: 17 12 12 12 12 12 12 12 + Approx quality factor = 90.19 (scaling=19.62 variance=201.04) + +*** Marker: DRI (Restart Interval) (xFFDD) *** + OFFSET: 0x00007B72 + Length = 4 + interval = 160 + +*** Marker: APP14 (xFFEE) *** + OFFSET: 0x00007B78 + Length = 14 + DCTEncodeVersion = 100 + APP14Flags0 = 49152 + APP14Flags1 = 0 + ColorTransform = 1 [YCbCr] + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x00007B88 + Frame header length = 17 + Precision = 8 + Number of Lines = 853 + Samples per Line = 1280 + Image Size = 1280 x 853 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x00, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x02, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00007B9B + Huffman table length = 159 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 03 + Codes of length 03 bits (003 total): 01 02 04 + Codes of length 04 bits (001 total): 05 + Codes of length 05 bits (001 total): 06 + Codes of length 06 bits (001 total): 07 + Codes of length 07 bits (001 total): 08 + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (001 total): 04 + Codes of length 06 bits (001 total): 05 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 006 + + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 12 21 31 + Codes of length 06 bits (002 total): 05 41 + Codes of length 07 bits (003 total): 13 22 51 + Codes of length 08 bits (006 total): 32 61 71 81 91 A1 + Codes of length 09 bits (005 total): 06 14 23 42 B1 + Codes of length 10 bits (002 total): 52 C1 + Codes of length 11 bits (004 total): 15 33 62 D1 + Codes of length 12 bits (004 total): 07 72 E1 F1 + Codes of length 13 bits (005 total): 16 24 43 82 F0 + Codes of length 14 bits (003 total): 34 92 A2 + Codes of length 15 bits (000 total): + Codes of length 16 bits (011 total): 53 63 C2 25 73 B2 D2 26 54 93 E2 + Total number of codes: 054 + + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (001 total): 02 + Codes of length 05 bits (000 total): + Codes of length 06 bits (002 total): 12 21 + Codes of length 07 bits (002 total): 03 31 + Codes of length 08 bits (003 total): 13 41 51 + Codes of length 09 bits (001 total): 61 + Codes of length 10 bits (000 total): + Codes of length 11 bits (002 total): 04 71 + Codes of length 12 bits (003 total): 14 22 81 + Codes of length 13 bits (001 total): 32 + Codes of length 14 bits (001 total): 42 + Codes of length 15 bits (001 total): 91 + Codes of length 16 bits (000 total): + Total number of codes: 020 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00007C3C + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x00, table=0(DC),0(AC) + Component[2]: selector=0x01, table=1(DC),1(AC) + Component[3]: selector=0x02, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x00007C4A + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x00032604.0 + + Compression stats: + Compression Ratio: 18.77:1 + Bits per pixel: 1.28:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 8237 ( 48%) + # codes of length 03 bits: 7451 ( 44%) + # codes of length 04 bits: 930 ( 5%) + # codes of length 05 bits: 300 ( 2%) + # codes of length 06 bits: 197 ( 1%) + # codes of length 07 bits: 5 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 16681 ( 49%) + # codes of length 02 bits: 10125 ( 30%) + # codes of length 03 bits: 5138 ( 15%) + # codes of length 04 bits: 1825 ( 5%) + # codes of length 05 bits: 432 ( 1%) + # codes of length 06 bits: 39 ( 0%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 104267 ( 49%) + # codes of length 03 bits: 23564 ( 11%) + # codes of length 04 bits: 44372 ( 21%) + # codes of length 05 bits: 19037 ( 9%) + # codes of length 06 bits: 5565 ( 3%) + # codes of length 07 bits: 5437 ( 3%) + # codes of length 08 bits: 5066 ( 2%) + # codes of length 09 bits: 2163 ( 1%) + # codes of length 10 bits: 491 ( 0%) + # codes of length 11 bits: 407 ( 0%) + # codes of length 12 bits: 211 ( 0%) + # codes of length 13 bits: 115 ( 0%) + # codes of length 14 bits: 36 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 26 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 34240 ( 51%) + # codes of length 02 bits: 15658 ( 24%) + # codes of length 03 bits: 7424 ( 11%) + # codes of length 04 bits: 3865 ( 6%) + # codes of length 05 bits: 0 ( 0%) + # codes of length 06 bits: 3125 ( 5%) + # codes of length 07 bits: 1208 ( 2%) + # codes of length 08 bits: 744 ( 1%) + # codes of length 09 bits: 113 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 60 ( 0%) + # codes of length 12 bits: 60 ( 0%) + # codes of length 13 bits: 9 ( 0%) + # codes of length 14 bits: 4 ( 0%) + # codes of length 15 bits: 1 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[122] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 714, -42, 42] RGB=[224,215,206] @ MCU[113, 24] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 106 + Next position in scan buffer: Offset 0x00032604.0 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x00032604 + + +*** Embedded JPEG Thumbnail *** + Offset: 0x00000340 + Length: 0x0000255C (9564) + + * Embedded Thumb Marker: SOI + + * Embedded Thumb Marker: DQT + Length = 132 + ---- + Precision=8 bits + Destination ID=0 (Luminance, typically) + DQT, Row #0: 6 4 4 6 9 11 12 16 + DQT, Row #1: 4 5 5 6 8 10 12 12 + DQT, Row #2: 4 5 5 6 10 12 12 12 + DQT, Row #3: 6 6 6 11 12 12 12 12 + DQT, Row #4: 9 8 10 12 12 12 12 12 + DQT, Row #5: 11 10 12 12 12 12 12 12 + DQT, Row #6: 12 12 12 12 12 12 12 12 + DQT, Row #7: 16 12 12 12 12 12 12 12 + ---- + Precision=8 bits + Destination ID=1 (Chrominance, typically) + DQT, Row #0: 7 7 13 24 20 20 17 17 + DQT, Row #1: 7 12 16 14 14 12 12 12 + DQT, Row #2: 13 16 14 14 12 12 12 12 + DQT, Row #3: 24 14 14 12 12 12 12 12 + DQT, Row #4: 20 14 12 12 12 12 12 12 + DQT, Row #5: 20 12 12 12 12 12 12 12 + DQT, Row #6: 17 12 12 12 12 12 12 12 + DQT, Row #7: 17 12 12 12 12 12 12 12 + + * Embedded Thumb Marker: DRI + Length = 4 + + * Embedded Thumb Marker: APP14 + Length = 14 + + * Embedded Thumb Marker: SOF + Frame header length = 17 + Precision = 8 + Number of Lines = 171 + Samples per Line = 256 + Image Size = 256 x 171 + + * Embedded Thumb Marker: DHT + Length = 148 + + * Embedded Thumb Marker: SOS + Skipping scan data + Skipped 9212 bytes + + * Embedded Thumb Marker: EOI + + * Embedded Thumb Signature: 01180AF3DE63318828A86409EF4013DD + +*** Searching Compression Signatures *** + + Signature: 01180AF3DE63318828A86409EF4013DD + Signature (Rotated): 01180AF3DE63318828A86409EF4013DD + File Offset: 0 bytes + Chroma subsampling: 1x1 + EXIF Make/Model: OK [Canon] [Canon EOS DIGITAL REBEL XSi] + EXIF Makernotes: NONE + EXIF Software: OK [Adobe Photoshop Camera Raw 9.8 (Windows)] + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + SW :[Adobe Photoshop ] [Save As 08 ] + + NOTE: EXIF Software field recognized as from editor + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + Appears to be new signature for known software. + If the camera/software doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/MultiScanBaselineCMYK.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/MultiScanBaselineCMYK.jpg.txt new file mode 100644 index 0000000000..cd415a201a --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/MultiScanBaselineCMYK.jpg.txt @@ -0,0 +1,282 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\MultiScanBaselineCMYK.jpg] + Filesize: [47443] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 72 x 72 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: APP14 (xFFEE) *** + OFFSET: 0x00000014 + Length = 14 + DCTEncodeVersion = 25600 + APP14Flags0 = 0 + APP14Flags1 = 0 + ColorTransform = 2 [YCCK] + +*** Marker: COM (Comment) (xFFFE) *** + OFFSET: 0x00000024 + Comment length = 38 + Comment=Created by fCoder Graphics Processor + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x0000004C + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 5 3 3 5 7 12 15 18 + DQT, Row #1: 4 4 4 6 8 17 18 17 + DQT, Row #2: 4 4 5 7 12 17 21 17 + DQT, Row #3: 4 5 7 9 15 26 24 19 + DQT, Row #4: 5 7 11 17 20 33 31 23 + DQT, Row #5: 7 11 17 19 24 31 34 28 + DQT, Row #6: 15 19 23 26 31 36 36 30 + DQT, Row #7: 22 28 29 29 34 30 31 30 + Approx quality factor = 84.93 (scaling=30.13 variance=1.05) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000091 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 5 5 7 14 30 30 30 30 + DQT, Row #1: 5 6 8 20 30 30 30 30 + DQT, Row #2: 7 8 17 30 30 30 30 30 + DQT, Row #3: 14 20 30 30 30 30 30 30 + DQT, Row #4: 30 30 30 30 30 30 30 30 + DQT, Row #5: 30 30 30 30 30 30 30 30 + DQT, Row #6: 30 30 30 30 30 30 30 30 + DQT, Row #7: 30 30 30 30 30 30 30 30 + Approx quality factor = 84.93 (scaling=30.15 variance=0.29) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x000000D6 + Frame header length = 20 + Precision = 8 + Number of Lines = 842 + Samples per Line = 595 + Image Size = 595 x 842 + Raw Image Orientation = Portrait + Number of Img components = 4 + Component[1]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Cr) + Component[4]: ID=0x04, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (K) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000EC + Huffman table length = 31 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (005 total): 01 02 03 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (001 total): 0A + Codes of length 09 bits (001 total): 0B + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000010D + Huffman table length = 181 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 05 12 21 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 06 13 51 61 + Codes of length 08 bits (003 total): 07 22 71 + Codes of length 09 bits (005 total): 14 32 81 91 A1 + Codes of length 10 bits (005 total): 08 23 42 B1 C1 + Codes of length 11 bits (004 total): 15 52 D1 F0 + Codes of length 12 bits (004 total): 24 33 62 72 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (001 total): 82 + Codes of length 16 bits (125 total): 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 + 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 + 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 + 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 + 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 + B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA + D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 + E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000001C4 + Huffman table length = 31 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (001 total): 07 + Codes of length 08 bits (001 total): 08 + Codes of length 09 bits (001 total): 09 + Codes of length 10 bits (001 total): 0A + Codes of length 11 bits (001 total): 0B + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000001E5 + Huffman table length = 181 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (002 total): 03 11 + Codes of length 05 bits (004 total): 04 05 21 31 + Codes of length 06 bits (004 total): 06 12 41 51 + Codes of length 07 bits (003 total): 07 61 71 + Codes of length 08 bits (004 total): 13 22 32 81 + Codes of length 09 bits (007 total): 08 14 42 91 A1 B1 C1 + Codes of length 10 bits (005 total): 09 23 33 52 F0 + Codes of length 11 bits (004 total): 15 62 72 D1 + Codes of length 12 bits (004 total): 0A 16 24 34 + Codes of length 13 bits (000 total): + Codes of length 14 bits (001 total): E1 + Codes of length 15 bits (002 total): 25 F1 + Codes of length 16 bits (119 total): 17 18 19 1A 26 27 28 29 2A 35 36 37 38 39 3A 43 + 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 + 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 82 + 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 + 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 + B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 + D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 + F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000029C + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support CMYK files yet. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00005825 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support CMYK files yet. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000766A + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support CMYK files yet. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000A1FA + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x04, table=0(DC),0(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support CMYK files yet. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0000B951 + + +*** Searching Compression Signatures *** + + Signature: 0155D875C95B74D0F3C5835A62516F48 + Signature (Rotated): 01D38A25358EB7649A254E19F1D46600 + File Offset: 0 bytes + Chroma subsampling: ?x? + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[NIKON ] [E2500 ] [FINE ] No + CAM:[Nokia ] [N73 ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C2000Z ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C3040Z ] [ ] No + CAM:[PENTAX ] [PENTAX Optio 550 ] [ ] No + CAM:[Research In Motion ] [BlackBerry 8100 ] [ ] No + CAM:[SEIKO EPSON CORP. ] [PhotoPC 3000Z ] [ ] No + SW :[IJG Library ] [085 ] + SW :[Picasa ] [085 (Normal) ] + SW :[ZoomBrowser EX ] [medium ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [085 ] + SW :[IrfanView ] [085 ] + SW :[idImager ] [085 ] + SW :[FastStone Image Viewer ] [085 ] + SW :[NeatImage ] [085 ] + SW :[Paint.NET ] [085 ] + SW :[Photomatix ] [085 ] + SW :[XnView ] [085 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Snake.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Snake.jpg.txt new file mode 100644 index 0000000000..926da026fd --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/Snake.jpg.txt @@ -0,0 +1,683 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Snake.jpg] + Filesize: [165200] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000002 + Length = 11941 + Identifier = [Exif] + Identifier TIFF = 0x[49492A00 08000000] + Endian = Intel (little) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000014 + Dir Length = 0x0008 + [Make ] = "Canon" + [Model ] = "Canon EOS DIGITAL REBEL XSi" + [XResolution ] = 300/1 + [YResolution ] = 300/1 + [ResolutionUnit ] = Inch + [Software ] = "Adobe Photoshop Camera Raw 9.8 (Windows)" + [DateTime ] = "2016:12:29 12:57:50" + [ExifOffset ] = @ 0x00DE + Offset to Next IFD = 0x000002D6 + + EXIF IFD1 @ Absolute 0x000002E2 + Dir Length = 0x0006 + [Compression ] = JPEG + [XResolution ] = 72/1 + [YResolution ] = 72/1 + [ResolutionUnit ] = Inch + [JpegIFOffset ] = @ +0x0334 = @ 0x0340 + [JpegIFByteCount ] = 0x[00002B69] / 11113 + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x000000EA + Dir Length = 0x001B + [ExposureTime ] = 1/25 s + [FNumber ] = F4.0 + [ExposureProgram ] = Shutter priority + [ISOSpeedRatings ] = 250 + [ExifVersion ] = 02.30 + [DateTimeOriginal ] = "2009:07:19 13:25:29" + [DateTimeDigitized ] = "2009:07:19 13:25:29" + [ShutterSpeedValue ] = 4643856/1000000 + [ApertureValue ] = 4/1 + [ExposureBiasValue ] = 0.00 eV + [MaxApertureValue ] = 4/1 + [MeteringMode ] = Pattern + [Flash ] = Flash did not fire + [FocalLength ] = 200 mm + [SubSecTimeOriginal ] = "03" + [SubSecTimeDigitized ] = "03" + [ColorSpace ] = Uncalibrated + [FocalPlaneXResolution ] = 4272000/878 + [FocalPlaneYResolution ] = 2848000/584 + [FocalPlaneResolutionUnit ] = Inch + [CustomRendered ] = Normal process + [ExposureMode ] = Auto exposure + [WhiteBalance ] = Auto white balance + [SceneCaptureType ] = Standard + +*** Marker: APP13 (xFFED) *** + OFFSET: 0x00002EA9 + Length = 11302 + Identifier = [Photoshop 3.0] + 8BIM: [0x03ED] Name="" Len=[0x0010] DefinedName="ResolutionInfo structure" + Horizontal resolution = 300 pixels per inch + Width unit = inch + Vertical resolution = 300 pixels per inch + Height unit = inch + 8BIM: [0x0404] Name="" Len=[0x003F] DefinedName="IPTC-NAA record" + IPTC [001:090] Coded Character Set = "%G" + IPTC [002:000] Record Version = 4 + IPTC [002:055] Date Created = "20090719" + IPTC [002:060] Time Created = "132529" + IPTC [002:062] Digital Creation Date = "20090719" + IPTC [002:063] Digital Creation Time = "132529" + 8BIM: [0x040C] Name="" Len=[0x2B85] DefinedName="Thumbnail resources" + Format = 1 + Width of thumbnail = 256 pixels + Height of thumbnail = 171 pixels + Widthbytes = 768 bytes + Total size = 131328 bytes + Size after compression = 11113 bytes + Bits per pixel = 24 bits + Number of planes = 1 + JFIF data @ 0x00002F4B + 8BIM: [0x0425] Name="" Len=[0x0010] DefinedName="Caption digest" + Caption digest = | 0xEE 2F A2 47 C5 F8 ED 07 08 CD FF 82 A0 D1 7F F2 | ./.G............ + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x00005AD1 + Length = 576 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 1 of 1 + Profile Size : 560 bytes + Preferred CMM Type : 'ADBE' (0x41444245) + Profile Version : 0.2.1.0 (0x02100000) + Profile Device/Class : Display Device profile ('mntr' (0x6D6E7472)) + Data Colour Space : rgbData ('RGB ' (0x52474220)) + Profile connection space (PCS) : 'XYZ ' (0x58595A20) + Profile creation date : 1999-06-03 00:00:00 + Profile file signature : 'acsp' (0x61637370) + Primary platform : Apple Computer, Inc. ('APPL' (0x4150504C)) + Profile flags : 0x00000000 + Profile flags > Profile not embedded + Profile flags > Profile can't be used independently of embedded + Device Manufacturer : 'none' (0x6E6F6E65) + Device Model : '....' (0x00000000) + Device attributes : 0x00000000_00000000 + Device attributes > Reflective + Device attributes > Glossy + Device attributes > Media polarity = negative + Device attributes > Black & white media + Rendering intent : Perceptual + Profile creator : 'ADBE' (0x41444245) + Profile ID : 0x00000000_00000000_00000000_00000000 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00005D13 + Length = 10733 + Identifier = [http://ns.adobe.com/xap/1.0/] + XMP = + | + | + | + | + | + | + | + | + | + | + | + | + | + | 0, 0 + | 32, 22 + | 64, 56 + | 128, 128 + | 192, 196 + | 255, 255 + | + | + | + | + | 0, 0 + | 255, 255 + | + | + | + | + | 0, 0 + | 255, 255 + | + | + | + | + | 0, 0 + | 255, 255 + | + | + | + | + | 0, 0 + | 255, 255 + | + | + | + | + | 0, 0 + | 255, 255 + | + | + | + | + | 0, 0 + | 255, 255 + | + | + | + | + | 0, 0 + | 255, 255 + | + | + | + | + | + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00008702 + Table length = 132 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 6 4 4 6 9 11 12 16 + DQT, Row #1: 4 5 5 6 8 10 12 12 + DQT, Row #2: 4 5 5 6 10 12 12 12 + DQT, Row #3: 6 6 6 11 12 12 12 12 + DQT, Row #4: 9 8 10 12 12 12 12 12 + DQT, Row #5: 11 10 12 12 12 12 12 12 + DQT, Row #6: 12 12 12 12 12 12 12 12 + DQT, Row #7: 16 12 12 12 12 12 12 12 + Approx quality factor = 88.28 (scaling=23.43 variance=111.68) + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 7 7 13 24 20 20 17 17 + DQT, Row #1: 7 12 16 14 14 12 12 12 + DQT, Row #2: 13 16 14 14 12 12 12 12 + DQT, Row #3: 24 14 14 12 12 12 12 12 + DQT, Row #4: 20 14 12 12 12 12 12 12 + DQT, Row #5: 20 12 12 12 12 12 12 12 + DQT, Row #6: 17 12 12 12 12 12 12 12 + DQT, Row #7: 17 12 12 12 12 12 12 12 + Approx quality factor = 90.19 (scaling=19.62 variance=201.04) + +*** Marker: DRI (Restart Interval) (xFFDD) *** + OFFSET: 0x00008788 + Length = 4 + interval = 160 + +*** Marker: APP14 (xFFEE) *** + OFFSET: 0x0000878E + Length = 14 + DCTEncodeVersion = 100 + APP14Flags0 = 49152 + APP14Flags1 = 0 + ColorTransform = 1 [YCbCr] + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x0000879E + Frame header length = 17 + Precision = 8 + Number of Lines = 853 + Samples per Line = 1280 + Image Size = 1280 x 853 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x00, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x02, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000087B1 + Huffman table length = 153 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 02 03 + Codes of length 03 bits (003 total): 01 04 05 + Codes of length 04 bits (001 total): 00 + Codes of length 05 bits (001 total): 06 + Codes of length 06 bits (001 total): 07 + Codes of length 07 bits (001 total): 08 + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (001 total): 04 + Codes of length 06 bits (001 total): 05 + Codes of length 07 bits (001 total): 06 + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 007 + + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (002 total): 00 03 + Codes of length 04 bits (002 total): 04 11 + Codes of length 05 bits (001 total): 21 + Codes of length 06 bits (003 total): 05 12 31 + Codes of length 07 bits (003 total): 13 41 51 + Codes of length 08 bits (003 total): 06 22 61 + Codes of length 09 bits (003 total): 14 32 71 + Codes of length 10 bits (003 total): 42 81 91 + Codes of length 11 bits (003 total): 15 23 A1 + Codes of length 12 bits (004 total): 07 52 B1 C1 + Codes of length 13 bits (001 total): 33 + Codes of length 14 bits (002 total): 24 43 + Codes of length 15 bits (002 total): 62 D1 + Codes of length 16 bits (011 total): 16 34 72 E1 25 53 63 92 82 A2 F0 + Total number of codes: 045 + + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (001 total): 02 + Codes of length 05 bits (000 total): + Codes of length 06 bits (003 total): 03 12 21 + Codes of length 07 bits (001 total): 31 + Codes of length 08 bits (000 total): + Codes of length 09 bits (002 total): 04 41 + Codes of length 10 bits (003 total): 13 22 51 + Codes of length 11 bits (000 total): + Codes of length 12 bits (003 total): 32 61 71 + Codes of length 13 bits (000 total): + Codes of length 14 bits (003 total): 05 14 42 + Codes of length 15 bits (000 total): + Codes of length 16 bits (003 total): 81 52 62 + Total number of codes: 022 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000884C + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x00, table=0(DC),0(AC) + Component[2]: selector=0x01, table=1(DC),1(AC) + Component[3]: selector=0x02, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x0000885A + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x0002854E.0 + + Compression stats: + Compression Ratio: 25.14:1 + Bits per pixel: 0.95:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 7183 ( 42%) + # codes of length 03 bits: 7286 ( 43%) + # codes of length 04 bits: 1551 ( 9%) + # codes of length 05 bits: 856 ( 5%) + # codes of length 06 bits: 218 ( 1%) + # codes of length 07 bits: 26 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 12266 ( 36%) + # codes of length 02 bits: 11394 ( 33%) + # codes of length 03 bits: 6548 ( 19%) + # codes of length 04 bits: 2911 ( 9%) + # codes of length 05 bits: 875 ( 3%) + # codes of length 06 bits: 241 ( 1%) + # codes of length 07 bits: 5 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 59325 ( 45%) + # codes of length 03 bits: 31160 ( 24%) + # codes of length 04 bits: 18131 ( 14%) + # codes of length 05 bits: 4871 ( 4%) + # codes of length 06 bits: 9522 ( 7%) + # codes of length 07 bits: 4029 ( 3%) + # codes of length 08 bits: 2270 ( 2%) + # codes of length 09 bits: 1006 ( 1%) + # codes of length 10 bits: 515 ( 0%) + # codes of length 11 bits: 268 ( 0%) + # codes of length 12 bits: 195 ( 0%) + # codes of length 13 bits: 24 ( 0%) + # codes of length 14 bits: 29 ( 0%) + # codes of length 15 bits: 20 ( 0%) + # codes of length 16 bits: 26 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 34240 ( 51%) + # codes of length 02 bits: 16000 ( 24%) + # codes of length 03 bits: 5994 ( 9%) + # codes of length 04 bits: 5610 ( 8%) + # codes of length 05 bits: 0 ( 0%) + # codes of length 06 bits: 3610 ( 5%) + # codes of length 07 bits: 600 ( 1%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 410 ( 1%) + # codes of length 10 bits: 200 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 70 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 17 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 6 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[110] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 954, 14, -14] RGB=[244,248,248] @ MCU[124, 21] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 106 + Next position in scan buffer: Offset 0x0002854D.3 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0002854E + + +*** Embedded JPEG Thumbnail *** + Offset: 0x00000340 + Length: 0x00002B69 (11113) + + * Embedded Thumb Marker: SOI + + * Embedded Thumb Marker: DQT + Length = 132 + ---- + Precision=8 bits + Destination ID=0 (Luminance, typically) + DQT, Row #0: 6 4 4 6 9 11 12 16 + DQT, Row #1: 4 5 5 6 8 10 12 12 + DQT, Row #2: 4 5 5 6 10 12 12 12 + DQT, Row #3: 6 6 6 11 12 12 12 12 + DQT, Row #4: 9 8 10 12 12 12 12 12 + DQT, Row #5: 11 10 12 12 12 12 12 12 + DQT, Row #6: 12 12 12 12 12 12 12 12 + DQT, Row #7: 16 12 12 12 12 12 12 12 + ---- + Precision=8 bits + Destination ID=1 (Chrominance, typically) + DQT, Row #0: 7 7 13 24 20 20 17 17 + DQT, Row #1: 7 12 16 14 14 12 12 12 + DQT, Row #2: 13 16 14 14 12 12 12 12 + DQT, Row #3: 24 14 14 12 12 12 12 12 + DQT, Row #4: 20 14 12 12 12 12 12 12 + DQT, Row #5: 20 12 12 12 12 12 12 12 + DQT, Row #6: 17 12 12 12 12 12 12 12 + DQT, Row #7: 17 12 12 12 12 12 12 12 + + * Embedded Thumb Marker: DRI + Length = 4 + + * Embedded Thumb Marker: APP14 + Length = 14 + + * Embedded Thumb Marker: SOF + Frame header length = 17 + Precision = 8 + Number of Lines = 171 + Samples per Line = 256 + Image Size = 256 x 171 + + * Embedded Thumb Marker: DHT + Length = 150 + + * Embedded Thumb Marker: SOS + Skipping scan data + Skipped 10759 bytes + + * Embedded Thumb Marker: EOI + + * Embedded Thumb Signature: 01180AF3DE63318828A86409EF4013DD + +*** Searching Compression Signatures *** + + Signature: 01180AF3DE63318828A86409EF4013DD + Signature (Rotated): 01180AF3DE63318828A86409EF4013DD + File Offset: 0 bytes + Chroma subsampling: 1x1 + EXIF Make/Model: OK [Canon] [Canon EOS DIGITAL REBEL XSi] + EXIF Makernotes: NONE + EXIF Software: OK [Adobe Photoshop Camera Raw 9.8 (Windows)] + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + SW :[Adobe Photoshop ] [Save As 08 ] + + NOTE: EXIF Software field recognized as from editor + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + Appears to be new signature for known software. + If the camera/software doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/badeof.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/badeof.jpg.txt new file mode 100644 index 0000000000..97be4853e6 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/badeof.jpg.txt @@ -0,0 +1,347 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\badeof.jpg] + Filesize: [5770] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 1 x 1 (aspect ratio) + thumbnail = 0 x 0 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000014 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 8 6 5 8 12 20 26 31 + DQT, Row #1: 6 6 7 10 13 29 30 28 + DQT, Row #2: 7 7 8 12 20 29 35 28 + DQT, Row #3: 7 9 11 15 26 44 40 31 + DQT, Row #4: 9 11 19 28 34 55 52 39 + DQT, Row #5: 12 18 28 32 41 52 57 46 + DQT, Row #6: 25 32 39 44 52 61 60 51 + DQT, Row #7: 36 46 48 49 56 50 52 50 + Approx quality factor = 74.75 (scaling=50.51 variance=0.81) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000059 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 9 9 12 24 50 50 50 50 + DQT, Row #1: 9 11 13 33 50 50 50 50 + DQT, Row #2: 12 13 28 50 50 50 50 50 + DQT, Row #3: 24 33 50 50 50 50 50 50 + DQT, Row #4: 50 50 50 50 50 50 50 50 + DQT, Row #5: 50 50 50 50 50 50 50 50 + DQT, Row #6: 50 50 50 50 50 50 50 50 + DQT, Row #7: 50 50 50 50 50 50 50 50 + Approx quality factor = 74.74 (scaling=50.52 variance=0.19) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x0000009E + Frame header length = 17 + Precision = 8 + Number of Lines = 149 + Samples per Line = 227 + Image Size = 227 x 149 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000B1 + Huffman table length = 31 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (005 total): 01 02 03 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (001 total): 0A + Codes of length 09 bits (001 total): 0B + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000D2 + Huffman table length = 181 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 05 12 21 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 06 13 51 61 + Codes of length 08 bits (003 total): 07 22 71 + Codes of length 09 bits (005 total): 14 32 81 91 A1 + Codes of length 10 bits (005 total): 08 23 42 B1 C1 + Codes of length 11 bits (004 total): 15 52 D1 F0 + Codes of length 12 bits (004 total): 24 33 62 72 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (001 total): 82 + Codes of length 16 bits (125 total): 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 + 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 + 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 + 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 + 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 + B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA + D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 + E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000189 + Huffman table length = 31 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (001 total): 07 + Codes of length 08 bits (001 total): 08 + Codes of length 09 bits (001 total): 09 + Codes of length 10 bits (001 total): 0A + Codes of length 11 bits (001 total): 0B + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000001AA + Huffman table length = 181 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (002 total): 03 11 + Codes of length 05 bits (004 total): 04 05 21 31 + Codes of length 06 bits (004 total): 06 12 41 51 + Codes of length 07 bits (003 total): 07 61 71 + Codes of length 08 bits (004 total): 13 22 32 81 + Codes of length 09 bits (007 total): 08 14 42 91 A1 B1 C1 + Codes of length 10 bits (005 total): 09 23 33 52 F0 + Codes of length 11 bits (004 total): 15 62 72 D1 + Codes of length 12 bits (004 total): 0A 16 24 34 + Codes of length 13 bits (000 total): + Codes of length 14 bits (001 total): E1 + Codes of length 15 bits (002 total): 25 F1 + Codes of length 16 bits (119 total): 17 18 19 1A 26 27 28 29 2A 35 36 37 38 39 3A 43 + 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 + 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 82 + 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 + 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 + B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 + D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 + F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000261 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x0000026F + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + + Compression stats: + Compression Ratio: 19.73:1 + Bits per pixel: 1.22:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 88 ( 15%) + # codes of length 03 bits: 409 ( 68%) + # codes of length 04 bits: 66 ( 11%) + # codes of length 05 bits: 33 ( 6%) + # codes of length 06 bits: 4 ( 1%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 134 ( 45%) + # codes of length 03 bits: 68 ( 23%) + # codes of length 04 bits: 60 ( 20%) + # codes of length 05 bits: 26 ( 9%) + # codes of length 06 bits: 12 ( 4%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 2706 ( 48%) + # codes of length 03 bits: 636 ( 11%) + # codes of length 04 bits: 1331 ( 23%) + # codes of length 05 bits: 473 ( 8%) + # codes of length 06 bits: 196 ( 3%) + # codes of length 07 bits: 169 ( 3%) + # codes of length 08 bits: 66 ( 1%) + # codes of length 09 bits: 60 ( 1%) + # codes of length 10 bits: 28 ( 0%) + # codes of length 11 bits: 14 ( 0%) + # codes of length 12 bits: 4 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 5 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 697 ( 46%) + # codes of length 03 bits: 243 ( 16%) + # codes of length 04 bits: 294 ( 19%) + # codes of length 05 bits: 164 ( 11%) + # codes of length 06 bits: 68 ( 4%) + # codes of length 07 bits: 5 ( 0%) + # codes of length 08 bits: 35 ( 2%) + # codes of length 09 bits: 4 ( 0%) + # codes of length 10 bits: 2 ( 0%) + # codes of length 11 bits: 1 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[107] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 1008, -9, 0] RGB=[254,254,250] @ MCU[ 4, 8] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x00001687.2 + + +*** Skipped 1 marker pad bytes *** +*** Marker: ??? (Unknown) (xFF00) *** + OFFSET: 0x00001689 + WARNING: Unknown marker [0xFF00] + Stopping decode + Use [Img Search Fwd/Rev] to locate other valid embedded JPEGs + +*** Searching Compression Signatures *** + + Signature: 0182408A81A4ABF04D4A34A8A5E98C58 + Signature (Rotated): 012D821C6AB210E2A753BE053B8F55D0 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[SONY ] [CYBERSHOT U ] [ ] Yes + SW :[Adobe Photoshop 7.0 ] [Save As 07 ] + SW :[Apple Quicktime ] [0466-0467 ] + SW :[Digital Photo Professiona] [05 ] + SW :[IJG Library ] [075 ] + SW :[MS Paint ] [ ] + SW :[MS Visio ] [ ] + SW :[ZoomBrowser EX ] [low ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [075 ] + SW :[IrfanView ] [075 ] + SW :[idImager ] [075 ] + SW :[FastStone Image Viewer ] [075 ] + SW :[NeatImage ] [075 ] + SW :[Paint.NET ] [075 ] + SW :[Photomatix ] [075 ] + SW :[XnView ] [075 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + + +*** Additional Info *** +NOTE: Data exists after EOF, range: 0x00000000-0x0000168A (5770 bytes) diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/badrst.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/badrst.jpg.txt new file mode 100644 index 0000000000..cb74eb88f5 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/badrst.jpg.txt @@ -0,0 +1,434 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\badrst.jpg] + Filesize: [74497] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 96 x 96 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000014 + Length = 8628 + Identifier = [Exif] + Identifier TIFF = 0x[4D4D002A 00000008] + Endian = Motorola (big) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000026 + Dir Length = 0x0003 + [Orientation ] = 1 = Row 0: top, Col 0: left + [ExifOffset ] = @ 0x083E + Offset to Next IFD = 0x000010B6 + + EXIF IFD1 @ Absolute 0x000010D4 + Dir Length = 0x0006 + [Compression ] = JPEG + [XResolution ] = 96/1 + [YResolution ] = 96/1 + [ResolutionUnit ] = Inch + [JpegIFOffset ] = @ +0x1114 = @ 0x1132 + [JpegIFByteCount ] = 0x[00001097] / 4247 + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x0000085C + Dir Length = 0x0005 + [DateTimeOriginal ] = "2016:02:28 11:17:08" + [DateTimeDigitized ] = "2016:02:28 11:17:08" + [SubSecTimeOriginal ] = "06" + [SubSecTimeDigitized ] = "06" + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x000021CA + Length = 2464 + Identifier = [http://ns.adobe.com/xap/1.0/] + XMP = + | + |2016-02-28T11:17:08.057 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00002B6C + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 3 2 2 3 5 8 10 12 + DQT, Row #1: 2 2 3 4 5 12 12 11 + DQT, Row #2: 3 3 3 5 8 11 14 11 + DQT, Row #3: 3 3 4 6 10 17 16 12 + DQT, Row #4: 4 4 7 11 14 22 21 15 + DQT, Row #5: 5 7 11 13 16 21 23 18 + DQT, Row #6: 10 13 16 17 21 24 24 20 + DQT, Row #7: 14 18 19 20 22 20 21 20 + Approx quality factor = 90.06 (scaling=19.88 variance=1.14) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00002BB1 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 3 4 5 9 20 20 20 20 + DQT, Row #1: 4 4 5 13 20 20 20 20 + DQT, Row #2: 5 5 11 20 20 20 20 20 + DQT, Row #3: 9 13 20 20 20 20 20 20 + DQT, Row #4: 20 20 20 20 20 20 20 20 + DQT, Row #5: 20 20 20 20 20 20 20 20 + DQT, Row #6: 20 20 20 20 20 20 20 20 + DQT, Row #7: 20 20 20 20 20 20 20 20 + Approx quality factor = 89.93 (scaling=20.14 variance=0.34) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x00002BF6 + Frame header length = 17 + Precision = 8 + Number of Lines = 480 + Samples per Line = 640 + Image Size = 640 x 480 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00002C09 + Huffman table length = 31 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (005 total): 01 02 03 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (001 total): 0A + Codes of length 09 bits (001 total): 0B + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00002C2A + Huffman table length = 181 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 05 12 21 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 06 13 51 61 + Codes of length 08 bits (003 total): 07 22 71 + Codes of length 09 bits (005 total): 14 32 81 91 A1 + Codes of length 10 bits (005 total): 08 23 42 B1 C1 + Codes of length 11 bits (004 total): 15 52 D1 F0 + Codes of length 12 bits (004 total): 24 33 62 72 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (001 total): 82 + Codes of length 16 bits (125 total): 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 + 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 + 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 + 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 + 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 + B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA + D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 + E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00002CE1 + Huffman table length = 31 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (001 total): 07 + Codes of length 08 bits (001 total): 08 + Codes of length 09 bits (001 total): 09 + Codes of length 10 bits (001 total): 0A + Codes of length 11 bits (001 total): 0B + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00002D02 + Huffman table length = 181 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (002 total): 03 11 + Codes of length 05 bits (004 total): 04 05 21 31 + Codes of length 06 bits (004 total): 06 12 41 51 + Codes of length 07 bits (003 total): 07 61 71 + Codes of length 08 bits (004 total): 13 22 32 81 + Codes of length 09 bits (007 total): 08 14 42 91 A1 B1 C1 + Codes of length 10 bits (005 total): 09 23 33 52 F0 + Codes of length 11 bits (004 total): 15 62 72 D1 + Codes of length 12 bits (004 total): 0A 16 24 34 + Codes of length 13 bits (000 total): + Codes of length 14 bits (001 total): E1 + Codes of length 15 bits (002 total): 25 F1 + Codes of length 16 bits (119 total): 17 18 19 1A 26 27 28 29 2A 35 36 37 38 39 3A 43 + 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 + 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 82 + 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 + 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 + B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 + D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 + F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: DRI (Restart Interval) (xFFDD) *** + OFFSET: 0x00002DB9 + Length = 4 + interval = 600 + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00002DBF + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x00002DCD + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Expect Restart interval elapsed @ 0x00008802.4 + ERROR: Restart marker not detected +*** ERROR: Can't find huffman bitstring @ 0x00008802.5, table 0, value [0xffffffe0] +*** ERROR: Bad huffman code @ 0x00008802.4 +*** ERROR: Bad scan data in MCU(0,15): Lum CSS(0,0) @ Offset 0x00008802.5 + MCU located at pixel=(0,240) +*** ERROR: Can't find huffman bitstring @ 0x00008802.6, table 0, value [0xffffffc0] +*** ERROR: Bad huffman code @ 0x00008802.5 +*** ERROR: Bad scan data in MCU(0,15): Lum CSS(1,0) @ Offset 0x00008802.6 + MCU located at pixel=(8,240) +*** ERROR: Can't find huffman bitstring @ 0x00008802.7, table 0, value [0xffffff80] +*** ERROR: Bad huffman code @ 0x00008802.6 +*** ERROR: Bad scan data in MCU(0,15): Lum CSS(0,1) @ Offset 0x00008802.7 + MCU located at pixel=(0,248) +*** ERROR: Can't find huffman bitstring @ 0x00008803.0, table 0, value [0xffffffff] +*** ERROR: Bad huffman code @ 0x00008802.7 +*** ERROR: Bad scan data in MCU(0,15): Lum CSS(1,1) @ Offset 0x00008803.0 + MCU located at pixel=(8,248) +*** ERROR: Can't find huffman bitstring @ 0x00008803.1, table 1, value [0xfffffffe] +*** ERROR: Bad huffman code @ 0x00008803.0 +*** ERROR: Bad scan data in MCU(0,15): Chr(Cb) CSS(0,0) @ Offset 0x00008803.1 + MCU located at pixel=(0,240) +*** ERROR: Can't find huffman bitstring @ 0x00008803.2, table 1, value [0xfffffffc] +*** ERROR: Bad huffman code @ 0x00008803.1 +*** ERROR: Bad scan data in MCU(0,15): Chr(Cr) CSS(0,0) @ Offset 0x00008803.2 + MCU located at pixel=(0,240) +*** ERROR: Can't find huffman bitstring @ 0x00008803.3, table 0, value [0xfffffff8] +*** ERROR: Bad huffman code @ 0x00008803.2 + Only reported first 20 instances of this message... + + Compression stats: + Compression Ratio: 14.80:1 + Bits per pixel: 1.62:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 40 ( 1%) + # codes of length 02 bits: 202 ( 4%) + # codes of length 03 bits: 3515 ( 73%) + # codes of length 04 bits: 423 ( 9%) + # codes of length 05 bits: 338 ( 7%) + # codes of length 06 bits: 228 ( 5%) + # codes of length 07 bits: 54 ( 1%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 20 ( 1%) + # codes of length 02 bits: 1657 ( 69%) + # codes of length 03 bits: 311 ( 13%) + # codes of length 04 bits: 232 ( 10%) + # codes of length 05 bits: 123 ( 5%) + # codes of length 06 bits: 49 ( 2%) + # codes of length 07 bits: 8 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 32135 ( 43%) + # codes of length 03 bits: 8668 ( 12%) + # codes of length 04 bits: 15771 ( 21%) + # codes of length 05 bits: 7559 ( 10%) + # codes of length 06 bits: 2518 ( 3%) + # codes of length 07 bits: 3834 ( 5%) + # codes of length 08 bits: 1387 ( 2%) + # codes of length 09 bits: 1122 ( 2%) + # codes of length 10 bits: 562 ( 1%) + # codes of length 11 bits: 234 ( 0%) + # codes of length 12 bits: 131 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 57 ( 0%) + # codes of length 16 bits: 286 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 4525 ( 57%) + # codes of length 03 bits: 1153 ( 14%) + # codes of length 04 bits: 1341 ( 17%) + # codes of length 05 bits: 543 ( 7%) + # codes of length 06 bits: 281 ( 4%) + # codes of length 07 bits: 14 ( 0%) + # codes of length 08 bits: 93 ( 1%) + # codes of length 09 bits: 23 ( 0%) + # codes of length 10 bits: 3 ( 0%) + # codes of length 11 bits: 3 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 2 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[103] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 1014, -3, -27] RGB=[248,255,252] @ MCU[ 0, 13] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 1 + Next position in scan buffer: Offset 0x0001210E.0 + + +*** Skipped 10 marker pad bytes *** +*** Marker: RST# *** + OFFSET: 0x0000880D + WARNING: Restart marker [0xFFD0] detected outside scan + Stopping decode + Use [Img Search Fwd/Rev] to locate other valid embedded JPEGs + +*** Searching Compression Signatures *** + + Signature: 013BA18D5561625796E986FDBC09F846 + Signature (Rotated): 01AC57E12793DFA7C46C704625C5AF0F + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[??? ] [Treo 680 ] [ ] Yes + CAM:[Canon ] [Canon PowerShot Pro1 ] [fine ] No + CAM:[NIKON ] [E2500 ] [FINE ] No + CAM:[NIKON ] [E3100 ] [FINE ] No + CAM:[NIKON ] [E4500 ] [FINE ] No + CAM:[NIKON ] [E5000 ] [FINE ] No + CAM:[NIKON ] [E5700 ] [FINE ] No + CAM:[NIKON ] [E775 ] [FINE ] No + CAM:[NIKON ] [E885 ] [FINE ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C3040Z ] [ ] No + CAM:[PENTAX ] [PENTAX Optio 550 ] [ ] No + CAM:[Research In Motion ] [BlackBerry 9530 ] [Superfine ] Yes + CAM:[SEIKO EPSON CORP. ] [PhotoPC 3000Z ] [ ] No + CAM:[SONY ] [DSC-H7 ] [ ] No + CAM:[SONY ] [DSC-H9 ] [ ] No + CAM:[SONY ] [DSC-S90 ] [ ] No + CAM:[SONY ] [DSC-W1 ] [ ] No + CAM:[SONY ] [SONY ] [ ] No + SW :[ACDSee ] [ ] + SW :[FixFoto ] [fine ] + SW :[IJG Library ] [090 ] + SW :[ZoomBrowser EX ] [high ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [090 ] + SW :[IrfanView ] [090 ] + SW :[idImager ] [090 ] + SW :[FastStone Image Viewer ] [090 ] + SW :[NeatImage ] [090 ] + SW :[Paint.NET ] [090 ] + SW :[Photomatix ] [090 ] + SW :[XnView ] [090 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + + +*** Additional Info *** +NOTE: Data exists after EOF, range: 0x00000000-0x00012301 (74497 bytes) diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/cmyk.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/cmyk.jpg.txt new file mode 100644 index 0000000000..68777cc049 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/cmyk.jpg.txt @@ -0,0 +1,435 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\cmyk.jpg] + Filesize: [2531270] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 300 x 300 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: APP14 (xFFEE) *** + OFFSET: 0x00000014 + Length = 14 + DCTEncodeVersion = 100 + APP14Flags0 = 0 + APP14Flags1 = 0 + ColorTransform = 0 [Unknown (RGB or CMYK)] + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x00000024 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 1 of 29 + Profile Size : 1829080 bytes + Preferred CMM Type : 'HDM ' (0x48444D20) + Profile Version : 0.2.4.0 (0x02400000) + Profile Device/Class : Output Device profile ('prtr' (0x70727472)) + Data Colour Space : cmykData ('CMYK' (0x434D594B)) + Profile connection space (PCS) : 'Lab ' (0x4C616220) + Profile creation date : 2007-02-28 08:00:00 + Profile file signature : 'acsp' (0x61637370) + Primary platform : ? (0x00000000) ('....' (0x00000000)) + Profile flags : 0x00000000 + Profile flags > Profile not embedded + Profile flags > Profile can't be used independently of embedded + Device Manufacturer : '....' (0x00000000) + Device Model : '....' (0x00000000) + Device attributes : 0x00000000_00000000 + Device attributes > Reflective + Device attributes > Glossy + Device attributes > Media polarity = negative + Device attributes > Black & white media + Rendering intent : Media-Relative Colorimetric + Profile creator : 'HDM ' (0x48444D20) + Profile ID : 0x00000000_00000000_00000000_00000000 + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0000FE1E + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 2 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0001FC18 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 3 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0002FA12 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 4 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0003F80C + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 5 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0004F606 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 6 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0005F400 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 7 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0006F1FA + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 8 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0007EFF4 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 9 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0008EDEE + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 10 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0009EBE8 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 11 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x000AE9E2 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 12 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x000BE7DC + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 13 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x000CE5D6 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 14 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x000DE3D0 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 15 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x000EE1CA + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 16 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x000FDFC4 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 17 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0010DDBE + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 18 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0011DBB8 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 19 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0012D9B2 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 20 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0013D7AC + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 21 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0014D5A6 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 22 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0015D3A0 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 23 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0016D19A + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 24 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0017CF94 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 25 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0018CD8E + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 26 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0019CB88 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 27 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x001AC982 + Length = 65016 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 28 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x001BC77C + Length = 9096 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 29 of 29 + Only support decode of 1st ICC Marker + +*** Marker: APP13 (xFFED) *** + OFFSET: 0x001BEB06 + Length = 188 + Identifier = [Photoshop 3.0] + 8BIM: [0x0404] Name="" Len=[0x009F] DefinedName="IPTC-NAA record" + IPTC [002:025] Keywords = "jpeg, productbeelden, productie, ptc, ptc369x1, pulsar" + IPTC [002:210] ? = ??? + IPTC [002:211] ? = ??? + IPTC [002:212] ? = ??? + IPTC [002:213] ? = ??? + IPTC [002:214] ? = ??? + IPTC [002:215] ? = ??? + IPTC [002:216] ? = ??? + IPTC [002:217] ? = ??? + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x001BEBC4 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 1 1 1 1 1 1 1 1 + DQT, Row #1: 1 1 1 1 1 1 1 1 + DQT, Row #2: 1 1 1 1 1 1 1 1 + DQT, Row #3: 1 1 1 1 1 1 1 1 + DQT, Row #4: 1 1 1 1 1 1 1 1 + DQT, Row #5: 1 1 1 1 1 1 1 1 + DQT, Row #6: 1 1 1 1 1 1 1 1 + DQT, Row #7: 1 1 1 1 1 1 1 1 + Approx quality factor = 100.00 (scaling=2.99 variance=6.13) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x001BEC09 + Frame header length = 20 + Precision = 8 + Number of Lines = 900 + Samples per Line = 414 + Image Size = 414 x 900 + Raw Image Orientation = Portrait + Number of Img components = 4 + Component[1]: ID=0x43, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Y) + Component[2]: ID=0x4D, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Cb) + Component[3]: ID=0x59, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Cr) + Component[4]: ID=0x4B, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (K) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x001BEC1F + Huffman table length = 31 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (005 total): 01 02 03 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (001 total): 0A + Codes of length 09 bits (001 total): 0B + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x001BEC40 + Huffman table length = 181 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 05 12 21 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 06 13 51 61 + Codes of length 08 bits (003 total): 07 22 71 + Codes of length 09 bits (005 total): 14 32 81 91 A1 + Codes of length 10 bits (005 total): 08 23 42 B1 C1 + Codes of length 11 bits (004 total): 15 52 D1 F0 + Codes of length 12 bits (004 total): 24 33 62 72 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (001 total): 82 + Codes of length 16 bits (125 total): 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 + 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 + 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 + 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 + 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 + B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA + D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 + E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x001BECF7 + Scan header length = 14 + Number of img components = 4 + Component[1]: selector=0x43, table=0(DC),0(AC) + Component[2]: selector=0x4D, table=0(DC),0(AC) + Component[3]: selector=0x59, table=0(DC),0(AC) + Component[4]: selector=0x4B, table=0(DC),0(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support CMYK files yet. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x00269FC4 + + +*** Searching Compression Signatures *** + + Signature: 01BC2BB6764A7F9709F829E766D93AAE + Signature (Rotated): 01BC2BB6764A7F9709F829E766D93AAE + File Offset: 0 bytes + Chroma subsampling: ?x? + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + SW :[IJG Library ] [100 Gray ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [100 Gray ] + SW :[IrfanView ] [100 Gray ] + SW :[idImager ] [100 Gray ] + SW :[FastStone Image Viewer ] [100 Gray ] + SW :[NeatImage ] [100 Gray ] + SW :[Paint.NET ] [100 Gray ] + SW :[Photomatix ] [100 Gray ] + SW :[XnView ] [100 Gray ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/exif.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/exif.jpg.txt new file mode 100644 index 0000000000..df54994c5e --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/exif.jpg.txt @@ -0,0 +1,454 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\exif.jpg] + Filesize: [32764] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 72 x 72 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000014 + Length = 7678 + Identifier = [Exif] + Identifier TIFF = 0x[49492A00 08000000] + Endian = Intel (little) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000026 + Dir Length = 0x0009 + [Make ] = "Canon" + [Model ] = "Canon PowerShot S40" + [Orientation ] = 1 = Row 0: top, Col 0: left + [XResolution ] = 180/1 + [YResolution ] = 180/1 + [ResolutionUnit ] = Inch + [DateTime ] = "2003:12:14 12:01:44" + [YCbCrPositioning ] = Centered + [ExifOffset ] = @ 0x00C4 + Offset to Next IFD = 0x000005BE + + EXIF IFD1 @ Absolute 0x000005DC + Dir Length = 0x0006 + [Compression ] = JPEG + [XResolution ] = 180/1 + [YResolution ] = 180/1 + [ResolutionUnit ] = Inch + [JpegIFOffset ] = @ +0x07F4 = @ 0x0812 + [JpegIFByteCount ] = 0x[00001548] / 5448 + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x000000E2 + Dir Length = 0x001F + [ExposureTime ] = 1/500 s + [FNumber ] = F4.9 + [ExifVersion ] = 02.20 + [DateTimeOriginal ] = "2003:12:14 12:01:44" + [DateTimeDigitized ] = "2003:12:14 12:01:44" + [ComponentsConfiguration ] = [Y Cb Cr .] + [CompressedBitsPerPixel ] = 5/1 + [ShutterSpeedValue ] = 287/32 + [ApertureValue ] = 149/32 + [ExposureBiasValue ] = 0.00 eV + [MaxApertureValue ] = 194698/65536 + [MeteringMode ] = CenterWeightedAverage + [Flash ] = Flash did not fire + [FocalLength ] = 21 mm + [MakerNote ] = @ 0x03AE + [UserComment ] = "" + [FlashPixVersion ] = 01.00 + [ColorSpace ] = sRGB + [ExifImageWidth ] = 2272 + [ExifImageHeight ] = 1704 + [ExifInteroperabilityOffset ] = @ 0x0588 + [FocalPlaneXResolution ] = 2272000/280 + [FocalPlaneYResolution ] = 1704000/210 + [FocalPlaneResolutionUnit ] = Inch + [SensingMethod ] = One-chip color area sensor + [FileSource ] = DSC + [CustomRendered ] = Normal process + [ExposureMode ] = Auto exposure + [WhiteBalance ] = Auto white balance + [DigitalZoomRatio ] = 2272/2272 + [SceneCaptureType ] = Standard + + EXIF MakerIFD @ Absolute 0x000003CC + Makernote decode option not enabled. + + EXIF InteropIFD @ Absolute 0x000005A6 + Dir Length = 0x0004 + [InteroperabilityIndex ] = "R98" + [InteroperabilityVersion ] = 01.00 + [RelatedImageWidth ] = 2272 + [RelatedImageLength ] = 1704 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00001E14 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 8 6 5 8 12 20 26 31 + DQT, Row #1: 6 6 7 10 13 29 30 28 + DQT, Row #2: 7 7 8 12 20 29 35 28 + DQT, Row #3: 7 9 11 15 26 44 40 31 + DQT, Row #4: 9 11 19 28 34 55 52 39 + DQT, Row #5: 12 18 28 32 41 52 57 46 + DQT, Row #6: 25 32 39 44 52 61 60 51 + DQT, Row #7: 36 46 48 49 56 50 52 50 + Approx quality factor = 74.75 (scaling=50.51 variance=0.81) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00001E59 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 9 9 12 24 50 50 50 50 + DQT, Row #1: 9 11 13 33 50 50 50 50 + DQT, Row #2: 12 13 28 50 50 50 50 50 + DQT, Row #3: 24 33 50 50 50 50 50 50 + DQT, Row #4: 50 50 50 50 50 50 50 50 + DQT, Row #5: 50 50 50 50 50 50 50 50 + DQT, Row #6: 50 50 50 50 50 50 50 50 + DQT, Row #7: 50 50 50 50 50 50 50 50 + Approx quality factor = 74.74 (scaling=50.52 variance=0.19) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x00001E9E + Frame header length = 17 + Precision = 8 + Number of Lines = 360 + Samples per Line = 480 + Image Size = 480 x 360 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00001EB1 + Huffman table length = 28 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 03 + Codes of length 03 bits (005 total): 01 02 04 05 06 + Codes of length 04 bits (001 total): 00 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00001ECF + Huffman table length = 65 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (002 total): 12 21 + Codes of length 06 bits (004 total): 05 31 41 51 + Codes of length 07 bits (004 total): 13 22 61 71 + Codes of length 08 bits (004 total): 06 32 81 91 + Codes of length 09 bits (004 total): 14 42 A1 B1 + Codes of length 10 bits (004 total): 23 52 C1 D1 + Codes of length 11 bits (005 total): 07 15 33 62 E1 + Codes of length 12 bits (003 total): 43 72 F0 + Codes of length 13 bits (003 total): 24 92 F1 + Codes of length 14 bits (004 total): 16 34 53 82 + Codes of length 15 bits (003 total): 25 83 C2 + Codes of length 16 bits (000 total): + Total number of codes: 046 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00001F12 + Huffman table length = 26 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 007 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00001F2E + Huffman table length = 45 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (002 total): 02 11 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (004 total): 04 12 21 31 + Codes of length 06 bits (001 total): 41 + Codes of length 07 bits (004 total): 13 22 51 61 + Codes of length 08 bits (002 total): 32 71 + Codes of length 09 bits (002 total): 05 14 + Codes of length 10 bits (002 total): 23 91 + Codes of length 11 bits (001 total): F0 + Codes of length 12 bits (005 total): 33 42 81 A1 B1 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 026 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00001F5D + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x00001F6B + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x00007FFA.0 + + Compression stats: + Compression Ratio: 20.97:1 + Bits per pixel: 1.14:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 509 ( 18%) + # codes of length 03 bits: 1910 ( 69%) + # codes of length 04 bits: 249 ( 9%) + # codes of length 05 bits: 87 ( 3%) + # codes of length 06 bits: 5 ( 0%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 905 ( 66%) + # codes of length 03 bits: 213 ( 15%) + # codes of length 04 bits: 169 ( 12%) + # codes of length 05 bits: 84 ( 6%) + # codes of length 06 bits: 9 ( 1%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 14518 ( 48%) + # codes of length 03 bits: 2881 ( 10%) + # codes of length 04 bits: 6591 ( 22%) + # codes of length 05 bits: 2038 ( 7%) + # codes of length 06 bits: 2356 ( 8%) + # codes of length 07 bits: 926 ( 3%) + # codes of length 08 bits: 484 ( 2%) + # codes of length 09 bits: 220 ( 1%) + # codes of length 10 bits: 149 ( 0%) + # codes of length 11 bits: 76 ( 0%) + # codes of length 12 bits: 27 ( 0%) + # codes of length 13 bits: 14 ( 0%) + # codes of length 14 bits: 8 ( 0%) + # codes of length 15 bits: 3 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 2784 ( 53%) + # codes of length 03 bits: 1230 ( 24%) + # codes of length 04 bits: 370 ( 7%) + # codes of length 05 bits: 582 ( 11%) + # codes of length 06 bits: 94 ( 2%) + # codes of length 07 bits: 121 ( 2%) + # codes of length 08 bits: 26 ( 0%) + # codes of length 09 bits: 14 ( 0%) + # codes of length 10 bits: 6 ( 0%) + # codes of length 11 bits: 2 ( 0%) + # codes of length 12 bits: 5 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[122] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 1008, 9, 27] RGB=[255,251,255] @ MCU[ 15, 13] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x00007FF9.7 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x00007FFA + + +*** Embedded JPEG Thumbnail *** + Offset: 0x00000812 + Length: 0x00001548 (5448) + + * Embedded Thumb Marker: SOI + + * Embedded Thumb Marker: DQT + Length = 132 + ---- + Precision=8 bits + Destination ID=0 (Luminance, typically) + DQT, Row #0: 9 6 5 9 13 22 29 35 + DQT, Row #1: 6 6 8 11 15 33 34 30 + DQT, Row #2: 8 7 9 13 22 33 39 31 + DQT, Row #3: 8 9 12 16 28 49 45 34 + DQT, Row #4: 10 12 21 32 39 61 58 42 + DQT, Row #5: 13 19 31 36 45 58 63 51 + DQT, Row #6: 28 36 44 49 58 68 66 55 + DQT, Row #7: 41 52 54 55 62 56 57 54 + ---- + Precision=8 bits + Destination ID=1 (Chrominance, typically) + DQT, Row #0: 9 9 12 20 15 26 79 79 + DQT, Row #1: 9 10 12 10 26 26 79 79 + DQT, Row #2: 12 12 10 10 26 79 79 79 + DQT, Row #3: 20 10 10 26 79 79 79 79 + DQT, Row #4: 15 26 26 79 79 79 79 79 + DQT, Row #5: 26 26 79 79 79 79 79 79 + DQT, Row #6: 79 79 79 79 79 79 79 79 + DQT, Row #7: 79 79 79 79 79 79 79 79 + + * Embedded Thumb Marker: SOF + Frame header length = 17 + Precision = 8 + Number of Lines = 120 + Samples per Line = 160 + Image Size = 160 x 120 + + * Embedded Thumb Marker: DHT + Length = 418 + + * Embedded Thumb Marker: SOS + Skipping scan data + Skipped 4869 bytes + + * Embedded Thumb Marker: EOI + + * Embedded Thumb Signature: 01D91E583DD0037108266E42ED3A262C + +*** Searching Compression Signatures *** + + Signature: 0182408A81A4ABF04D4A34A8A5E98C58 + Signature (Rotated): 012D821C6AB210E2A753BE053B8F55D0 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: OK [Canon] [Canon PowerShot S40] + EXIF Makernotes: OK + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[SONY ] [CYBERSHOT U ] [ ] Yes + SW :[Adobe Photoshop 7.0 ] [Save As 07 ] + SW :[Apple Quicktime ] [0466-0467 ] + SW :[Digital Photo Professiona] [05 ] + SW :[IJG Library ] [075 ] + SW :[MS Paint ] [ ] + SW :[MS Visio ] [ ] + SW :[ZoomBrowser EX ] [low ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [075 ] + SW :[IrfanView ] [075 ] + SW :[idImager ] [075 ] + SW :[FastStone Image Viewer ] [075 ] + SW :[NeatImage ] [075 ] + SW :[Paint.NET ] [075 ] + SW :[Photomatix ] [075 ] + SW :[XnView ] [075 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 4 - Uncertain if processed or original + While the EXIF fields indicate original, no compression signatures + in the current database were found matching this make/model + + Appears to be new signature for known camera. + If the camera/software doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/gamma_dalai_lama_gray.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/gamma_dalai_lama_gray.jpg.txt new file mode 100644 index 0000000000..a5adb0247c --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/gamma_dalai_lama_gray.jpg.txt @@ -0,0 +1,339 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\gamma_dalai_lama_gray.jpg] + Filesize: [84887] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 72 x 72 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: COM (Comment) (xFFFE) *** + OFFSET: 0x00000014 + Comment length = 46 + Comment= Scaled 1:2 this image wrongly becomes gray. + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000044 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 1 1 1 1 1 1 1 1 + DQT, Row #1: 1 1 1 1 1 1 1 1 + DQT, Row #2: 1 1 1 1 1 1 1 1 + DQT, Row #3: 1 1 1 1 1 1 1 1 + DQT, Row #4: 1 1 1 1 1 1 1 1 + DQT, Row #5: 1 1 1 1 1 1 1 1 + DQT, Row #6: 1 1 1 1 1 1 1 1 + DQT, Row #7: 1 1 1 1 1 1 1 1 + Approx quality factor = 100.00 (scaling=2.99 variance=6.13) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000089 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 1 1 1 1 1 1 1 1 + DQT, Row #1: 1 1 1 1 1 1 1 1 + DQT, Row #2: 1 1 1 1 1 1 1 1 + DQT, Row #3: 1 1 1 1 1 1 1 1 + DQT, Row #4: 1 1 1 1 1 1 1 1 + DQT, Row #5: 1 1 1 1 1 1 1 1 + DQT, Row #6: 1 1 1 1 1 1 1 1 + DQT, Row #7: 1 1 1 1 1 1 1 1 + Approx quality factor = 100.00 (scaling=1.54 variance=1.58) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x000000CE + Frame header length = 17 + Precision = 8 + Number of Lines = 222 + Samples per Line = 258 + Image Size = 258 x 222 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000E1 + Huffman table length = 25 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (000 total): + Codes of length 03 bits (003 total): 01 02 03 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (000 total): + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 006 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000FC + Huffman table length = 111 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (002 total): 04 11 + Codes of length 05 bits (002 total): 05 21 + Codes of length 06 bits (007 total): 00 06 12 13 14 31 41 + Codes of length 07 bits (003 total): 22 51 61 + Codes of length 08 bits (007 total): 07 15 16 32 42 71 81 + Codes of length 09 bits (005 total): 08 23 52 62 91 + Codes of length 10 bits (008 total): 33 43 44 72 A1 A2 B1 E3 + Codes of length 11 bits (010 total): 17 24 53 54 55 66 82 92 95 C1 + Codes of length 12 bits (009 total): 18 25 28 34 45 56 63 64 65 + Codes of length 13 bits (014 total): 26 27 46 73 76 85 93 96 A3 A5 A6 C3 C7 D7 + Codes of length 14 bits (010 total): 83 86 B2 B3 B8 C6 D3 D6 E8 F0 + Codes of length 15 bits (011 total): 36 37 48 74 75 84 94 A8 B4 B6 D1 + Codes of length 16 bits (001 total): D2 + Total number of codes: 092 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000016D + Huffman table length = 27 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (001 total): 04 + Codes of length 06 bits (001 total): 05 + Codes of length 07 bits (001 total): 06 + Codes of length 08 bits (001 total): 07 + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 008 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000018A + Huffman table length = 121 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (003 total): 02 03 04 + Codes of length 04 bits (002 total): 05 11 + Codes of length 05 bits (002 total): 06 21 + Codes of length 06 bits (005 total): 07 08 12 13 31 + Codes of length 07 bits (009 total): 00 09 14 16 17 22 32 41 51 + Codes of length 08 bits (005 total): 0A 15 23 42 61 + Codes of length 09 bits (005 total): 18 24 33 52 71 + Codes of length 10 bits (002 total): 62 81 + Codes of length 11 bits (007 total): 19 34 43 53 72 82 91 + Codes of length 12 bits (007 total): 25 44 54 56 63 A1 A2 + Codes of length 13 bits (014 total): 1A 26 27 28 46 47 55 57 64 73 97 B1 E3 E6 + Codes of length 14 bits (007 total): 35 45 48 58 92 A4 C1 + Codes of length 15 bits (002 total): D8 29 + Codes of length 16 bits (031 total): 67 74 83 85 93 94 B2 36 68 87 88 98 D6 F0 37 39 + 65 66 75 78 84 96 A3 A6 A8 C3 C9 D1 D3 D9 DA + Total number of codes: 102 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000205 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x00000213 + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x00014B95.0 + + Compression stats: + Compression Ratio: 2.04:1 + Bits per pixel: 11.78:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 893 ( 97%) + # codes of length 02 bits: 0 ( 0%) + # codes of length 03 bits: 23 ( 2%) + # codes of length 04 bits: 4 ( 0%) + # codes of length 05 bits: 4 ( 0%) + # codes of length 06 bits: 0 ( 0%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 1728 ( 94%) + # codes of length 02 bits: 46 ( 2%) + # codes of length 03 bits: 30 ( 2%) + # codes of length 04 bits: 18 ( 1%) + # codes of length 05 bits: 14 ( 1%) + # codes of length 06 bits: 6 ( 0%) + # codes of length 07 bits: 4 ( 0%) + # codes of length 08 bits: 2 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 14381 ( 45%) + # codes of length 03 bits: 3819 ( 12%) + # codes of length 04 bits: 4489 ( 14%) + # codes of length 05 bits: 2199 ( 7%) + # codes of length 06 bits: 3836 ( 12%) + # codes of length 07 bits: 911 ( 3%) + # codes of length 08 bits: 1089 ( 3%) + # codes of length 09 bits: 398 ( 1%) + # codes of length 10 bits: 294 ( 1%) + # codes of length 11 bits: 190 ( 1%) + # codes of length 12 bits: 84 ( 0%) + # codes of length 13 bits: 75 ( 0%) + # codes of length 14 bits: 21 ( 0%) + # codes of length 15 bits: 11 ( 0%) + # codes of length 16 bits: 1 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 17100 ( 23%) + # codes of length 03 bits: 31709 ( 42%) + # codes of length 04 bits: 7638 ( 10%) + # codes of length 05 bits: 4203 ( 6%) + # codes of length 06 bits: 6465 ( 9%) + # codes of length 07 bits: 5462 ( 7%) + # codes of length 08 bits: 1472 ( 2%) + # codes of length 09 bits: 804 ( 1%) + # codes of length 10 bits: 206 ( 0%) + # codes of length 11 bits: 296 ( 0%) + # codes of length 12 bits: 156 ( 0%) + # codes of length 13 bits: 164 ( 0%) + # codes of length 14 bits: 47 ( 0%) + # codes of length 15 bits: 7 ( 0%) + # codes of length 16 bits: 55 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[125] (range: 0..255) + + Brightest Pixel Search: + YCC=[ -8, 0, 0] RGB=[127,127,127] @ MCU[ 0, 0] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x00014B94.4 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x00014B95 + + +*** Searching Compression Signatures *** + + Signature: 01BBB1709AC9C1F89220D955A31A8F34 + Signature (Rotated): 01BBB1709AC9C1F89220D955A31A8F34 + File Offset: 0 bytes + Chroma subsampling: 1x1 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[CASIO COMPUTER CO.,LTD ] [EX-Z750 ] [ ] No + CAM:[CASIO COMPUTER CO.,LTD. ] [EX-Z1000 ] [ ] No + CAM:[PENTAX ] [PENTAX Optio S5i ] [ ] No + CAM:[SIGMA ] [SIGMA SD9 ] [ ] No + SW :[ACDSee ] [100 ] + SW :[Apple ImageIO.framework ] [100 (Best) ] + SW :[Digital Photo Professiona] [10 ] + SW :[IJG Library ] [100 ] + SW :[MS Office Pic Mgr ] [ ] + SW :[Nikon Scan ] [Excellent Qualit] + SW :[Picasa ] [100 (Maximum) ] + SW :[ZoomBrowser EX ] [highest ] + SW :[EOS Viewer Utility ] [ ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [100 ] + SW :[IrfanView ] [100 ] + SW :[idImager ] [100 ] + SW :[FastStone Image Viewer ] [100 ] + SW :[NeatImage ] [100 ] + SW :[Paint.NET ] [100 ] + SW :[Photomatix ] [100 ] + SW :[XnView ] [100 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/jpeg400jfif.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/jpeg400jfif.jpg.txt new file mode 100644 index 0000000000..b686b3ce39 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/jpeg400jfif.jpg.txt @@ -0,0 +1,211 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\jpeg400jfif.jpg] + Filesize: [45066] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.2] + density = 300 x 300 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000014 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 6 4 4 6 9 11 12 16 + DQT, Row #1: 4 5 5 6 8 10 12 12 + DQT, Row #2: 4 5 5 6 10 12 12 12 + DQT, Row #3: 6 6 6 11 12 12 12 12 + DQT, Row #4: 9 8 10 12 12 12 12 12 + DQT, Row #5: 11 10 12 12 12 12 12 12 + DQT, Row #6: 12 12 12 12 12 12 12 12 + DQT, Row #7: 16 12 12 12 12 12 12 12 + Approx quality factor = 88.28 (scaling=23.43 variance=111.68) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x00000059 + Frame header length = 11 + Precision = 8 + Number of Lines = 800 + Samples per Line = 600 + Image Size = 600 x 800 + Raw Image Orientation = Portrait + Number of Img components = 1 + Component[1]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + +*** Marker: DRI (Restart Interval) (xFFDD) *** + OFFSET: 0x00000066 + Length = 4 + interval = 75 + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000006C + Huffman table length = 210 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (000 total): + Codes of length 03 bits (007 total): 04 05 03 02 06 01 00 + Codes of length 04 bits (001 total): 07 + Codes of length 05 bits (001 total): 08 + Codes of length 06 bits (001 total): 09 + Codes of length 07 bits (001 total): 0A + Codes of length 08 bits (001 total): 0B + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 11 04 00 + Codes of length 05 bits (003 total): 05 21 12 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 51 06 13 61 + Codes of length 08 bits (002 total): 22 71 + Codes of length 09 bits (006 total): 81 14 32 91 A1 07 + Codes of length 10 bits (007 total): 15 B1 42 23 C1 52 D1 + Codes of length 11 bits (003 total): E1 33 16 + Codes of length 12 bits (004 total): 62 F0 24 72 + Codes of length 13 bits (002 total): 82 F1 + Codes of length 14 bits (006 total): 25 43 34 53 92 A2 + Codes of length 15 bits (002 total): B2 63 + Codes of length 16 bits (115 total): 73 C2 35 44 27 93 A3 B3 36 17 54 64 74 C3 D2 E2 + 08 26 83 09 0A 18 19 84 94 45 46 A4 B4 56 D3 55 + 28 1A F2 E3 F3 C4 D4 E4 F4 65 75 85 95 A5 B5 C5 + D5 E5 F5 66 76 86 96 A6 B6 C6 D6 E6 F6 37 47 57 + 67 77 87 97 A7 B7 C7 D7 E7 F7 38 48 58 68 78 88 + 98 A8 B8 C8 D8 E8 F8 29 39 49 59 69 79 89 99 A9 + B9 C9 D9 E9 F9 2A 3A 4A 5A 6A 7A 8A 9A AA BA CA + DA EA FA + Total number of codes: 162 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000140 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x0000014A + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x0000B008.0 + + Compression stats: + Compression Ratio: 10.73:1 + Bits per pixel: 0.75:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 0 ( 0%) + # codes of length 03 bits: 7215 ( 96%) + # codes of length 04 bits: 146 ( 2%) + # codes of length 05 bits: 139 ( 2%) + # codes of length 06 bits: 0 ( 0%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 26194 ( 42%) + # codes of length 03 bits: 2772 ( 4%) + # codes of length 04 bits: 16626 ( 27%) + # codes of length 05 bits: 5914 ( 9%) + # codes of length 06 bits: 4996 ( 8%) + # codes of length 07 bits: 2599 ( 4%) + # codes of length 08 bits: 988 ( 2%) + # codes of length 09 bits: 1360 ( 2%) + # codes of length 10 bits: 617 ( 1%) + # codes of length 11 bits: 60 ( 0%) + # codes of length 12 bits: 152 ( 0%) + # codes of length 13 bits: 60 ( 0%) + # codes of length 14 bits: 30 ( 0%) + # codes of length 15 bits: 5 ( 0%) + # codes of length 16 bits: 28 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[ 58] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 894, 0, 0] RGB=[239,239,239] @ MCU[ 35, 23] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 99 + Next position in scan buffer: Offset 0x0000B007.2 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0000B008 + + +*** Searching Compression Signatures *** + + Signature: 01BE82BEB1019CB30EB122273E78E87C + Signature (Rotated): 01BE82BEB1019CB30EB122273E78E87C + File Offset: 0 bytes + Chroma subsampling: Gray + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/jpeg420exif.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/jpeg420exif.jpg.txt new file mode 100644 index 0000000000..babe797d6a --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/jpeg420exif.jpg.txt @@ -0,0 +1,412 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\jpeg420exif.jpg] + Filesize: [768608] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000002 + Length = 8817 + Identifier = [Exif] + Identifier TIFF = 0x[49492A00 08000000] + Endian = Intel (little) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000014 + Dir Length = 0x0008 + [Make ] = "Hewlett-Packard Company" + [Model ] = "HP PhotoSmart 715" + [Orientation ] = 1 = Row 0: top, Col 0: left + [XResolution ] = 72/1 + [YResolution ] = 72/1 + [ResolutionUnit ] = Inch + [YCbCrPositioning ] = Centered + [ExifOffset ] = @ 0x006E + Offset to Next IFD = 0x0000018E + + EXIF IFD1 @ Absolute 0x0000019A + Dir Length = 0x0007 + [Compression ] = JPEG + [XResolution ] = 72/1 + [YResolution ] = 72/1 + [ResolutionUnit ] = Inch + [JpegIFOffset ] = @ +0x0C07 = @ 0x0C13 + [JpegIFByteCount ] = 0x[00001658] / 5720 + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x0000007A + Dir Length = 0x0015 + [ExposureTime ] = 48/10000 s + [FNumber ] = F8.2 + [ISOSpeedRatings ] = 100 + [ExifVersion ] = 02.10 + [DateTimeOriginal ] = "2001:10:02 14:57:31" + [DateTimeDigitized ] = "2001:10:02 14:57:31" + [ComponentsConfiguration ] = [Y Cb Cr .] + [CompressedBitsPerPixel ] = 20/10 + [ExposureBiasValue ] = 0.00 eV + [MaxApertureValue ] = 20/10 + [SubjectDistance ] = 5043/1000 + [MeteringMode ] = CenterWeightedAverage + [LightSource ] = unknown + [Flash ] = Flash did not fire + [FocalLength ] = 7 mm + [MakerNote ] = @ 0x0282 + [FlashPixVersion ] = 01.00 + [ColorSpace ] = sRGB + [ExifImageWidth ] = 2048 + [ExifImageHeight ] = 1536 + [ExifInteroperabilityOffset ] = @ 0x0170 + + EXIF MakerIFD @ Absolute 0x0000028E + Makernote decode option not enabled. + + EXIF InteropIFD @ Absolute 0x0000017C + Dir Length = 0x0002 + [InteroperabilityIndex ] = "R98" + [InteroperabilityVersion ] = 01.00 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00002275 + Table length = 132 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 3 2 2 3 5 9 11 14 + DQT, Row #1: 3 3 3 4 5 12 13 12 + DQT, Row #2: 3 3 3 5 9 12 15 12 + DQT, Row #3: 3 3 5 6 11 19 18 13 + DQT, Row #4: 3 5 7 12 15 24 23 17 + DQT, Row #5: 5 7 12 14 18 23 25 21 + DQT, Row #6: 11 14 17 19 23 27 27 22 + DQT, Row #7: 16 21 21 22 25 22 23 22 + Approx quality factor = 89.24 (scaling=21.52 variance=2.21) + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 3 3 5 10 22 22 22 22 + DQT, Row #1: 3 5 5 14 22 22 22 22 + DQT, Row #2: 5 5 11 22 22 22 22 22 + DQT, Row #3: 10 14 22 22 22 22 22 22 + DQT, Row #4: 22 22 22 22 22 22 22 22 + DQT, Row #5: 22 22 22 22 22 22 22 22 + DQT, Row #6: 22 22 22 22 22 22 22 22 + DQT, Row #7: 22 22 22 22 22 22 22 22 + Approx quality factor = 89.12 (scaling=21.76 variance=1.62) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000022FB + Huffman table length = 418 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (005 total): 01 02 03 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (001 total): 0A + Codes of length 09 bits (001 total): 0B + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (001 total): 07 + Codes of length 08 bits (001 total): 08 + Codes of length 09 bits (001 total): 09 + Codes of length 10 bits (001 total): 0A + Codes of length 11 bits (001 total): 0B + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 05 12 21 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 06 13 51 61 + Codes of length 08 bits (003 total): 07 22 71 + Codes of length 09 bits (005 total): 14 32 81 91 A1 + Codes of length 10 bits (005 total): 08 23 42 B1 C1 + Codes of length 11 bits (004 total): 15 52 D1 F0 + Codes of length 12 bits (004 total): 24 33 62 72 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (001 total): 82 + Codes of length 16 bits (125 total): 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 + 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 + 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 + 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 + 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 + B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA + D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 + E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (002 total): 03 11 + Codes of length 05 bits (004 total): 04 05 21 31 + Codes of length 06 bits (004 total): 06 12 41 51 + Codes of length 07 bits (003 total): 07 61 71 + Codes of length 08 bits (004 total): 13 22 32 81 + Codes of length 09 bits (007 total): 08 14 42 91 A1 B1 C1 + Codes of length 10 bits (005 total): 09 23 33 52 F0 + Codes of length 11 bits (004 total): 15 62 72 D1 + Codes of length 12 bits (004 total): 0A 16 24 34 + Codes of length 13 bits (000 total): + Codes of length 14 bits (001 total): E1 + Codes of length 15 bits (002 total): 25 F1 + Codes of length 16 bits (119 total): 17 18 19 1A 26 27 28 29 2A 35 36 37 38 39 3A 43 + 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 + 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 82 + 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 + 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 + B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 + D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 + F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x0000249F + Frame header length = 17 + Precision = 8 + Number of Lines = 1536 + Samples per Line = 2048 + Image Size = 2048 x 1536 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000024B2 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x000024C0 + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x000BBA5E.0 + + Compression stats: + Compression Ratio: 12.43:1 + Bits per pixel: 1.93:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 4275 ( 9%) + # codes of length 03 bits: 32688 ( 67%) + # codes of length 04 bits: 5786 ( 12%) + # codes of length 05 bits: 3984 ( 8%) + # codes of length 06 bits: 2042 ( 4%) + # codes of length 07 bits: 377 ( 1%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 16194 ( 66%) + # codes of length 03 bits: 3495 ( 14%) + # codes of length 04 bits: 2416 ( 10%) + # codes of length 05 bits: 1460 ( 6%) + # codes of length 06 bits: 736 ( 3%) + # codes of length 07 bits: 261 ( 1%) + # codes of length 08 bits: 14 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 363342 ( 41%) + # codes of length 03 bits: 113855 ( 13%) + # codes of length 04 bits: 200569 ( 23%) + # codes of length 05 bits: 102577 ( 12%) + # codes of length 06 bits: 32874 ( 4%) + # codes of length 07 bits: 43593 ( 5%) + # codes of length 08 bits: 14054 ( 2%) + # codes of length 09 bits: 8847 ( 1%) + # codes of length 10 bits: 4365 ( 0%) + # codes of length 11 bits: 2009 ( 0%) + # codes of length 12 bits: 770 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 17 ( 0%) + # codes of length 16 bits: 780 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 60258 ( 49%) + # codes of length 03 bits: 17570 ( 14%) + # codes of length 04 bits: 20881 ( 17%) + # codes of length 05 bits: 14001 ( 11%) + # codes of length 06 bits: 6443 ( 5%) + # codes of length 07 bits: 842 ( 1%) + # codes of length 08 bits: 1737 ( 1%) + # codes of length 09 bits: 387 ( 0%) + # codes of length 10 bits: 169 ( 0%) + # codes of length 11 bits: 93 ( 0%) + # codes of length 12 bits: 31 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 2 ( 0%) + # codes of length 15 bits: 5 ( 0%) + # codes of length 16 bits: 1 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[108] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 996, 0, -33] RGB=[244,255,252] @ MCU[ 32, 59] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x000BBA5D.5 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x000BBA5E + + +*** Embedded JPEG Thumbnail *** + Offset: 0x00000C13 + Length: 0x00001658 (5720) + + * Embedded Thumb Marker: SOI + + * Embedded Thumb Marker: DQT + Length = 132 + ---- + Precision=8 bits + Destination ID=0 (Luminance, typically) + DQT, Row #0: 10 6 6 10 14 26 32 40 + DQT, Row #1: 8 8 8 12 16 36 38 36 + DQT, Row #2: 8 8 10 14 26 36 44 36 + DQT, Row #3: 8 10 14 18 32 56 52 40 + DQT, Row #4: 10 14 22 36 44 70 66 50 + DQT, Row #5: 14 22 36 42 52 68 74 60 + DQT, Row #6: 32 42 50 56 66 78 78 64 + DQT, Row #7: 46 60 62 64 74 64 66 64 + ---- + Precision=8 bits + Destination ID=1 (Chrominance, typically) + DQT, Row #0: 10 10 14 30 64 64 64 64 + DQT, Row #1: 10 14 16 42 64 64 64 64 + DQT, Row #2: 14 16 36 64 64 64 64 64 + DQT, Row #3: 30 42 64 64 64 64 64 64 + DQT, Row #4: 64 64 64 64 64 64 64 64 + DQT, Row #5: 64 64 64 64 64 64 64 64 + DQT, Row #6: 64 64 64 64 64 64 64 64 + DQT, Row #7: 64 64 64 64 64 64 64 64 + + * Embedded Thumb Marker: DHT + Length = 418 + + * Embedded Thumb Marker: SOF + Frame header length = 17 + Precision = 8 + Number of Lines = 120 + Samples per Line = 160 + Image Size = 160 x 120 + + * Embedded Thumb Marker: SOS + Skipping scan data + Skipped 5141 bytes + + * Embedded Thumb Marker: EOI + + * Embedded Thumb Signature: 0158E595F22440126FB766B33F56B158 + +*** Searching Compression Signatures *** + + Signature: 010A5B03EB73D6AF719B39FCC8C3AE25 + Signature (Rotated): 011326BE69D2A27FCF4DBCC33DEB07A2 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: OK [Hewlett-Packard Company] [HP PhotoSmart 715] + EXIF Makernotes: OK + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 4 - Uncertain if processed or original + While the EXIF fields indicate original, no compression signatures + in the current database were found matching this make/model + + Appears to be new signature for known camera. + If the camera/software doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/jpeg420small.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/jpeg420small.jpg.txt new file mode 100644 index 0000000000..c001d7297a --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/jpeg420small.jpg.txt @@ -0,0 +1,330 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\jpeg420small.jpg] + Filesize: [5276] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 96 x 96 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000014 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 5 3 3 5 7 12 15 18 + DQT, Row #1: 4 4 4 6 8 17 18 17 + DQT, Row #2: 4 4 5 7 12 17 21 17 + DQT, Row #3: 4 5 7 9 15 26 24 19 + DQT, Row #4: 5 7 11 17 20 33 31 23 + DQT, Row #5: 7 11 17 19 24 31 34 28 + DQT, Row #6: 15 19 23 26 31 36 36 30 + DQT, Row #7: 22 28 29 29 34 30 31 30 + Approx quality factor = 84.93 (scaling=30.13 variance=1.05) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000059 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 5 5 7 14 30 30 30 30 + DQT, Row #1: 5 6 8 20 30 30 30 30 + DQT, Row #2: 7 8 17 30 30 30 30 30 + DQT, Row #3: 14 20 30 30 30 30 30 30 + DQT, Row #4: 30 30 30 30 30 30 30 30 + DQT, Row #5: 30 30 30 30 30 30 30 30 + DQT, Row #6: 30 30 30 30 30 30 30 30 + DQT, Row #7: 30 30 30 30 30 30 30 30 + Approx quality factor = 84.93 (scaling=30.15 variance=0.29) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x0000009E + Frame header length = 17 + Precision = 8 + Number of Lines = 100 + Samples per Line = 200 + Image Size = 200 x 100 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000B1 + Huffman table length = 28 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 04 + Codes of length 03 bits (005 total): 00 02 03 05 07 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 01 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000CF + Huffman table length = 66 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 11 21 + Codes of length 05 bits (002 total): 04 12 + Codes of length 06 bits (004 total): 05 06 31 41 + Codes of length 07 bits (004 total): 07 13 22 51 + Codes of length 08 bits (002 total): 32 61 + Codes of length 09 bits (005 total): 71 72 B1 D1 D2 + Codes of length 10 bits (007 total): 14 23 33 62 81 91 A1 + Codes of length 11 bits (010 total): 15 16 24 25 34 42 44 82 92 A2 + Codes of length 12 bits (007 total): 26 52 53 54 64 94 C2 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 047 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000113 + Huffman table length = 28 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 06 07 + Codes of length 03 bits (002 total): 04 05 + Codes of length 04 bits (003 total): 00 03 08 + Codes of length 05 bits (001 total): 01 + Codes of length 06 bits (001 total): 02 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000131 + Huffman table length = 63 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (002 total): 00 02 + Codes of length 04 bits (002 total): 03 04 + Codes of length 05 bits (004 total): 05 06 11 21 + Codes of length 06 bits (007 total): 07 12 34 51 61 72 B1 + Codes of length 07 bits (014 total): 13 15 16 17 31 32 33 35 41 53 71 91 92 D1 + Codes of length 08 bits (004 total): 22 62 C1 E1 + Codes of length 09 bits (006 total): 14 52 54 81 82 B2 + Codes of length 10 bits (003 total): 36 42 C2 + Codes of length 11 bits (001 total): F0 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 044 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000172 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x00000180 + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x0000149A.0 + + Compression stats: + Compression Ratio: 12.27:1 + Bits per pixel: 1.96:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 69 ( 19%) + # codes of length 03 bits: 232 ( 64%) + # codes of length 04 bits: 34 ( 9%) + # codes of length 05 bits: 19 ( 5%) + # codes of length 06 bits: 10 ( 3%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 76 ( 42%) + # codes of length 03 bits: 60 ( 33%) + # codes of length 04 bits: 36 ( 20%) + # codes of length 05 bits: 5 ( 3%) + # codes of length 06 bits: 5 ( 3%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 2698 ( 51%) + # codes of length 03 bits: 506 ( 10%) + # codes of length 04 bits: 1064 ( 20%) + # codes of length 05 bits: 373 ( 7%) + # codes of length 06 bits: 334 ( 6%) + # codes of length 07 bits: 133 ( 3%) + # codes of length 08 bits: 37 ( 1%) + # codes of length 09 bits: 43 ( 1%) + # codes of length 10 bits: 33 ( 1%) + # codes of length 11 bits: 26 ( 0%) + # codes of length 12 bits: 7 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 276 ( 20%) + # codes of length 03 bits: 340 ( 25%) + # codes of length 04 bits: 214 ( 15%) + # codes of length 05 bits: 190 ( 14%) + # codes of length 06 bits: 141 ( 10%) + # codes of length 07 bits: 170 ( 12%) + # codes of length 08 bits: 29 ( 2%) + # codes of length 09 bits: 17 ( 1%) + # codes of length 10 bits: 6 ( 0%) + # codes of length 11 bits: 1 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[ 86] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 250, -405, 230] RGB=[198,156, 68] @ MCU[ 0, 6] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x00001499.3 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0000149A + + +*** Searching Compression Signatures *** + + Signature: 0155D875C95B74D0F3C5835A62516F48 + Signature (Rotated): 01D38A25358EB7649A254E19F1D46600 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[NIKON ] [E2500 ] [FINE ] No + CAM:[Nokia ] [N73 ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C2000Z ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C3040Z ] [ ] No + CAM:[PENTAX ] [PENTAX Optio 550 ] [ ] No + CAM:[Research In Motion ] [BlackBerry 8100 ] [ ] No + CAM:[SEIKO EPSON CORP. ] [PhotoPC 3000Z ] [ ] No + SW :[IJG Library ] [085 ] + SW :[Picasa ] [085 (Normal) ] + SW :[ZoomBrowser EX ] [medium ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [085 ] + SW :[IrfanView ] [085 ] + SW :[idImager ] [085 ] + SW :[FastStone Image Viewer ] [085 ] + SW :[NeatImage ] [085 ] + SW :[Paint.NET ] [085 ] + SW :[Photomatix ] [085 ] + SW :[XnView ] [085 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/jpeg444.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/jpeg444.jpg.txt new file mode 100644 index 0000000000..06a1ee4263 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/jpeg444.jpg.txt @@ -0,0 +1,405 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\jpeg444.jpg] + Filesize: [5667] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.0] + density = 300 x 300 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000014 + Length = 68 + Identifier = [This is an unknown APP marker. Compliant decoders must ignore it.] +Identifier [This is an unknown APP marker. Compliant decoders must ignore it.] not supported. Skipping remainder. + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0000005A + Length = 68 + Identifier = [This is an unknown APP marker. Compliant decoders must ignore it.] + Not supported. Skipping remainder. + +*** Marker: APP3 (xFFE3) *** + OFFSET: 0x000000A0 + Length = 68 + +*** Marker: APP4 (xFFE4) *** + OFFSET: 0x000000E6 + Length = 68 + +*** Marker: APP5 (xFFE5) *** + OFFSET: 0x0000012C + Length = 68 + +*** Marker: APP6 (xFFE6) *** + OFFSET: 0x00000172 + Length = 68 + +*** Marker: APP7 (xFFE7) *** + OFFSET: 0x000001B8 + Length = 68 + +*** Marker: APP8 (xFFE8) *** + OFFSET: 0x000001FE + Length = 68 + +*** Marker: APP9 (xFFE9) *** + OFFSET: 0x00000244 + Length = 68 + +*** Marker: APP10 (xFFEA) *** + OFFSET: 0x0000028A + Length = 68 + +*** Marker: APP11 (xFFEB) *** + OFFSET: 0x000002D0 + Length = 68 + +*** Marker: APP12 (xFFEC) *** + OFFSET: 0x00000316 + Length = 68 + Identifier = [This is an unknown APP marker. Compliant decoders must ignore it.] + Not Photoshop DUCKY. Skipping remainder. + +*** Marker: APP13 (xFFED) *** + OFFSET: 0x0000035C + Length = 68 + Identifier = [This is an unknown APP marker. Compliant decoders must ignore it.] + Not Photoshop. Skipping remainder. + +*** Marker: APP14 (xFFEE) *** + OFFSET: 0x000003A2 + Length = 68 + DCTEncodeVersion = 26995 + APP14Flags0 = 8289 + APP14Flags1 = 28192 + ColorTransform = 117 [???] + +*** Marker: APP15 (xFFEF) *** + OFFSET: 0x000003E8 + Length = 68 + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x0000042E + Frame header length = 17 + Precision = 8 + Number of Lines = 256 + Samples per Line = 256 + Image Size = 256 x 256 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x00, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x02, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x02 (Chrom: Cr) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000441 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 8 6 5 8 12 20 26 31 + DQT, Row #1: 6 6 7 10 13 29 30 28 + DQT, Row #2: 7 7 8 12 20 29 35 28 + DQT, Row #3: 7 9 11 15 26 44 40 31 + DQT, Row #4: 9 11 19 28 34 55 52 39 + DQT, Row #5: 12 18 28 32 41 52 57 46 + DQT, Row #6: 25 32 39 44 52 61 60 51 + DQT, Row #7: 36 46 48 49 56 50 52 50 + Approx quality factor = 74.75 (scaling=50.51 variance=0.81) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000486 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 9 9 12 24 50 50 50 50 + DQT, Row #1: 9 11 13 33 50 50 50 50 + DQT, Row #2: 12 13 28 50 50 50 50 50 + DQT, Row #3: 24 33 50 50 50 50 50 50 + DQT, Row #4: 50 50 50 50 50 50 50 50 + DQT, Row #5: 50 50 50 50 50 50 50 50 + DQT, Row #6: 50 50 50 50 50 50 50 50 + DQT, Row #7: 50 50 50 50 50 50 50 50 + Approx quality factor = 74.74 (scaling=50.52 variance=0.19) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x000004CB + Table length = 67 + ---- + Precision=8 bits + Destination ID=2 (Chrominance) + DQT, Row #0: 9 9 12 24 50 50 50 50 + DQT, Row #1: 9 11 13 33 50 50 50 50 + DQT, Row #2: 12 13 28 50 50 50 50 50 + DQT, Row #3: 24 33 50 50 50 50 50 50 + DQT, Row #4: 50 50 50 50 50 50 50 50 + DQT, Row #5: 50 50 50 50 50 50 50 50 + DQT, Row #6: 50 50 50 50 50 50 50 50 + DQT, Row #7: 50 50 50 50 50 50 50 50 + Approx quality factor = 74.74 (scaling=50.52 variance=0.19) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000510 + Huffman table length = 418 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (005 total): 01 02 03 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (001 total): 0A + Codes of length 09 bits (001 total): 0B + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 05 12 21 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 06 13 51 61 + Codes of length 08 bits (003 total): 07 22 71 + Codes of length 09 bits (005 total): 14 32 81 91 A1 + Codes of length 10 bits (005 total): 08 23 42 B1 C1 + Codes of length 11 bits (004 total): 15 52 D1 F0 + Codes of length 12 bits (004 total): 24 33 62 72 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (001 total): 82 + Codes of length 16 bits (125 total): 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 + 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 + 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 + 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 + 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 + B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA + D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 + E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (001 total): 07 + Codes of length 08 bits (001 total): 08 + Codes of length 09 bits (001 total): 09 + Codes of length 10 bits (001 total): 0A + Codes of length 11 bits (001 total): 0B + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (002 total): 03 11 + Codes of length 05 bits (004 total): 04 05 21 31 + Codes of length 06 bits (004 total): 06 12 41 51 + Codes of length 07 bits (003 total): 07 61 71 + Codes of length 08 bits (004 total): 13 22 32 81 + Codes of length 09 bits (007 total): 08 14 42 91 A1 B1 C1 + Codes of length 10 bits (005 total): 09 23 33 52 F0 + Codes of length 11 bits (004 total): 15 62 72 D1 + Codes of length 12 bits (004 total): 0A 16 24 34 + Codes of length 13 bits (000 total): + Codes of length 14 bits (001 total): E1 + Codes of length 15 bits (002 total): 25 F1 + Codes of length 16 bits (119 total): 17 18 19 1A 26 27 28 29 2A 35 36 37 38 39 3A 43 + 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 + 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 82 + 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 + 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 + B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 + D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 + F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000006B4 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x00, table=0(DC),0(AC) + Component[2]: selector=0x01, table=1(DC),1(AC) + Component[3]: selector=0x02, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x000006C2 + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x00001620.0 + + Compression stats: + Compression Ratio: 49.99:1 + Bits per pixel: 0.48:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 992 ( 97%) + # codes of length 03 bits: 31 ( 3%) + # codes of length 04 bits: 0 ( 0%) + # codes of length 05 bits: 1 ( 0%) + # codes of length 06 bits: 0 ( 0%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 1023 ( 50%) + # codes of length 03 bits: 864 ( 42%) + # codes of length 04 bits: 128 ( 6%) + # codes of length 05 bits: 0 ( 0%) + # codes of length 06 bits: 0 ( 0%) + # codes of length 07 bits: 2 ( 0%) + # codes of length 08 bits: 31 ( 2%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 0 ( 0%) + # codes of length 03 bits: 0 ( 0%) + # codes of length 04 bits: 1024 ( 50%) + # codes of length 05 bits: 1024 ( 50%) + # codes of length 06 bits: 0 ( 0%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 2048 ( 67%) + # codes of length 03 bits: 1024 ( 33%) + # codes of length 04 bits: 0 ( 0%) + # codes of length 05 bits: 0 ( 0%) + # codes of length 06 bits: 0 ( 0%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[126] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 984,-1026, -999] RGB=[ 75,255, 24] @ MCU[ 0, 31] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x0000161F.6 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x00001620 + + +*** Searching Compression Signatures *** + + Signature: 019DC7724B5425C464D28F2CF78F707E + Signature (Rotated): 016C4383FFABE35F063D8FCB331942C0 + File Offset: 0 bytes + Chroma subsampling: 1x1 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[NIKON ] [E4800 ] [NORMAL ] No + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + + +*** Additional Info *** +NOTE: Data exists after EOF, range: 0x00001622-0x00001623 (1 bytes) diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/ratio-1x1.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/ratio-1x1.jpg.txt new file mode 100644 index 0000000000..b2ae24c469 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/ratio-1x1.jpg.txt @@ -0,0 +1,338 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\ratio-1x1.jpg] + Filesize: [34674] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 1 x 1 (aspect ratio) + thumbnail = 0 x 0 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000014 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 3 2 2 3 4 6 8 10 + DQT, Row #1: 2 2 2 3 4 9 10 9 + DQT, Row #2: 2 2 3 4 6 9 11 9 + DQT, Row #3: 2 3 4 5 8 14 13 10 + DQT, Row #4: 3 4 6 9 11 17 16 12 + DQT, Row #5: 4 6 9 10 13 17 18 15 + DQT, Row #6: 8 10 12 14 16 19 19 16 + DQT, Row #7: 12 15 15 16 18 16 16 16 + Approx quality factor = 91.86 (scaling=16.28 variance=1.13) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000059 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 3 3 4 8 16 16 16 16 + DQT, Row #1: 3 3 4 11 16 16 16 16 + DQT, Row #2: 4 4 9 16 16 16 16 16 + DQT, Row #3: 8 11 16 16 16 16 16 16 + DQT, Row #4: 16 16 16 16 16 16 16 16 + DQT, Row #5: 16 16 16 16 16 16 16 16 + DQT, Row #6: 16 16 16 16 16 16 16 16 + DQT, Row #7: 16 16 16 16 16 16 16 16 + Approx quality factor = 91.90 (scaling=16.20 variance=0.15) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x0000009E + Frame header length = 17 + Precision = 8 + Number of Lines = 769 + Samples per Line = 1900 + Image Size = 1900 x 769 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000B1 + Huffman table length = 29 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 07 08 + Codes of length 04 bits (002 total): 06 09 + Codes of length 05 bits (002 total): 04 05 + Codes of length 06 bits (003 total): 01 02 03 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 010 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000D0 + Huffman table length = 78 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (000 total): + Codes of length 03 bits (001 total): 01 + Codes of length 04 bits (003 total): 02 03 04 + Codes of length 05 bits (003 total): 05 06 11 + Codes of length 06 bits (001 total): 07 + Codes of length 07 bits (005 total): 08 12 21 31 41 + Codes of length 08 bits (005 total): 13 14 22 51 61 + Codes of length 09 bits (003 total): 15 32 71 + Codes of length 10 bits (009 total): 09 16 23 52 62 81 91 92 A1 + Codes of length 11 bits (005 total): 17 33 42 72 82 + Codes of length 12 bits (005 total): 24 43 54 93 B3 + Codes of length 13 bits (006 total): 63 73 83 A2 B2 C2 + Codes of length 14 bits (003 total): 25 44 A3 + Codes of length 15 bits (009 total): 34 35 36 37 64 76 B1 B4 C1 + Codes of length 16 bits (000 total): + Total number of codes: 059 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000120 + Huffman table length = 28 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (000 total): + Codes of length 03 bits (003 total): 05 06 07 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (000 total): + Codes of length 06 bits (003 total): 02 03 08 + Codes of length 07 bits (001 total): 01 + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000013E + Huffman table length = 64 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (000 total): + Codes of length 03 bits (001 total): 01 + Codes of length 04 bits (002 total): 02 03 + Codes of length 05 bits (003 total): 04 05 11 + Codes of length 06 bits (005 total): 06 21 31 71 81 + Codes of length 07 bits (003 total): 41 51 91 + Codes of length 08 bits (010 total): 12 13 14 22 32 61 A1 B1 C1 D1 + Codes of length 09 bits (005 total): 15 33 42 E1 F0 + Codes of length 10 bits (003 total): 07 23 52 + Codes of length 11 bits (004 total): 43 62 72 82 + Codes of length 12 bits (002 total): 92 F1 + Codes of length 13 bits (001 total): B2 + Codes of length 14 bits (005 total): 16 A2 C2 D2 E2 + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 045 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000180 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x0000018E + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x00008770.0 + + Compression stats: + Compression Ratio: 127.89:1 + Bits per pixel: 0.19:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 22539 ( 97%) + # codes of length 02 bits: 0 ( 0%) + # codes of length 03 bits: 379 ( 2%) + # codes of length 04 bits: 246 ( 1%) + # codes of length 05 bits: 91 ( 0%) + # codes of length 06 bits: 69 ( 0%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 11012 ( 94%) + # codes of length 02 bits: 0 ( 0%) + # codes of length 03 bits: 445 ( 4%) + # codes of length 04 bits: 105 ( 1%) + # codes of length 05 bits: 0 ( 0%) + # codes of length 06 bits: 81 ( 1%) + # codes of length 07 bits: 19 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 23301 ( 56%) + # codes of length 02 bits: 0 ( 0%) + # codes of length 03 bits: 4265 ( 10%) + # codes of length 04 bits: 7407 ( 18%) + # codes of length 05 bits: 3364 ( 8%) + # codes of length 06 bits: 767 ( 2%) + # codes of length 07 bits: 1399 ( 3%) + # codes of length 08 bits: 642 ( 2%) + # codes of length 09 bits: 201 ( 0%) + # codes of length 10 bits: 285 ( 1%) + # codes of length 11 bits: 99 ( 0%) + # codes of length 12 bits: 42 ( 0%) + # codes of length 13 bits: 30 ( 0%) + # codes of length 14 bits: 6 ( 0%) + # codes of length 15 bits: 9 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 11662 ( 60%) + # codes of length 02 bits: 0 ( 0%) + # codes of length 03 bits: 1971 ( 10%) + # codes of length 04 bits: 1938 ( 10%) + # codes of length 05 bits: 1525 ( 8%) + # codes of length 06 bits: 1005 ( 5%) + # codes of length 07 bits: 339 ( 2%) + # codes of length 08 bits: 727 ( 4%) + # codes of length 09 bits: 148 ( 1%) + # codes of length 10 bits: 41 ( 0%) + # codes of length 11 bits: 23 ( 0%) + # codes of length 12 bits: 8 ( 0%) + # codes of length 13 bits: 2 ( 0%) + # codes of length 14 bits: 5 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[250] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 1017, 0, 0] RGB=[255,255,255] @ MCU[ 0, 0] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x0000876F.5 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x00008770 + + +*** Searching Compression Signatures *** + + Signature: 01557A9AE226A38386271DFE13D64298 + Signature (Rotated): 0167FCEDBA3A8E8CF822163DB3564762 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[Konica Minolta Camera, In] [DiMAGE Z2 ] [ ] No + CAM:[Minolta Co., Ltd. ] [DiMAGE F100 ] [ ] No + CAM:[NIKON ] [E2500 ] [FINE ] No + CAM:[NIKON ] [E4500 ] [FINE ] No + CAM:[NIKON ] [E5000 ] [FINE ] No + CAM:[NIKON ] [E8700 ] [FINE ] No + CAM:[NIKON ] [E885 ] [FINE ] No + CAM:[OLYMPUS CORPORATION ] [C8080WZ ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C2000Z ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C3040Z ] [ ] No + CAM:[SEIKO EPSON CORP. ] [PhotoPC 3000Z ] [ ] No + CAM:[SONY ] [DSC-H2 ] [ ] No + CAM:[SONY ] [DSC-H7 ] [ ] No + CAM:[SONY ] [DSC-H9 ] [ ] No + CAM:[SONY ] [DSC-P200 ] [ ] No + CAM:[SONY ] [DSC-S90 ] [ ] No + CAM:[SONY ] [DSC-W7 ] [ ] No + SW :[IJG Library ] [092 ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [092 ] + SW :[IrfanView ] [092 ] + SW :[idImager ] [092 ] + SW :[FastStone Image Viewer ] [092 ] + SW :[NeatImage ] [092 ] + SW :[Paint.NET ] [092 ] + SW :[Photomatix ] [092 ] + SW :[XnView ] [092 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/testimgint.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/testimgint.jpg.txt new file mode 100644 index 0000000000..ac2b2f9adb --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/testimgint.jpg.txt @@ -0,0 +1,342 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\testimgint.jpg] + Filesize: [5756] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 1 x 1 (aspect ratio) + thumbnail = 0 x 0 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000014 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 8 6 5 8 12 20 26 31 + DQT, Row #1: 6 6 7 10 13 29 30 28 + DQT, Row #2: 7 7 8 12 20 29 35 28 + DQT, Row #3: 7 9 11 15 26 44 40 31 + DQT, Row #4: 9 11 19 28 34 55 52 39 + DQT, Row #5: 12 18 28 32 41 52 57 46 + DQT, Row #6: 25 32 39 44 52 61 60 51 + DQT, Row #7: 36 46 48 49 56 50 52 50 + Approx quality factor = 74.75 (scaling=50.51 variance=0.81) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000059 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 9 9 12 24 50 50 50 50 + DQT, Row #1: 9 11 13 33 50 50 50 50 + DQT, Row #2: 12 13 28 50 50 50 50 50 + DQT, Row #3: 24 33 50 50 50 50 50 50 + DQT, Row #4: 50 50 50 50 50 50 50 50 + DQT, Row #5: 50 50 50 50 50 50 50 50 + DQT, Row #6: 50 50 50 50 50 50 50 50 + DQT, Row #7: 50 50 50 50 50 50 50 50 + Approx quality factor = 74.74 (scaling=50.52 variance=0.19) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x0000009E + Frame header length = 17 + Precision = 8 + Number of Lines = 149 + Samples per Line = 227 + Image Size = 227 x 149 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000B1 + Huffman table length = 31 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (005 total): 01 02 03 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (001 total): 0A + Codes of length 09 bits (001 total): 0B + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000D2 + Huffman table length = 181 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 05 12 21 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 06 13 51 61 + Codes of length 08 bits (003 total): 07 22 71 + Codes of length 09 bits (005 total): 14 32 81 91 A1 + Codes of length 10 bits (005 total): 08 23 42 B1 C1 + Codes of length 11 bits (004 total): 15 52 D1 F0 + Codes of length 12 bits (004 total): 24 33 62 72 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (001 total): 82 + Codes of length 16 bits (125 total): 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 + 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 + 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 + 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 + 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 + B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA + D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 + E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000189 + Huffman table length = 31 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (001 total): 07 + Codes of length 08 bits (001 total): 08 + Codes of length 09 bits (001 total): 09 + Codes of length 10 bits (001 total): 0A + Codes of length 11 bits (001 total): 0B + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000001AA + Huffman table length = 181 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (002 total): 03 11 + Codes of length 05 bits (004 total): 04 05 21 31 + Codes of length 06 bits (004 total): 06 12 41 51 + Codes of length 07 bits (003 total): 07 61 71 + Codes of length 08 bits (004 total): 13 22 32 81 + Codes of length 09 bits (007 total): 08 14 42 91 A1 B1 C1 + Codes of length 10 bits (005 total): 09 23 33 52 F0 + Codes of length 11 bits (004 total): 15 62 72 D1 + Codes of length 12 bits (004 total): 0A 16 24 34 + Codes of length 13 bits (000 total): + Codes of length 14 bits (001 total): E1 + Codes of length 15 bits (002 total): 25 F1 + Codes of length 16 bits (119 total): 17 18 19 1A 26 27 28 29 2A 35 36 37 38 39 3A 43 + 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 + 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 82 + 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 + 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 + B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 + D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 + F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000261 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x0000026F + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x0000167A.0 + + Compression stats: + Compression Ratio: 19.78:1 + Bits per pixel: 1.21:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 86 ( 14%) + # codes of length 03 bits: 412 ( 69%) + # codes of length 04 bits: 65 ( 11%) + # codes of length 05 bits: 33 ( 6%) + # codes of length 06 bits: 4 ( 1%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 132 ( 44%) + # codes of length 03 bits: 70 ( 23%) + # codes of length 04 bits: 60 ( 20%) + # codes of length 05 bits: 26 ( 9%) + # codes of length 06 bits: 12 ( 4%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 2692 ( 48%) + # codes of length 03 bits: 628 ( 11%) + # codes of length 04 bits: 1327 ( 23%) + # codes of length 05 bits: 471 ( 8%) + # codes of length 06 bits: 199 ( 4%) + # codes of length 07 bits: 170 ( 3%) + # codes of length 08 bits: 65 ( 1%) + # codes of length 09 bits: 58 ( 1%) + # codes of length 10 bits: 31 ( 1%) + # codes of length 11 bits: 15 ( 0%) + # codes of length 12 bits: 4 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 6 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 689 ( 46%) + # codes of length 03 bits: 244 ( 16%) + # codes of length 04 bits: 289 ( 19%) + # codes of length 05 bits: 158 ( 11%) + # codes of length 06 bits: 70 ( 5%) + # codes of length 07 bits: 5 ( 0%) + # codes of length 08 bits: 36 ( 2%) + # codes of length 09 bits: 5 ( 0%) + # codes of length 10 bits: 1 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[107] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 1008, -9, 0] RGB=[254,254,250] @ MCU[ 4, 8] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x00001679.6 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0000167A + + +*** Searching Compression Signatures *** + + Signature: 0182408A81A4ABF04D4A34A8A5E98C58 + Signature (Rotated): 012D821C6AB210E2A753BE053B8F55D0 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[SONY ] [CYBERSHOT U ] [ ] Yes + SW :[Adobe Photoshop 7.0 ] [Save As 07 ] + SW :[Apple Quicktime ] [0466-0467 ] + SW :[Digital Photo Professiona] [05 ] + SW :[IJG Library ] [075 ] + SW :[MS Paint ] [ ] + SW :[MS Visio ] [ ] + SW :[ZoomBrowser EX ] [low ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [075 ] + SW :[IrfanView ] [075 ] + SW :[idImager ] [075 ] + SW :[FastStone Image Viewer ] [075 ] + SW :[NeatImage ] [075 ] + SW :[Paint.NET ] [075 ] + SW :[Photomatix ] [075 ] + SW :[XnView ] [075 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/testorig.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/testorig.jpg.txt new file mode 100644 index 0000000000..8e339260ba --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/testorig.jpg.txt @@ -0,0 +1,342 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\testorig.jpg] + Filesize: [5770] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 1 x 1 (aspect ratio) + thumbnail = 0 x 0 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000014 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 8 6 5 8 12 20 26 31 + DQT, Row #1: 6 6 7 10 13 29 30 28 + DQT, Row #2: 7 7 8 12 20 29 35 28 + DQT, Row #3: 7 9 11 15 26 44 40 31 + DQT, Row #4: 9 11 19 28 34 55 52 39 + DQT, Row #5: 12 18 28 32 41 52 57 46 + DQT, Row #6: 25 32 39 44 52 61 60 51 + DQT, Row #7: 36 46 48 49 56 50 52 50 + Approx quality factor = 74.75 (scaling=50.51 variance=0.81) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000059 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 9 9 12 24 50 50 50 50 + DQT, Row #1: 9 11 13 33 50 50 50 50 + DQT, Row #2: 12 13 28 50 50 50 50 50 + DQT, Row #3: 24 33 50 50 50 50 50 50 + DQT, Row #4: 50 50 50 50 50 50 50 50 + DQT, Row #5: 50 50 50 50 50 50 50 50 + DQT, Row #6: 50 50 50 50 50 50 50 50 + DQT, Row #7: 50 50 50 50 50 50 50 50 + Approx quality factor = 74.74 (scaling=50.52 variance=0.19) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x0000009E + Frame header length = 17 + Precision = 8 + Number of Lines = 149 + Samples per Line = 227 + Image Size = 227 x 149 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000B1 + Huffman table length = 31 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (005 total): 01 02 03 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (001 total): 0A + Codes of length 09 bits (001 total): 0B + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000D2 + Huffman table length = 181 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 05 12 21 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 06 13 51 61 + Codes of length 08 bits (003 total): 07 22 71 + Codes of length 09 bits (005 total): 14 32 81 91 A1 + Codes of length 10 bits (005 total): 08 23 42 B1 C1 + Codes of length 11 bits (004 total): 15 52 D1 F0 + Codes of length 12 bits (004 total): 24 33 62 72 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (001 total): 82 + Codes of length 16 bits (125 total): 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 + 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 + 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 + 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 + 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 + B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA + D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 + E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000189 + Huffman table length = 31 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (001 total): 07 + Codes of length 08 bits (001 total): 08 + Codes of length 09 bits (001 total): 09 + Codes of length 10 bits (001 total): 0A + Codes of length 11 bits (001 total): 0B + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000001AA + Huffman table length = 181 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (002 total): 03 11 + Codes of length 05 bits (004 total): 04 05 21 31 + Codes of length 06 bits (004 total): 06 12 41 51 + Codes of length 07 bits (003 total): 07 61 71 + Codes of length 08 bits (004 total): 13 22 32 81 + Codes of length 09 bits (007 total): 08 14 42 91 A1 B1 C1 + Codes of length 10 bits (005 total): 09 23 33 52 F0 + Codes of length 11 bits (004 total): 15 62 72 D1 + Codes of length 12 bits (004 total): 0A 16 24 34 + Codes of length 13 bits (000 total): + Codes of length 14 bits (001 total): E1 + Codes of length 15 bits (002 total): 25 F1 + Codes of length 16 bits (119 total): 17 18 19 1A 26 27 28 29 2A 35 36 37 38 39 3A 43 + 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 + 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 82 + 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 + 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 + B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 + D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 + F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000261 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x0000026F + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x00001688.0 + + Compression stats: + Compression Ratio: 19.73:1 + Bits per pixel: 1.22:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 88 ( 15%) + # codes of length 03 bits: 409 ( 68%) + # codes of length 04 bits: 66 ( 11%) + # codes of length 05 bits: 33 ( 6%) + # codes of length 06 bits: 4 ( 1%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 134 ( 45%) + # codes of length 03 bits: 68 ( 23%) + # codes of length 04 bits: 60 ( 20%) + # codes of length 05 bits: 26 ( 9%) + # codes of length 06 bits: 12 ( 4%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 2706 ( 48%) + # codes of length 03 bits: 636 ( 11%) + # codes of length 04 bits: 1331 ( 23%) + # codes of length 05 bits: 473 ( 8%) + # codes of length 06 bits: 196 ( 3%) + # codes of length 07 bits: 169 ( 3%) + # codes of length 08 bits: 66 ( 1%) + # codes of length 09 bits: 60 ( 1%) + # codes of length 10 bits: 28 ( 0%) + # codes of length 11 bits: 14 ( 0%) + # codes of length 12 bits: 4 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 5 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 697 ( 46%) + # codes of length 03 bits: 243 ( 16%) + # codes of length 04 bits: 294 ( 19%) + # codes of length 05 bits: 164 ( 11%) + # codes of length 06 bits: 68 ( 4%) + # codes of length 07 bits: 5 ( 0%) + # codes of length 08 bits: 35 ( 2%) + # codes of length 09 bits: 4 ( 0%) + # codes of length 10 bits: 2 ( 0%) + # codes of length 11 bits: 1 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[107] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 1008, -9, 0] RGB=[254,254,250] @ MCU[ 4, 8] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x00001687.2 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x00001688 + + +*** Searching Compression Signatures *** + + Signature: 0182408A81A4ABF04D4A34A8A5E98C58 + Signature (Rotated): 012D821C6AB210E2A753BE053B8F55D0 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[SONY ] [CYBERSHOT U ] [ ] Yes + SW :[Adobe Photoshop 7.0 ] [Save As 07 ] + SW :[Apple Quicktime ] [0466-0467 ] + SW :[Digital Photo Professiona] [05 ] + SW :[IJG Library ] [075 ] + SW :[MS Paint ] [ ] + SW :[MS Visio ] [ ] + SW :[ZoomBrowser EX ] [low ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [075 ] + SW :[IrfanView ] [075 ] + SW :[idImager ] [075 ] + SW :[FastStone Image Viewer ] [075 ] + SW :[NeatImage ] [075 ] + SW :[Paint.NET ] [075 ] + SW :[Photomatix ] [075 ] + SW :[XnView ] [075 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/turtle.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/turtle.jpg.txt new file mode 100644 index 0000000000..a8231b19e6 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/turtle.jpg.txt @@ -0,0 +1,367 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\turtle.jpg] + Filesize: [55126] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 28 x 28 DPcm (dots per cm) + thumbnail = 0 x 0 + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x00000014 + Length = 3256 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 1 of 1 + Profile Size : 3240 bytes + Preferred CMM Type : 'appl' (0x6170706C) + Profile Version : 0.2.1.0 (0x02100000) + Profile Device/Class : Display Device profile ('mntr' (0x6D6E7472)) + Data Colour Space : rgbData ('RGB ' (0x52474220)) + Profile connection space (PCS) : 'XYZ ' (0x58595A20) + Profile creation date : 2012-05-11 16:46:50 + Profile file signature : 'acsp' (0x61637370) + Primary platform : Apple Computer, Inc. ('APPL' (0x4150504C)) + Profile flags : 0x00000000 + Profile flags > Profile not embedded + Profile flags > Profile can't be used independently of embedded + Device Manufacturer : '....' (0x00000000) + Device Model : '....' (0x00000000) + Device attributes : 0x00000000_00000000 + Device attributes > Reflective + Device attributes > Glossy + Device attributes > Media polarity = negative + Device attributes > Black & white media + Rendering intent : Perceptual + Profile creator : 'appl' (0x6170706C) + Profile ID : 0x00000000_00000000_00000000_00000000 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000CCE + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 3 2 2 3 4 6 8 10 + DQT, Row #1: 2 2 2 3 4 9 10 9 + DQT, Row #2: 2 2 3 4 6 9 11 9 + DQT, Row #3: 2 3 4 5 8 14 13 10 + DQT, Row #4: 3 4 6 9 11 17 16 12 + DQT, Row #5: 4 6 9 10 13 17 18 15 + DQT, Row #6: 8 10 12 14 16 19 19 16 + DQT, Row #7: 12 15 15 16 18 16 16 16 + Approx quality factor = 91.86 (scaling=16.28 variance=1.13) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000D13 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 3 3 4 8 16 16 16 16 + DQT, Row #1: 3 3 4 11 16 16 16 16 + DQT, Row #2: 4 4 9 16 16 16 16 16 + DQT, Row #3: 8 11 16 16 16 16 16 16 + DQT, Row #4: 16 16 16 16 16 16 16 16 + DQT, Row #5: 16 16 16 16 16 16 16 16 + DQT, Row #6: 16 16 16 16 16 16 16 16 + DQT, Row #7: 16 16 16 16 16 16 16 16 + Approx quality factor = 91.90 (scaling=16.20 variance=0.15) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x00000D58 + Frame header length = 17 + Precision = 8 + Number of Lines = 281 + Samples per Line = 450 + Image Size = 450 x 281 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000D6B + Huffman table length = 29 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 06 + Codes of length 03 bits (004 total): 00 04 05 07 + Codes of length 04 bits (003 total): 01 03 08 + Codes of length 05 bits (001 total): 02 + Codes of length 06 bits (001 total): 09 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 010 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000D8A + Huffman table length = 82 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (003 total): 02 03 04 + Codes of length 04 bits (003 total): 05 06 11 + Codes of length 05 bits (002 total): 00 12 + Codes of length 06 bits (004 total): 07 13 21 31 + Codes of length 07 bits (003 total): 22 41 51 + Codes of length 08 bits (004 total): 08 14 61 71 + Codes of length 09 bits (006 total): 15 23 32 81 91 A1 + Codes of length 10 bits (007 total): 16 42 52 55 94 B1 D2 + Codes of length 11 bits (004 total): 33 62 72 C1 + Codes of length 12 bits (007 total): 24 34 82 92 A2 D1 F0 + Codes of length 13 bits (005 total): 17 43 53 54 E1 + Codes of length 14 bits (006 total): 09 25 35 83 93 B2 + Codes of length 15 bits (007 total): 18 26 44 64 73 A3 C2 + Codes of length 16 bits (001 total): A4 + Total number of codes: 063 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000DDE + Huffman table length = 28 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (005 total): 01 04 05 06 07 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (001 total): 02 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000DFC + Huffman table length = 76 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (002 total): 02 11 + Codes of length 04 bits (004 total): 00 03 04 21 + Codes of length 05 bits (003 total): 05 12 31 + Codes of length 06 bits (004 total): 06 41 51 61 + Codes of length 07 bits (006 total): 13 22 71 81 91 A1 + Codes of length 08 bits (006 total): 07 14 32 52 B1 D1 + Codes of length 09 bits (007 total): 15 42 53 62 72 92 C1 + Codes of length 10 bits (005 total): 23 33 A2 E1 F0 + Codes of length 11 bits (005 total): 16 17 34 82 B2 + Codes of length 12 bits (007 total): 24 35 54 63 73 D2 F1 + Codes of length 13 bits (004 total): 08 25 93 C2 + Codes of length 14 bits (003 total): 43 C3 D3 + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 057 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000E4A + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x00000E58 + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x0000D754.0 + + Compression stats: + Compression Ratio: 7.37:1 + Bits per pixel: 3.26:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 344 ( 16%) + # codes of length 03 bits: 1157 ( 55%) + # codes of length 04 bits: 453 ( 22%) + # codes of length 05 bits: 102 ( 5%) + # codes of length 06 bits: 32 ( 2%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 216 ( 21%) + # codes of length 03 bits: 672 ( 64%) + # codes of length 04 bits: 77 ( 7%) + # codes of length 05 bits: 59 ( 6%) + # codes of length 06 bits: 20 ( 2%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 11146 ( 23%) + # codes of length 03 bits: 21025 ( 43%) + # codes of length 04 bits: 8376 ( 17%) + # codes of length 05 bits: 3259 ( 7%) + # codes of length 06 bits: 2911 ( 6%) + # codes of length 07 bits: 840 ( 2%) + # codes of length 08 bits: 696 ( 1%) + # codes of length 09 bits: 499 ( 1%) + # codes of length 10 bits: 295 ( 1%) + # codes of length 11 bits: 93 ( 0%) + # codes of length 12 bits: 85 ( 0%) + # codes of length 13 bits: 26 ( 0%) + # codes of length 14 bits: 15 ( 0%) + # codes of length 15 bits: 8 ( 0%) + # codes of length 16 bits: 1 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 5260 ( 31%) + # codes of length 03 bits: 4077 ( 24%) + # codes of length 04 bits: 4031 ( 24%) + # codes of length 05 bits: 1544 ( 9%) + # codes of length 06 bits: 912 ( 5%) + # codes of length 07 bits: 626 ( 4%) + # codes of length 08 bits: 335 ( 2%) + # codes of length 09 bits: 163 ( 1%) + # codes of length 10 bits: 58 ( 0%) + # codes of length 11 bits: 27 ( 0%) + # codes of length 12 bits: 18 ( 0%) + # codes of length 13 bits: 6 ( 0%) + # codes of length 14 bits: 3 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[167] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 1017, 3, -3] RGB=[253,255,255] @ MCU[ 26, 8] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x0000D753.1 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0000D754 + + +*** Searching Compression Signatures *** + + Signature: 01557A9AE226A38386271DFE13D64298 + Signature (Rotated): 0167FCEDBA3A8E8CF822163DB3564762 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[Konica Minolta Camera, In] [DiMAGE Z2 ] [ ] No + CAM:[Minolta Co., Ltd. ] [DiMAGE F100 ] [ ] No + CAM:[NIKON ] [E2500 ] [FINE ] No + CAM:[NIKON ] [E4500 ] [FINE ] No + CAM:[NIKON ] [E5000 ] [FINE ] No + CAM:[NIKON ] [E8700 ] [FINE ] No + CAM:[NIKON ] [E885 ] [FINE ] No + CAM:[OLYMPUS CORPORATION ] [C8080WZ ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C2000Z ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C3040Z ] [ ] No + CAM:[SEIKO EPSON CORP. ] [PhotoPC 3000Z ] [ ] No + CAM:[SONY ] [DSC-H2 ] [ ] No + CAM:[SONY ] [DSC-H7 ] [ ] No + CAM:[SONY ] [DSC-H9 ] [ ] No + CAM:[SONY ] [DSC-P200 ] [ ] No + CAM:[SONY ] [DSC-S90 ] [ ] No + CAM:[SONY ] [DSC-W7 ] [ ] No + SW :[IJG Library ] [092 ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [092 ] + SW :[IrfanView ] [092 ] + SW :[idImager ] [092 ] + SW :[FastStone Image Viewer ] [092 ] + SW :[NeatImage ] [092 ] + SW :[Paint.NET ] [092 ] + SW :[Photomatix ] [092 ] + SW :[XnView ] [092 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/baseline/JpegSnoopReports/ycck.jpg.txt b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/ycck.jpg.txt new file mode 100644 index 0000000000..717f35f3c9 --- /dev/null +++ b/tests/Images/Input/Jpg/baseline/JpegSnoopReports/ycck.jpg.txt @@ -0,0 +1,640 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\ycck.jpg] + Filesize: [611572] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000002 + Length = 4452 + Identifier = [Exif] + Identifier TIFF = 0x[4D4D002A 00000008] + Endian = Motorola (big) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000014 + Dir Length = 0x0007 + [Orientation ] = 1 = Row 0: top, Col 0: left + [XResolution ] = 720000/10000 + [YResolution ] = 720000/10000 + [ResolutionUnit ] = Inch + [Software ] = "Adobe Photoshop CC 2015.5 (Windows)" + [DateTime ] = "2016:08:23 18:21:25" + [ExifOffset ] = @ 0x00AC + Offset to Next IFD = 0x000000D8 + + EXIF IFD1 @ Absolute 0x000000E4 + Dir Length = 0x0006 + [Compression ] = JPEG + [XResolution ] = 72/1 + [YResolution ] = 72/1 + [ResolutionUnit ] = Inch + [JpegIFOffset ] = @ +0x0136 = @ 0x0142 + [JpegIFByteCount ] = 0x[00001026] / 4134 + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x000000B8 + Dir Length = 0x0003 + [ColorSpace ] = Uncalibrated + [ExifImageWidth ] = 0x[00000200] / 512 + [ExifImageHeight ] = 0x[00000200] / 512 + +*** Marker: APP13 (xFFED) *** + OFFSET: 0x00001168 + Length = 6522 + Identifier = [Photoshop 3.0] + 8BIM: [0x0425] Name="" Len=[0x0010] DefinedName="Caption digest" + Caption digest = | 0x00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ + 8BIM: [0x043A] Name="" Len=[0x00E5] DefinedName="Print Information" + Print Information = + | 0x00 00 00 10 00 00 00 01 00 00 00 00 00 0B 70 72 | ..............pr + | 0x69 6E 74 4F 75 74 70 75 74 00 00 00 05 00 00 00 | intOutput....... + | 0x00 50 73 74 53 62 6F 6F 6C 01 00 00 00 00 49 6E | .PstSbool.....In + | 0x74 65 65 6E 75 6D 00 00 00 00 49 6E 74 65 00 00 | teenum....Inte.. + | 0x00 00 43 6C 72 6D 00 00 00 0F 70 72 69 6E 74 53 | ..Clrm....printS + | 0x69 78 74 65 65 6E 42 69 74 62 6F 6F 6C 00 00 00 | ixteenBitbool... + | 0x00 0B 70 72 69 6E 74 65 72 4E 61 6D 65 54 45 58 | ..printerNameTEX + | 0x54 00 00 00 01 00 00 00 00 00 0F 70 72 69 6E 74 | T..........print + | ... + 8BIM: [0x043B] Name="" Len=[0x022D] DefinedName="Print Style" + Print Style = + | 0x00 00 00 10 00 00 00 01 00 00 00 00 00 12 70 72 | ..............pr + | 0x69 6E 74 4F 75 74 70 75 74 4F 70 74 69 6F 6E 73 | intOutputOptions + | 0x00 00 00 17 00 00 00 00 43 70 74 6E 62 6F 6F 6C | ........Cptnbool + | 0x00 00 00 00 00 43 6C 62 72 62 6F 6F 6C 00 00 00 | .....Clbrbool... + | 0x00 00 52 67 73 4D 62 6F 6F 6C 00 00 00 00 00 43 | ..RgsMbool.....C + | 0x72 6E 43 62 6F 6F 6C 00 00 00 00 00 43 6E 74 43 | rnCbool.....CntC + | 0x62 6F 6F 6C 00 00 00 00 00 4C 62 6C 73 62 6F 6F | bool.....Lblsboo + | 0x6C 00 00 00 00 00 4E 67 74 76 62 6F 6F 6C 00 00 | l.....Ngtvbool.. + | ... + 8BIM: [0x03ED] Name="" Len=[0x0010] DefinedName="ResolutionInfo structure" + Horizontal resolution = 72 pixels per inch + Width unit = cm + Vertical resolution = 72 pixels per inch + Height unit = cm + 8BIM: [0x0426] Name="" Len=[0x000E] DefinedName="Print scale" + Style = centered + X location = 0.00000 + Y location = 0.00000 + Scale = 1.00000 + 8BIM: [0x040D] Name="" Len=[0x0004] DefinedName="Global Angle" + Global Angle = 30 degrees + 8BIM: [0x0419] Name="" Len=[0x0004] DefinedName="Global Altitude" + Global Altitude = 30 + 8BIM: [0x03F3] Name="" Len=[0x0009] DefinedName="Print flags" + Labels = false + Crop marks = false + Color bars = false + Registration marks = false + Negative = false + Flip = false + Interpolate = false + Caption = false + Print flags = true + 8BIM: [0x2710] Name="" Len=[0x000A] DefinedName="Print flags information" + Version = 1 + Center crop marks = 0 + Reserved = 0 + Bleed width value = 0 + Bleed width scale = 2 + 8BIM: [0x03F5] Name="" Len=[0x0048] DefinedName="Color halftoning information" + Color halftoning information = + | 0x00 2F 66 66 00 01 00 6C 66 66 00 06 00 00 00 00 | ./ff...lff...... + | 0x00 01 00 2F 66 66 00 01 00 A1 99 9A 00 06 00 00 | .../ff.......... + | 0x00 00 00 01 00 32 00 00 00 01 00 5A 00 00 00 06 | .....2.....Z.... + | 0x00 00 00 00 00 01 00 35 00 00 00 01 00 2D 00 00 | .......5.....-.. + | 0x00 06 00 00 00 00 00 01 | ........ + 8BIM: [0x03F8] Name="" Len=[0x0070] DefinedName="Color transfer functions" + Color transfer functions = + | 0x00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF | ................ + | 0xFF FF FF FF FF FF FF FF 03 E8 00 00 00 00 FF FF | ................ + | 0xFF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF | ................ + | 0xFF FF FF FF 03 E8 00 00 00 00 FF FF FF FF FF FF | ................ + | 0xFF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF | ................ + | 0x03 E8 00 00 00 00 FF FF FF FF FF FF FF FF FF FF | ................ + | 0xFF FF FF FF FF FF FF FF FF FF FF FF 03 E8 00 00 | ................ + 8BIM: [0x0400] Name="" Len=[0x0002] DefinedName="Layer state information" + Target layer = 0 + 8BIM: [0x0402] Name="" Len=[0x0002] DefinedName="Layers group information" + Layer #0: + Layer Group = 0 + 8BIM: [0x0430] Name="" Len=[0x0001] DefinedName="Layer Groups Enabled ID" + Layer #0: + Layer Group Enabled ID = 1 + 8BIM: [0x042D] Name="" Len=[0x0006] DefinedName="Layer Selection IDs" + Num selected = 1 + Layer ID = 3 + 8BIM: [0x0408] Name="" Len=[0x0010] DefinedName="Grid and guides information" + Version = 1 + Grid Horizontal = 576 + Grid Vertical = 576 + Number of Guide Resources = 0 + 8BIM: [0x041E] Name="" Len=[0x0004] DefinedName="URL List" + URL List = | 0x00 00 00 00 | .... + 8BIM: [0x041A] Name="" Len=[0x0363] DefinedName="Slices" + Slice Header: + Version = 6 + Bound Rect (top) = 0 + Bound Rect (left) = 0 + Bound Rect (bottom) = 512 + Bound Rect (right) = 512 + Name of group of slices = "imageprocessor-logo-512" + Number of slices = 1 + ----- + Slice #0: + Slice Resource: + ID = 0 + Group ID = 0 + Origin = 0 + Name = "" + Type = 1 + Position (top) = 0 + Position (left) = 0 + Position (bottom) = 512 + Position (right) = 512 + URL = "" + Target = "" + Message = "" + Alt Tag = "" + Cell text is HTML = true + Cell text = "" + Horizontal alignment = 0 + Vertical alignment = 0 + Alpha color = 0 + Red = 0 + Green = 0 + Blue = 0 + Descriptor version = 16 + Descriptor: + Name from classID = "" + classID = "null" + Num items in descriptor = 2 + ----- + Descriptor item #0: + Key = "bounds" + OSType key = "Objc" + Descriptor: + Name from classID = "" + classID = "Rct1" + Num items in descriptor = 4 + ----- + Descriptor item #0: + Key = "Top " + OSType key = "long" + Value = 0 + Descriptor item #1: + Key = "Left" + OSType key = "long" + Value = 0 + Descriptor item #2: + Key = "Btom" + OSType key = "long" + Value = 512 + Descriptor item #3: + Key = "Rght" + OSType key = "long" + Value = 512 + ----- + Descriptor item #1: + Key = "slices" + OSType key = "VlLs" + Num items in list = 1 + ----- + Item #0: + OSType key = "" + Descriptor: + Name from classID = "" + classID = "slice" + Num items in descriptor = 18 + ----- + Descriptor item #0: + Key = "sliceID" + OSType key = "long" + Value = 0 + Descriptor item #1: + Key = "groupID" + OSType key = "long" + Value = 0 + Descriptor item #2: + Key = "origin" + OSType key = "enum" + Type = "ESliceOrigin" + Enum = "autoGenerated" + Descriptor item #3: + Key = "Type" + OSType key = "enum" + Type = "ESliceType" + Enum = "Img " + Descriptor item #4: + Key = "bounds" + OSType key = "Objc" + Descriptor: + Name from classID = "" + classID = "Rct1" + Num items in descriptor = 4 + ----- + Descriptor item #0: + Key = "Top " + OSType key = "long" + Value = 0 + Descriptor item #1: + Key = "Left" + OSType key = "long" + Value = 0 + Descriptor item #2: + Key = "Btom" + OSType key = "long" + Value = 512 + Descriptor item #3: + Key = "Rght" + OSType key = "long" + Value = 512 + ----- + Descriptor item #5: + Key = "url" + OSType key = "TEXT" + String = "" + Descriptor item #6: + Key = "null" + OSType key = "TEXT" + String = "" + Descriptor item #7: + Key = "Msge" + OSType key = "TEXT" + String = "" + Descriptor item #8: + Key = "altTag" + OSType key = "TEXT" + String = "" + Descriptor item #9: + Key = "cellTextIsHTML" + OSType key = "bool" + Value = true + Descriptor item #10: + Key = "cellText" + OSType key = "TEXT" + String = "" + Descriptor item #11: + Key = "horzAlign" + OSType key = "enum" + Type = "ESliceHorzAlign" + Enum = "default" + Descriptor item #12: + Key = "vertAlign" + OSType key = "enum" + Type = "ESliceVertAlign" + Enum = "default" + Descriptor item #13: + Key = "bgColorType" + OSType key = "enum" + Type = "ESliceBGColorType" + Enum = "None" + Descriptor item #14: + Key = "topOutset" + OSType key = "long" + Value = 0 + Descriptor item #15: + Key = "leftOutset" + OSType key = "long" + Value = 0 + Descriptor item #16: + Key = "bottomOutset" + OSType key = "long" + Value = 0 + Descriptor item #17: + Key = "rightOutset" + OSType key = "long" + Value = 0 + ----- + ----- + ----- + ----- + 8BIM: [0x0428] Name="" Len=[0x000C] DefinedName="Pixel Aspect Ratio" + Version = 2 + X/Y Ratio = 1.00000 + 8BIM: [0x0414] Name="" Len=[0x0004] DefinedName="Document-specific IDs seed number" + Base value = 3 + 8BIM: [0x040C] Name="" Len=[0x1042] DefinedName="Thumbnail resources" + Format = 1 + Width of thumbnail = 160 pixels + Height of thumbnail = 160 pixels + Widthbytes = 480 bytes + Total size = 76800 bytes + Size after compression = 4134 bytes + Bits per pixel = 24 bits + Number of planes = 1 + JFIF data @ 0x00001A3C + 8BIM: [0x0421] Name="" Len=[0x0061] DefinedName="Version Info" + Version = 1 + hasRealMergedData = 1 + Writer name = "Adobe Photoshop" + Reader name = "Adobe Photoshop CC 2015.5" + File version = 1 + 8BIM: [0x0406] Name="" Len=[0x0007] DefinedName="JPEG quality" + Photoshop Save As Quality = 8 + Photoshop Save Format = "Standard" + Photoshop Save Progressive Scans = "3 Scans" + ??? = 1 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00002AE4 + Length = 3685 + Identifier = [http://ns.adobe.com/xap/1.0/] + XMP = + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0000394B + Length = 65506 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 1 of 9 + Profile Size : 557168 bytes + Preferred CMM Type : 'ADBE' (0x41444245) + Profile Version : 0.2.1.0 (0x02100000) + Profile Device/Class : Output Device profile ('prtr' (0x70727472)) + Data Colour Space : cmykData ('CMYK' (0x434D594B)) + Profile connection space (PCS) : 'Lab ' (0x4C616220) + Profile creation date : 2000-07-26 05:41:53 + Profile file signature : 'acsp' (0x61637370) + Primary platform : Apple Computer, Inc. ('APPL' (0x4150504C)) + Profile flags : 0x00000000 + Profile flags > Profile not embedded + Profile flags > Profile can't be used independently of embedded + Device Manufacturer : 'ADBE' (0x41444245) + Device Model : '....' (0x00000000) + Device attributes : 0x00000000_00000000 + Device attributes > Reflective + Device attributes > Glossy + Device attributes > Media polarity = negative + Device attributes > Black & white media + Rendering intent : Media-Relative Colorimetric + Profile creator : 'ADBE' (0x41444245) + Profile ID : 0x00000000_00000000_00000000_00000000 + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0001392F + Length = 65506 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 2 of 9 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x00023913 + Length = 65506 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 3 of 9 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x000338F7 + Length = 65506 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 4 of 9 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x000438DB + Length = 65506 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 5 of 9 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x000538BF + Length = 65506 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 6 of 9 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x000638A3 + Length = 65506 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 7 of 9 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x00073887 + Length = 65506 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 8 of 9 + Only support decode of 1st ICC Marker + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0008386B + Length = 33264 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 9 of 9 + Only support decode of 1st ICC Marker + +*** Marker: APP14 (xFFEE) *** + OFFSET: 0x0008BA5D + Length = 14 + DCTEncodeVersion = 100 + APP14Flags0 = 0 + APP14Flags1 = 0 + ColorTransform = 2 [YCCK] + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x0008BA6D + Table length = 132 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 6 4 4 6 9 11 12 16 + DQT, Row #1: 4 5 5 6 8 10 12 12 + DQT, Row #2: 4 5 5 6 10 12 12 12 + DQT, Row #3: 6 6 6 11 12 12 12 12 + DQT, Row #4: 9 8 10 12 12 12 12 12 + DQT, Row #5: 11 10 12 12 12 12 12 12 + DQT, Row #6: 12 12 12 12 12 12 12 12 + DQT, Row #7: 16 12 12 12 12 12 12 12 + Approx quality factor = 88.28 (scaling=23.43 variance=111.68) + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 7 7 13 24 20 20 17 17 + DQT, Row #1: 7 12 16 14 14 12 12 12 + DQT, Row #2: 13 16 14 14 12 12 12 12 + DQT, Row #3: 24 14 14 12 12 12 12 12 + DQT, Row #4: 20 14 12 12 12 12 12 12 + DQT, Row #5: 20 12 12 12 12 12 12 12 + DQT, Row #6: 17 12 12 12 12 12 12 12 + DQT, Row #7: 17 12 12 12 12 12 12 12 + Approx quality factor = 90.19 (scaling=19.62 variance=201.04) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x0008BAF3 + Frame header length = 20 + Precision = 8 + Number of Lines = 512 + Samples per Line = 512 + Image Size = 512 x 512 + Raw Image Orientation = Landscape + Number of Img components = 4 + Component[1]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Cr) + Component[4]: ID=0x04, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (K) + +*** Marker: DRI (Restart Interval) (xFFDD) *** + OFFSET: 0x0008BB09 + Length = 4 + interval = 64 + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0008BB0F + Huffman table length = 418 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (000 total): + Codes of length 03 bits (007 total): 04 05 03 02 06 01 00 + Codes of length 04 bits (001 total): 07 + Codes of length 05 bits (001 total): 08 + Codes of length 06 bits (001 total): 09 + Codes of length 07 bits (001 total): 0A + Codes of length 08 bits (001 total): 0B + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 00 + Codes of length 03 bits (002 total): 02 03 + Codes of length 04 bits (003 total): 04 05 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (001 total): 0A + Codes of length 09 bits (001 total): 0B + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 11 04 00 + Codes of length 05 bits (003 total): 05 21 12 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 51 06 13 61 + Codes of length 08 bits (002 total): 22 71 + Codes of length 09 bits (006 total): 81 14 32 91 A1 07 + Codes of length 10 bits (007 total): 15 B1 42 23 C1 52 D1 + Codes of length 11 bits (003 total): E1 33 16 + Codes of length 12 bits (004 total): 62 F0 24 72 + Codes of length 13 bits (002 total): 82 F1 + Codes of length 14 bits (006 total): 25 43 34 53 92 A2 + Codes of length 15 bits (002 total): B2 63 + Codes of length 16 bits (115 total): 73 C2 35 44 27 93 A3 B3 36 17 54 64 74 C3 D2 E2 + 08 26 83 09 0A 18 19 84 94 45 46 A4 B4 56 D3 55 + 28 1A F2 E3 F3 C4 D4 E4 F4 65 75 85 95 A5 B5 C5 + D5 E5 F5 66 76 86 96 A6 B6 C6 D6 E6 F6 37 47 57 + 67 77 87 97 A7 B7 C7 D7 E7 F7 38 48 58 68 78 88 + 98 A8 B8 C8 D8 E8 F8 29 39 49 59 69 79 89 99 A9 + B9 C9 D9 E9 F9 2A 3A 4A 5A 6A 7A 8A 9A AA BA CA + DA EA FA + Total number of codes: 162 + + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 00 + Codes of length 03 bits (002 total): 02 11 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (002 total): 04 21 + Codes of length 06 bits (003 total): 12 31 41 + Codes of length 07 bits (005 total): 05 51 13 61 22 + Codes of length 08 bits (005 total): 06 71 81 91 32 + Codes of length 09 bits (004 total): A1 B1 F0 14 + Codes of length 10 bits (005 total): C1 D1 E1 23 42 + Codes of length 11 bits (006 total): 15 52 62 72 F1 33 + Codes of length 12 bits (004 total): 24 34 43 82 + Codes of length 13 bits (008 total): 16 92 53 25 A2 63 B2 C2 + Codes of length 14 bits (003 total): 07 73 D2 + Codes of length 15 bits (003 total): 35 E2 44 + Codes of length 16 bits (109 total): 83 17 54 93 08 09 0A 18 19 26 36 45 1A 27 64 74 + 55 37 F2 A3 B3 C3 28 29 D3 E3 F3 84 94 A4 B4 C4 + D4 E4 F4 65 75 85 95 A5 B5 C5 D5 E5 F5 46 56 66 + 76 86 96 A6 B6 C6 D6 E6 F6 47 57 67 77 87 97 A7 + B7 C7 D7 E7 F7 38 48 58 68 78 88 98 A8 B8 C8 D8 + E8 F8 39 49 59 69 79 89 99 A9 B9 C9 D9 E9 F9 2A + 3A 4A 5A 6A 7A 8A 9A AA BA CA DA EA FA + Total number of codes: 162 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0008BCB3 + Scan header length = 14 + Number of img components = 4 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Component[4]: selector=0x04, table=0(DC),0(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support CMYK files yet. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x000954F2 + + +*** Searching Compression Signatures *** + + Signature: 01180AF3DE63318828A86409EF4013DD + Signature (Rotated): 01180AF3DE63318828A86409EF4013DD + File Offset: 0 bytes + Chroma subsampling: ?x? + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: OK [Adobe Photoshop CC 2015.5 (Windows)] + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + SW :[Adobe Photoshop ] [Save As 08 ] + + NOTE: Photoshop IRB detected + NOTE: EXIF Software field recognized as from editor + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + diff --git a/tests/Images/Input/Jpg/issues/Issue721-InvalidAPP0.jpg b/tests/Images/Input/Jpg/issues/Issue721-InvalidAPP0.jpg new file mode 100644 index 0000000000..6fa3bf660e Binary files /dev/null and b/tests/Images/Input/Jpg/issues/Issue721-InvalidAPP0.jpg differ diff --git a/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-A.jpg b/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-A.jpg new file mode 100644 index 0000000000..0a11065ce9 Binary files /dev/null and b/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-A.jpg differ diff --git a/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-B.jpg b/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-B.jpg new file mode 100644 index 0000000000..eb52570e1c Binary files /dev/null and b/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-B.jpg differ diff --git a/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-C.jpg b/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-C.jpg new file mode 100644 index 0000000000..0224cb7f1f Binary files /dev/null and b/tests/Images/Input/Jpg/issues/Issue723-Ordered-Interleaved-Progressive-C.jpg differ diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue159-MissingFF00-Progressive-Bedroom.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue159-MissingFF00-Progressive-Bedroom.jpg.txt new file mode 100644 index 0000000000..ea1f5e0f79 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue159-MissingFF00-Progressive-Bedroom.jpg.txt @@ -0,0 +1,461 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue159-MissingFF00-Progressive-Bedroom.jpg] + Filesize: [338422] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 72 x 72 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000014 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 6 4 4 6 9 14 18 22 + DQT, Row #1: 4 4 5 7 9 21 22 20 + DQT, Row #2: 5 5 6 9 14 21 25 20 + DQT, Row #3: 5 6 8 10 18 31 29 22 + DQT, Row #4: 6 8 13 20 24 39 37 28 + DQT, Row #5: 9 13 20 23 29 37 41 33 + DQT, Row #6: 18 23 28 31 37 44 43 36 + DQT, Row #7: 26 33 34 35 40 36 37 36 + Approx quality factor = 81.99 (scaling=36.03 variance=1.13) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000059 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 6 6 9 17 36 36 36 36 + DQT, Row #1: 6 8 9 24 36 36 36 36 + DQT, Row #2: 9 9 20 36 36 36 36 36 + DQT, Row #3: 17 24 36 36 36 36 36 36 + DQT, Row #4: 36 36 36 36 36 36 36 36 + DQT, Row #5: 36 36 36 36 36 36 36 36 + DQT, Row #6: 36 36 36 36 36 36 36 36 + DQT, Row #7: 36 36 36 36 36 36 36 36 + Approx quality factor = 81.88 (scaling=36.24 variance=0.48) + +*** Marker: SOF2 (Progressive DCT, Huffman) (xFFC2) *** + OFFSET: 0x0000009E + Frame header length = 17 + Precision = 8 + Number of Lines = 2300 + Samples per Line = 2300 + Image Size = 2300 x 2300 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000B1 + Huffman table length = 27 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 01 02 + Codes of length 04 bits (003 total): 03 04 05 + Codes of length 05 bits (001 total): 06 + Codes of length 06 bits (001 total): 07 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 008 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000CE + Huffman table length = 26 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (001 total): 04 + Codes of length 06 bits (001 total): 05 + Codes of length 07 bits (001 total): 06 + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 007 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000000EA + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),0(AC) + Component[3]: selector=0x03, table=1(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000CBBB + Huffman table length = 56 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (000 total): + Codes of length 03 bits (004 total): 00 01 02 03 + Codes of length 04 bits (005 total): 04 10 11 12 41 + Codes of length 05 bits (002 total): 21 31 + Codes of length 06 bits (005 total): 05 13 20 22 32 + Codes of length 07 bits (003 total): 15 30 42 + Codes of length 08 bits (005 total): 14 23 33 34 50 + Codes of length 09 bits (000 total): + Codes of length 10 bits (002 total): 24 40 + Codes of length 11 bits (002 total): 06 16 + Codes of length 12 bits (002 total): 43 60 + Codes of length 13 bits (002 total): 70 A0 + Codes of length 14 bits (003 total): 44 80 90 + Codes of length 15 bits (001 total): B0 + Codes of length 16 bits (001 total): 35 + Total number of codes: 037 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000CBF5 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 5 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0001A2A8 + Huffman table length = 51 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (005 total): 02 03 10 20 30 + Codes of length 05 bits (003 total): 12 13 31 + Codes of length 06 bits (004 total): 04 21 32 40 + Codes of length 07 bits (002 total): 14 51 + Codes of length 08 bits (002 total): 05 41 + Codes of length 09 bits (002 total): 33 50 + Codes of length 10 bits (001 total): 22 + Codes of length 11 bits (004 total): 15 52 61 70 + Codes of length 12 bits (003 total): 23 42 60 + Codes of length 13 bits (000 total): + Codes of length 14 bits (003 total): 71 A0 B0 + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 032 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0001A2DD + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0001BB1E + Huffman table length = 50 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (005 total): 02 03 10 20 30 + Codes of length 05 bits (003 total): 12 13 31 + Codes of length 06 bits (004 total): 04 21 32 40 + Codes of length 07 bits (002 total): 14 51 + Codes of length 08 bits (003 total): 05 41 50 + Codes of length 09 bits (001 total): 22 + Codes of length 10 bits (000 total): + Codes of length 11 bits (001 total): 61 + Codes of length 12 bits (004 total): 23 33 60 70 + Codes of length 13 bits (002 total): 15 43 + Codes of length 14 bits (003 total): 52 A0 B0 + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 031 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0001BB52 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0001D49C + Huffman table length = 67 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (000 total): + Codes of length 04 bits (004 total): 10 11 21 31 + Codes of length 05 bits (005 total): 02 41 51 71 81 + Codes of length 06 bits (001 total): 20 + Codes of length 07 bits (006 total): 12 22 30 61 91 A1 + Codes of length 08 bits (004 total): 03 32 33 C1 + Codes of length 09 bits (002 total): 40 50 + Codes of length 10 bits (010 total): 13 23 42 52 72 82 B1 D1 E1 F0 + Codes of length 11 bits (002 total): 34 62 + Codes of length 12 bits (003 total): 60 92 F1 + Codes of length 13 bits (000 total): + Codes of length 14 bits (002 total): 24 80 + Codes of length 15 bits (000 total): + Codes of length 16 bits (007 total): 04 70 43 A2 14 C0 D0 + Total number of codes: 048 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0001D4E1 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 6 .. 63 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000291D6 + Huffman table length = 44 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (002 total): 11 21 + Codes of length 04 bits (002 total): 31 41 + Codes of length 05 bits (001 total): 51 + Codes of length 06 bits (003 total): 10 61 71 + Codes of length 07 bits (003 total): 81 91 A1 + Codes of length 08 bits (003 total): 20 B1 F0 + Codes of length 09 bits (004 total): 30 C1 D1 E1 + Codes of length 10 bits (003 total): 40 50 F1 + Codes of length 11 bits (001 total): 60 + Codes of length 12 bits (001 total): 70 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 025 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00029204 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x21 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00044080 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=0(DC),0(AC) + Component[3]: selector=0x03, table=0(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00047DC7 + Huffman table length = 45 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (004 total): 10 30 31 61 + Codes of length 05 bits (006 total): 20 21 40 41 51 71 + Codes of length 06 bits (002 total): 50 60 + Codes of length 07 bits (003 total): 81 91 A1 + Codes of length 08 bits (001 total): B1 + Codes of length 09 bits (000 total): + Codes of length 10 bits (002 total): 70 F0 + Codes of length 11 bits (002 total): C1 D1 + Codes of length 12 bits (003 total): A0 B0 F1 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 026 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00047DF6 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00048BD6 + Huffman table length = 45 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (004 total): 20 30 31 61 + Codes of length 05 bits (006 total): 10 21 40 41 51 71 + Codes of length 06 bits (002 total): 50 81 + Codes of length 07 bits (003 total): 60 91 B1 + Codes of length 08 bits (001 total): A1 + Codes of length 09 bits (000 total): + Codes of length 10 bits (002 total): 70 C1 + Codes of length 11 bits (002 total): 80 F0 + Codes of length 12 bits (003 total): A0 B0 D1 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 026 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00048C05 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00049AE7 + Huffman table length = 48 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 70 + Codes of length 03 bits (001 total): 00 + Codes of length 04 bits (007 total): 01 11 31 41 51 80 91 + Codes of length 05 bits (003 total): 10 21 81 + Codes of length 06 bits (002 total): 50 60 + Codes of length 07 bits (005 total): 20 40 61 71 D1 + Codes of length 08 bits (004 total): 30 B1 E1 F1 + Codes of length 09 bits (002 total): A1 F0 + Codes of length 10 bits (003 total): A0 C0 C1 + Codes of length 11 bits (001 total): D0 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 029 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00049B19 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x000529F4 + + +*** Searching Compression Signatures *** + + Signature: 0138A8D4ECE59F41D2EB9AF5168B6675 + Signature (Rotated): 01CA9A809F737BA668C16DDE52E74092 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C3040Z ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C700UZ ] [ ] No + SW :[IJG Library ] [082 ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [082 ] + SW :[IrfanView ] [082 ] + SW :[idImager ] [082 ] + SW :[FastStone Image Viewer ] [082 ] + SW :[NeatImage ] [082 ] + SW :[Paint.NET ] [082 ] + SW :[Photomatix ] [082 ] + SW :[XnView ] [082 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue159-MissingFF00-Progressive-Girl.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue159-MissingFF00-Progressive-Girl.jpg.txt new file mode 100644 index 0000000000..4858b4ea18 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue159-MissingFF00-Progressive-Girl.jpg.txt @@ -0,0 +1,520 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue159-MissingFF00-Progressive-Girl.jpg] + Filesize: [60927] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.2] + density = 1 x 1 (aspect ratio) + thumbnail = 0 x 0 + +*** Marker: APP13 (xFFED) *** + OFFSET: 0x00000014 + Length = 132 + Identifier = [Photoshop 3.0] + 8BIM: [0x0404] Name="" Len=[0x0067] DefinedName="IPTC-NAA record" + IPTC [002:040] Special Instructions = "FBMD01000a820d0000192d00007a4400006e460000a9470000a44e00000b7b0000cc830000e0880000a08c0000ffed0000" + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0000009A + Length = 3064 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 1 of 1 + Profile Size : 3048 bytes + Preferred CMM Type : '....' (0x00000000) + Profile Version : 0.2.0.0 (0x02000000) + Profile Device/Class : Display Device profile ('mntr' (0x6D6E7472)) + Data Colour Space : rgbData ('RGB ' (0x52474220)) + Profile connection space (PCS) : 'XYZ ' (0x58595A20) + Profile creation date : 2009-03-27 21:36:31 + Profile file signature : 'acsp' (0x61637370) + Primary platform : ? (0x00000000) ('....' (0x00000000)) + Profile flags : 0x00000000 + Profile flags > Profile not embedded + Profile flags > Profile can't be used independently of embedded + Device Manufacturer : '....' (0x00000000) + Device Model : '....' (0x00000000) + Device attributes : 0x00000001_00000000 + Device attributes > Reflective + Device attributes > Glossy + Device attributes > Media polarity = negative + Device attributes > Black & white media + Rendering intent : Perceptual + Profile creator : '....' (0x00000000) + Profile ID : 0x29F83DDE_AFF255AE_7842FAE4_CA83390D + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000C94 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 9 6 6 9 14 23 30 35 + DQT, Row #1: 7 7 8 11 15 34 35 32 + DQT, Row #2: 8 8 9 14 23 33 40 32 + DQT, Row #3: 8 10 13 17 30 50 46 36 + DQT, Row #4: 10 13 21 32 39 63 60 45 + DQT, Row #5: 14 20 32 37 47 60 66 53 + DQT, Row #6: 28 37 45 50 60 70 70 59 + DQT, Row #7: 42 53 55 57 65 58 60 57 + Approx quality factor = 71.07 (scaling=57.86 variance=0.92) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000CD9 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 10 10 14 27 57 57 57 57 + DQT, Row #1: 10 12 15 38 57 57 57 57 + DQT, Row #2: 14 15 32 57 57 57 57 57 + DQT, Row #3: 27 38 57 57 57 57 57 57 + DQT, Row #4: 57 57 57 57 57 57 57 57 + DQT, Row #5: 57 57 57 57 57 57 57 57 + DQT, Row #6: 57 57 57 57 57 57 57 57 + DQT, Row #7: 57 57 57 57 57 57 57 57 + Approx quality factor = 71.23 (scaling=57.54 variance=0.18) + +*** Marker: SOF2 (Progressive DCT, Huffman) (xFFC2) *** + OFFSET: 0x00000D1E + Frame header length = 17 + Precision = 8 + Number of Lines = 990 + Samples per Line = 750 + Image Size = 750 x 990 + Raw Image Orientation = Portrait + Number of Img components = 3 + Component[1]: ID=0x00, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x01, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000D31 + Huffman table length = 27 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (001 total): 07 + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 008 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000D4E + Huffman table length = 24 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (001 total): 04 + Codes of length 06 bits (000 total): + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 005 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000D68 + Huffman table length = 24 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (001 total): 04 + Codes of length 06 bits (000 total): + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 005 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000D82 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x00, table=0(DC),0(AC) + Component[2]: selector=0x01, table=1(DC),1(AC) + Component[3]: selector=0x02, table=1(DC),1(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00002CED + Huffman table length = 42 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (003 total): 03 10 11 + Codes of length 05 bits (003 total): 04 12 20 + Codes of length 06 bits (004 total): 21 30 31 40 + Codes of length 07 bits (002 total): 32 41 + Codes of length 08 bits (002 total): 13 22 + Codes of length 09 bits (003 total): 05 14 50 + Codes of length 10 bits (001 total): 33 + Codes of length 11 bits (001 total): 42 + Codes of length 12 bits (001 total): 23 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 023 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00002D19 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x00, table=0(DC),0(AC) + Spectral selection = 1 .. 5 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00004455 + Huffman table length = 35 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (001 total): 00 + Codes of length 04 bits (005 total): 02 10 11 20 30 + Codes of length 05 bits (001 total): 40 + Codes of length 06 bits (000 total): + Codes of length 07 bits (002 total): 12 50 + Codes of length 08 bits (003 total): 03 31 41 + Codes of length 09 bits (001 total): 21 + Codes of length 10 bits (001 total): 51 + Codes of length 11 bits (001 total): 60 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 016 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000447A + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=1(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000464C + Huffman table length = 32 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (000 total): + Codes of length 04 bits (007 total): 00 10 11 20 30 40 50 + Codes of length 05 bits (001 total): 02 + Codes of length 06 bits (001 total): 12 + Codes of length 07 bits (000 total): + Codes of length 08 bits (003 total): 21 31 70 + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 013 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000466E + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=1(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00004775 + Huffman table length = 50 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (003 total): 02 21 40 + Codes of length 05 bits (007 total): 10 12 30 31 32 50 91 + Codes of length 06 bits (003 total): 20 41 51 + Codes of length 07 bits (003 total): 60 61 71 + Codes of length 08 bits (004 total): 03 22 42 81 + Codes of length 09 bits (002 total): 13 62 + Codes of length 10 bits (001 total): 43 + Codes of length 11 bits (005 total): 23 52 70 80 A1 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 031 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000047A9 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x00, table=0(DC),0(AC) + Spectral selection = 6 .. 63 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00004E79 + Huffman table length = 41 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (003 total): 10 21 31 + Codes of length 05 bits (004 total): 41 61 71 81 + Codes of length 06 bits (002 total): 20 51 + Codes of length 07 bits (002 total): 30 91 + Codes of length 08 bits (003 total): A1 D1 F0 + Codes of length 09 bits (001 total): 40 + Codes of length 10 bits (001 total): B1 + Codes of length 11 bits (001 total): C1 + Codes of length 12 bits (001 total): F1 + Codes of length 13 bits (001 total): E1 + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 022 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00004EA4 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x00, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x21 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00007B0B + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x00, table=0(DC),0(AC) + Component[2]: selector=0x01, table=1(DC),1(AC) + Component[3]: selector=0x02, table=1(DC),1(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000083AB + Huffman table length = 31 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (000 total): + Codes of length 04 bits (003 total): 10 11 20 + Codes of length 05 bits (000 total): + Codes of length 06 bits (003 total): 21 30 31 + Codes of length 07 bits (001 total): 41 + Codes of length 08 bits (001 total): 51 + Codes of length 09 bits (001 total): 40 + Codes of length 10 bits (001 total): 61 + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000083CC + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=1(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000088BF + Huffman table length = 31 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (003 total): 00 10 11 + Codes of length 04 bits (001 total): 20 + Codes of length 05 bits (001 total): 30 + Codes of length 06 bits (000 total): + Codes of length 07 bits (003 total): 21 31 41 + Codes of length 08 bits (001 total): 40 + Codes of length 09 bits (001 total): 51 + Codes of length 10 bits (001 total): 71 + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000088E0 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=1(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00008C75 + Huffman table length = 41 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (002 total): 21 31 + Codes of length 05 bits (002 total): 41 51 + Codes of length 06 bits (001 total): 61 + Codes of length 07 bits (003 total): 10 71 81 + Codes of length 08 bits (003 total): 91 A1 B1 + Codes of length 09 bits (005 total): 20 30 C1 D1 F1 + Codes of length 10 bits (001 total): E1 + Codes of length 11 bits (001 total): F0 + Codes of length 12 bits (001 total): 40 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 022 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00008CA0 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x00, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0000EDFD + + +*** Searching Compression Signatures *** + + Signature: 01B8FDD60747E53114DC15797CC09B4E + Signature (Rotated): 011975EE86201F10E48E4F365C73A839 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + SW :[IJG Library ] [071 ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [071 ] + SW :[IrfanView ] [071 ] + SW :[idImager ] [071 ] + SW :[FastStone Image Viewer ] [071 ] + SW :[NeatImage ] [071 ] + SW :[Paint.NET ] [071 ] + SW :[Photomatix ] [071 ] + SW :[XnView ] [071 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue178-BadCoeffsProgressive-Lemon.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue178-BadCoeffsProgressive-Lemon.jpg.txt new file mode 100644 index 0000000000..af39df365c --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue178-BadCoeffsProgressive-Lemon.jpg.txt @@ -0,0 +1,471 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue178-BadCoeffsProgressive-Lemon.jpg] + Filesize: [279270] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 1 x 1 (aspect ratio) + thumbnail = 0 x 0 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000014 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 1 1 1 1 1 1 1 1 + DQT, Row #1: 1 1 1 1 1 1 1 1 + DQT, Row #2: 1 1 1 1 1 1 1 1 + DQT, Row #3: 1 1 1 1 1 2 2 1 + DQT, Row #4: 1 1 1 1 1 2 2 2 + DQT, Row #5: 1 1 1 1 2 2 2 2 + DQT, Row #6: 1 1 2 2 2 2 2 2 + DQT, Row #7: 1 2 2 2 2 2 2 2 + Approx quality factor = 98.32 (scaling=3.35 variance=5.00) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000059 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 1 1 1 1 2 2 2 2 + DQT, Row #1: 1 1 1 1 2 2 2 2 + DQT, Row #2: 1 1 1 2 2 2 2 2 + DQT, Row #3: 1 1 2 2 2 2 2 2 + DQT, Row #4: 2 2 2 2 2 2 2 2 + DQT, Row #5: 2 2 2 2 2 2 2 2 + DQT, Row #6: 2 2 2 2 2 2 2 2 + DQT, Row #7: 2 2 2 2 2 2 2 2 + Approx quality factor = 98.83 (scaling=2.34 variance=0.89) + +*** Marker: SOF2 (Progressive DCT, Huffman) (xFFC2) *** + OFFSET: 0x0000009E + Frame header length = 17 + Precision = 8 + Number of Lines = 710 + Samples per Line = 710 + Image Size = 710 x 710 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000B1 + Huffman table length = 30 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 05 + Codes of length 03 bits (004 total): 03 04 06 07 + Codes of length 04 bits (003 total): 00 02 08 + Codes of length 05 bits (001 total): 01 + Codes of length 06 bits (001 total): 09 + Codes of length 07 bits (001 total): 0A + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 011 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000D1 + Huffman table length = 29 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 03 + Codes of length 03 bits (005 total): 00 01 02 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 010 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000000F0 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),0(AC) + Component[3]: selector=0x03, table=1(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00002D06 + Huffman table length = 49 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 02 + Codes of length 03 bits (004 total): 00 01 03 04 + Codes of length 04 bits (002 total): 05 11 + Codes of length 05 bits (002 total): 06 12 + Codes of length 06 bits (002 total): 13 21 + Codes of length 07 bits (002 total): 07 14 + Codes of length 08 bits (001 total): 22 + Codes of length 09 bits (003 total): 10 15 31 + Codes of length 10 bits (004 total): 16 23 32 41 + Codes of length 11 bits (001 total): 20 + Codes of length 12 bits (004 total): 08 30 33 50 + Codes of length 13 bits (003 total): 24 40 42 + Codes of length 14 bits (001 total): 34 + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 030 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00002D39 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 5 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00007ADB + Huffman table length = 72 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (003 total): 00 02 03 + Codes of length 04 bits (002 total): 04 11 + Codes of length 05 bits (003 total): 12 21 31 + Codes of length 06 bits (005 total): 05 22 41 51 61 + Codes of length 07 bits (005 total): 06 13 71 81 A1 + Codes of length 08 bits (005 total): 14 32 91 B1 F0 + Codes of length 09 bits (006 total): 23 42 52 C1 D1 E1 + Codes of length 10 bits (004 total): 10 15 33 62 + Codes of length 11 bits (003 total): 07 82 F1 + Codes of length 12 bits (006 total): 20 24 30 43 72 A2 + Codes of length 13 bits (006 total): 16 34 53 92 B2 E2 + Codes of length 14 bits (003 total): 25 44 93 + Codes of length 15 bits (001 total): D2 + Codes of length 16 bits (000 total): + Total number of codes: 053 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00007B25 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000A7E3 + Huffman table length = 74 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (002 total): 02 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (005 total): 05 12 21 31 41 + Codes of length 06 bits (005 total): 06 13 22 51 61 + Codes of length 07 bits (004 total): 32 71 81 91 + Codes of length 08 bits (006 total): 14 42 52 A1 B1 F0 + Codes of length 09 bits (008 total): 07 10 15 23 62 C1 D1 E1 + Codes of length 10 bits (004 total): 33 43 72 92 + Codes of length 11 bits (004 total): 24 82 A2 F1 + Codes of length 12 bits (004 total): 16 34 63 D2 + Codes of length 13 bits (006 total): 20 40 53 B2 C2 E2 + Codes of length 14 bits (003 total): 17 25 30 + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 055 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000A82F + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000E96D + Huffman table length = 84 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (002 total): 02 11 + Codes of length 04 bits (003 total): 00 03 21 + Codes of length 05 bits (005 total): 04 12 31 41 51 + Codes of length 06 bits (004 total): 05 22 61 71 + Codes of length 07 bits (006 total): 13 32 81 91 A1 F0 + Codes of length 08 bits (008 total): 10 14 23 42 52 B1 C1 D1 + Codes of length 09 bits (003 total): 33 62 E1 + Codes of length 10 bits (005 total): 06 72 82 92 F1 + Codes of length 11 bits (006 total): 15 20 24 43 53 A2 + Codes of length 12 bits (004 total): 30 34 63 73 + Codes of length 13 bits (004 total): 44 83 B2 C2 + Codes of length 14 bits (005 total): 40 50 93 A3 D2 + Codes of length 15 bits (002 total): 16 25 + Codes of length 16 bits (007 total): 26 35 54 60 64 B3 E2 + Total number of codes: 065 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000E9C3 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 6 .. 63 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0001AD55 + Huffman table length = 40 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 11 21 + Codes of length 04 bits (002 total): 00 31 + Codes of length 05 bits (002 total): 41 51 + Codes of length 06 bits (002 total): 61 71 + Codes of length 07 bits (002 total): 81 91 + Codes of length 08 bits (002 total): A1 B1 + Codes of length 09 bits (003 total): C1 D1 F0 + Codes of length 10 bits (000 total): + Codes of length 11 bits (003 total): 10 E1 F1 + Codes of length 12 bits (001 total): 20 + Codes of length 13 bits (001 total): 30 + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 021 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0001AD7F + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x21 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000287B1 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=0(DC),0(AC) + Component[3]: selector=0x03, table=0(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00028DB3 + Huffman table length = 39 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 11 21 + Codes of length 04 bits (002 total): 00 31 + Codes of length 05 bits (001 total): 41 + Codes of length 06 bits (003 total): 51 61 71 + Codes of length 07 bits (003 total): 81 91 F0 + Codes of length 08 bits (005 total): A1 B1 C1 D1 E1 + Codes of length 09 bits (001 total): F1 + Codes of length 10 bits (001 total): 10 + Codes of length 11 bits (001 total): 20 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 020 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00028DDC + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0002CD0C + Huffman table length = 41 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 11 21 + Codes of length 04 bits (002 total): 00 31 + Codes of length 05 bits (002 total): 41 51 + Codes of length 06 bits (001 total): 61 + Codes of length 07 bits (004 total): 71 81 91 F0 + Codes of length 08 bits (002 total): A1 B1 + Codes of length 09 bits (003 total): C1 D1 E1 + Codes of length 10 bits (001 total): F1 + Codes of length 11 bits (001 total): 30 + Codes of length 12 bits (001 total): 10 + Codes of length 13 bits (001 total): 20 + Codes of length 14 bits (001 total): 40 + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 022 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0002CD37 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00031187 + Huffman table length = 38 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (001 total): 11 + Codes of length 03 bits (001 total): 21 + Codes of length 04 bits (000 total): + Codes of length 05 bits (003 total): 00 31 41 + Codes of length 06 bits (000 total): + Codes of length 07 bits (002 total): 51 61 + Codes of length 08 bits (002 total): 71 81 + Codes of length 09 bits (002 total): 91 A1 + Codes of length 10 bits (002 total): B1 C1 + Codes of length 11 bits (003 total): D1 E1 F0 + Codes of length 12 bits (001 total): F1 + Codes of length 13 bits (001 total): 10 + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 019 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000311AF + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x000442E4 + + +*** Searching Compression Signatures *** + + Signature: 01C7F83908166C226C06A44017421732 + Signature (Rotated): 01D3EFDD3855C42AE3E0E6289F1A6726 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[Canon ] [Canon EOS-1Ds Mark II ] [fine ] No + CAM:[CASIO COMPUTER CO.,LTD. ] [EX-Z1000 ] [ ] Yes + CAM:[NIKON ] [NIKON D2X ] [FINE ] No + CAM:[NIKON ] [NIKON D3 ] [FINE ] No + CAM:[SIGMA ] [SIGMA SD10 ] [Qual:12 ] No + CAM:[SIGMA ] [SIGMA SD10 ] [Qual:12 ] No + CAM:[SIGMA ] [SIGMA SD14 ] [Qual:12 ] No + CAM:[SIGMA ] [SIGMA SD14 ] [Qual:12 ] No + CAM:[SONY ] [DSC-H2 ] [ ] No + CAM:[SONY ] [DSC-R1 ] [ ] No + CAM:[SONY ] [DSC-W7 ] [ ] No + SW :[Digital Photo Professiona] [09 ] + SW :[IJG Library ] [099 ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [099 ] + SW :[IrfanView ] [099 ] + SW :[idImager ] [099 ] + SW :[FastStone Image Viewer ] [099 ] + SW :[NeatImage ] [099 ] + SW :[Paint.NET ] [099 ] + SW :[Photomatix ] [099 ] + SW :[XnView ] [099 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue214-CriticalEOF .jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue214-CriticalEOF .jpg.txt new file mode 100644 index 0000000000..f5b6d277d7 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue214-CriticalEOF .jpg.txt @@ -0,0 +1,94 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue214-CriticalEOF .jpg] + Filesize: [35601] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 300 x 300 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000014 + Length = 39251 + Identifier = [Exif] + Identifier TIFF = 0x[49492A00 08000000] + Endian = Intel (little) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000026 + Dir Length = 0x0015 + [Make ] = "NIKON CORPORATION" + [Model ] = "NIKON D40" + [Orientation ] = 1 = Row 0: top, Col 0: left + [XResolution ] = 300/1 + [YResolution ] = 300/1 + [ResolutionUnit ] = Inch + [Software ] = "Ver.1.10 " + [DateTime ] = "2009:02:17 08:30:16" + [YCbCrPositioning ] = Co-sited + [ExifOffset ] = @ 0x015C + [CustomRendered ] = Normal process + [ExposureMode ] = Auto exposure + [WhiteBalance ] = Auto white balance + [SceneCaptureType ] = Standard + Offset to Next IFD = 0x00007652 + + EXIF IFD1 @ Absolute 0x00007670 + Dir Length = 0x0007 + [Compression ] = JPEG + [XResolution ] = 300/1 + [YResolution ] = 300/1 + [ResolutionUnit ] = Inch + [JpegIFOffset ] = @ +0x76BC = @ 0x76DA + [JpegIFByteCount ] = 0x[0000228F] / 8847 + [YCbCrPositioning ] = Co-sited + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x0000017A + Dir Length = 0x001C + [ExposureTime ] = 10/1250 s + [FNumber ] = F5.6 + [ExposureProgram ] = Not defined + [ISOSpeedRatings ] = 220 + [ExifVersion ] = 02.21 + [DateTimeOriginal ] = "2009:02:17 08:30:16" + [DateTimeDigitized ] = "2009:02:17 08:30:15" + [ComponentsConfiguration ] = [Y Cb Cr .] + [CompressedBitsPerPixel ] = 1/1 + [ExposureBiasValue ] = 0.00 eV + [MaxApertureValue ] = 41/10 + [MeteringMode ] = Pattern + [LightSource ] = unknown + [Flash ] = Flash did not fire + [FocalLength ] = 30 mm + [MakerNote ] = @ 0x030A + [UserComment ] = " " + [SubSecTime ] = "60" + [SubSecTimeOriginal ] = "60" + [SubSecTimeDigitized ] = "60" + [FlashPixVersion ] = 01.00 + [ColorSpace ] = sRGB + [ExifImageWidth ] = 3008 + [ExifImageHeight ] = 2000 + [SensingMethod ] = One-chip color area sensor + [FileSource ] = DSC + [SceneType ] = A directly photographed image + [CFAPattern ] = + = [ Blu Grn ] + = [ Grn Red ] + + EXIF MakerIFD @ Absolute 0x00000328 + Makernote decode option not enabled. + +ERROR: Early EOF - file may be missing EOI diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue385-BadZigZag-Progressive.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue385-BadZigZag-Progressive.jpg.txt new file mode 100644 index 0000000000..1f98e67dc1 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue385-BadZigZag-Progressive.jpg.txt @@ -0,0 +1,468 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue385-BadZigZag-Progressive.jpg] + Filesize: [388517] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 1 x 1 (aspect ratio) + thumbnail = 0 x 0 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000014 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 5 3 3 5 7 12 15 18 + DQT, Row #1: 4 4 4 6 8 17 18 17 + DQT, Row #2: 4 4 5 7 12 17 21 17 + DQT, Row #3: 4 5 7 9 15 26 24 19 + DQT, Row #4: 5 7 11 17 20 33 31 23 + DQT, Row #5: 7 11 17 19 24 31 34 28 + DQT, Row #6: 15 19 23 26 31 36 36 30 + DQT, Row #7: 22 28 29 29 34 30 31 30 + Approx quality factor = 84.93 (scaling=30.13 variance=1.05) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000059 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 5 5 7 14 30 30 30 30 + DQT, Row #1: 5 6 8 20 30 30 30 30 + DQT, Row #2: 7 8 17 30 30 30 30 30 + DQT, Row #3: 14 20 30 30 30 30 30 30 + DQT, Row #4: 30 30 30 30 30 30 30 30 + DQT, Row #5: 30 30 30 30 30 30 30 30 + DQT, Row #6: 30 30 30 30 30 30 30 30 + DQT, Row #7: 30 30 30 30 30 30 30 30 + Approx quality factor = 84.93 (scaling=30.15 variance=0.29) + +*** Marker: SOF2 (Progressive DCT, Huffman) (xFFC2) *** + OFFSET: 0x0000009E + Frame header length = 17 + Precision = 8 + Number of Lines = 1440 + Samples per Line = 1920 + Image Size = 1920 x 1440 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000B1 + Huffman table length = 28 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (003 total): 02 03 04 + Codes of length 04 bits (001 total): 05 + Codes of length 05 bits (001 total): 06 + Codes of length 06 bits (001 total): 07 + Codes of length 07 bits (001 total): 08 + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000CF + Huffman table length = 25 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (001 total): 04 + Codes of length 06 bits (001 total): 05 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 006 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000000EA + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),0(AC) + Component[3]: selector=0x03, table=1(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00007E0B + Huffman table length = 55 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (002 total): 02 11 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (003 total): 04 12 21 + Codes of length 06 bits (002 total): 05 10 + Codes of length 07 bits (004 total): 13 20 22 31 + Codes of length 08 bits (004 total): 06 14 30 41 + Codes of length 09 bits (005 total): 15 32 33 34 40 + Codes of length 10 bits (005 total): 23 24 35 42 50 + Codes of length 11 bits (000 total): + Codes of length 12 bits (002 total): 25 70 + Codes of length 13 bits (003 total): 16 44 60 + Codes of length 14 bits (001 total): 36 + Codes of length 15 bits (001 total): 45 + Codes of length 16 bits (001 total): 43 + Total number of codes: 036 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00007E44 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 5 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00012C0C + Huffman table length = 46 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (004 total): 02 10 12 20 + Codes of length 05 bits (000 total): + Codes of length 06 bits (004 total): 03 21 30 31 + Codes of length 07 bits (006 total): 13 32 40 41 50 51 + Codes of length 08 bits (002 total): 22 61 + Codes of length 09 bits (002 total): 04 60 + Codes of length 10 bits (003 total): 23 33 71 + Codes of length 11 bits (001 total): 42 + Codes of length 12 bits (001 total): 05 + Codes of length 13 bits (001 total): 43 + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 027 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00012C3C + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00013C45 + Huffman table length = 43 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (004 total): 02 10 12 20 + Codes of length 05 bits (001 total): 30 + Codes of length 06 bits (004 total): 03 21 31 40 + Codes of length 07 bits (002 total): 41 50 + Codes of length 08 bits (002 total): 13 51 + Codes of length 09 bits (002 total): 22 60 + Codes of length 10 bits (003 total): 32 42 61 + Codes of length 11 bits (001 total): 04 + Codes of length 12 bits (001 total): 81 + Codes of length 13 bits (001 total): 71 + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 024 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00013C72 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000148F2 + Huffman table length = 75 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (000 total): + Codes of length 04 bits (004 total): 02 11 21 31 + Codes of length 05 bits (002 total): 03 10 + Codes of length 06 bits (006 total): 12 20 22 32 41 51 + Codes of length 07 bits (007 total): 04 30 33 61 71 81 91 + Codes of length 08 bits (005 total): 13 40 50 52 72 + Codes of length 09 bits (007 total): 23 34 42 62 82 92 A1 + Codes of length 10 bits (001 total): B1 + Codes of length 11 bits (006 total): 14 60 70 73 C1 E1 + Codes of length 12 bits (005 total): 05 63 83 A2 D1 + Codes of length 13 bits (004 total): 24 43 53 93 + Codes of length 14 bits (002 total): 74 F0 + Codes of length 15 bits (002 total): 15 B2 + Codes of length 16 bits (003 total): 35 E2 F1 + Total number of codes: 056 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0001493F + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 6 .. 63 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0001BD13 + Huffman table length = 44 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 11 + Codes of length 03 bits (000 total): + Codes of length 04 bits (002 total): 21 31 + Codes of length 05 bits (002 total): 41 51 + Codes of length 06 bits (002 total): 10 61 + Codes of length 07 bits (001 total): 71 + Codes of length 08 bits (004 total): 20 81 91 A1 + Codes of length 09 bits (002 total): 30 B1 + Codes of length 10 bits (002 total): C1 F0 + Codes of length 11 bits (003 total): 40 D1 F1 + Codes of length 12 bits (001 total): E1 + Codes of length 13 bits (001 total): 50 + Codes of length 14 bits (001 total): 60 + Codes of length 15 bits (001 total): 70 + Codes of length 16 bits (000 total): + Total number of codes: 025 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0001BD41 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x21 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0002C20D + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=0(DC),0(AC) + Component[3]: selector=0x03, table=0(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0002E1DF + Huffman table length = 37 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (000 total): + Codes of length 05 bits (002 total): 21 31 + Codes of length 06 bits (002 total): 10 41 + Codes of length 07 bits (003 total): 20 51 61 + Codes of length 08 bits (001 total): 71 + Codes of length 09 bits (001 total): 30 + Codes of length 10 bits (001 total): 81 + Codes of length 11 bits (001 total): 40 + Codes of length 12 bits (001 total): 91 + Codes of length 13 bits (001 total): A1 + Codes of length 14 bits (001 total): B1 + Codes of length 15 bits (001 total): C1 + Codes of length 16 bits (000 total): + Total number of codes: 018 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0002E206 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000313D7 + Huffman table length = 36 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 11 + Codes of length 03 bits (000 total): + Codes of length 04 bits (003 total): 10 21 31 + Codes of length 05 bits (000 total): + Codes of length 06 bits (003 total): 20 41 51 + Codes of length 07 bits (001 total): 61 + Codes of length 08 bits (001 total): 71 + Codes of length 09 bits (001 total): 30 + Codes of length 10 bits (001 total): 81 + Codes of length 11 bits (001 total): 40 + Codes of length 12 bits (001 total): 91 + Codes of length 13 bits (001 total): B1 + Codes of length 14 bits (001 total): A1 + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 017 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000313FD + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00033E31 + Huffman table length = 40 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (002 total): 21 31 + Codes of length 05 bits (002 total): 41 51 + Codes of length 06 bits (002 total): 61 71 + Codes of length 07 bits (002 total): 81 91 + Codes of length 08 bits (002 total): A1 B1 + Codes of length 09 bits (003 total): 10 C1 D1 + Codes of length 10 bits (001 total): F0 + Codes of length 11 bits (001 total): E1 + Codes of length 12 bits (001 total): F1 + Codes of length 13 bits (001 total): 20 + Codes of length 14 bits (001 total): 30 + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 021 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00033E5B + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0005EDA3 + + +*** Searching Compression Signatures *** + + Signature: 0155D875C95B74D0F3C5835A62516F48 + Signature (Rotated): 01D38A25358EB7649A254E19F1D46600 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[NIKON ] [E2500 ] [FINE ] No + CAM:[Nokia ] [N73 ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C2000Z ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C3040Z ] [ ] No + CAM:[PENTAX ] [PENTAX Optio 550 ] [ ] No + CAM:[Research In Motion ] [BlackBerry 8100 ] [ ] No + CAM:[SEIKO EPSON CORP. ] [PhotoPC 3000Z ] [ ] No + SW :[IJG Library ] [085 ] + SW :[Picasa ] [085 (Normal) ] + SW :[ZoomBrowser EX ] [medium ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [085 ] + SW :[IrfanView ] [085 ] + SW :[idImager ] [085 ] + SW :[FastStone Image Viewer ] [085 ] + SW :[NeatImage ] [085 ] + SW :[Paint.NET ] [085 ] + SW :[Photomatix ] [085 ] + SW :[XnView ] [085 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue394-MultiHuffmanBaseline-Speakers.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue394-MultiHuffmanBaseline-Speakers.jpg.txt new file mode 100644 index 0000000000..22e9a99dd2 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue394-MultiHuffmanBaseline-Speakers.jpg.txt @@ -0,0 +1,438 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue394-MultiHuffmanBaseline-Speakers.jpg] + Filesize: [257401] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x00000002 + Length = 576 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 1 of 1 + Profile Size : 560 bytes + Preferred CMM Type : 'ADBE' (0x41444245) + Profile Version : 0.2.1.0 (0x02100000) + Profile Device/Class : Display Device profile ('mntr' (0x6D6E7472)) + Data Colour Space : rgbData ('RGB ' (0x52474220)) + Profile connection space (PCS) : 'XYZ ' (0x58595A20) + Profile creation date : 1999-06-03 00:00:00 + Profile file signature : 'acsp' (0x61637370) + Primary platform : Apple Computer, Inc. ('APPL' (0x4150504C)) + Profile flags : 0x00000000 + Profile flags > Profile not embedded + Profile flags > Profile can't be used independently of embedded + Device Manufacturer : 'none' (0x6E6F6E65) + Device Model : '....' (0x00000000) + Device attributes : 0x00000000_00000000 + Device attributes > Reflective + Device attributes > Glossy + Device attributes > Media polarity = negative + Device attributes > Black & white media + Rendering intent : Perceptual + Profile creator : 'ADBE' (0x41444245) + Profile ID : 0x00000000_00000000_00000000_00000000 + +*** Marker: APP13 (xFFED) *** + OFFSET: 0x00000244 + Length = 90 + Identifier = [Photoshop 3.0] + 8BIM: [0x0404] Name="" Len=[0x003D] DefinedName="IPTC-NAA record" + IPTC [001:090] Coded Character Set = "%G" + IPTC [002:000] Record Version = 3 + IPTC [002:055] Date Created = "20161215" + IPTC [002:060] Time Created = "043026-0600" + IPTC [002:221] ? = ??? + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x000002A0 + Table length = 132 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 6 4 4 6 9 11 12 16 + DQT, Row #1: 4 5 5 6 8 10 12 12 + DQT, Row #2: 4 5 5 6 10 12 12 12 + DQT, Row #3: 6 6 6 11 12 12 12 12 + DQT, Row #4: 9 8 10 12 12 12 12 12 + DQT, Row #5: 11 10 12 12 12 12 12 12 + DQT, Row #6: 12 12 12 12 12 12 12 12 + DQT, Row #7: 16 12 12 12 12 12 12 12 + Approx quality factor = 88.28 (scaling=23.43 variance=111.68) + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 7 7 13 24 20 20 17 17 + DQT, Row #1: 7 12 16 14 14 12 12 12 + DQT, Row #2: 13 16 14 14 12 12 12 12 + DQT, Row #3: 24 14 14 12 12 12 12 12 + DQT, Row #4: 20 14 12 12 12 12 12 12 + DQT, Row #5: 20 12 12 12 12 12 12 12 + DQT, Row #6: 17 12 12 12 12 12 12 12 + DQT, Row #7: 17 12 12 12 12 12 12 12 + Approx quality factor = 90.19 (scaling=19.62 variance=201.04) + +*** Marker: DRI (Restart Interval) (xFFDD) *** + OFFSET: 0x00000326 + Length = 4 + interval = 115 + +*** Marker: APP14 (xFFEE) *** + OFFSET: 0x0000032C + Length = 14 + DCTEncodeVersion = 100 + APP14Flags0 = 49152 + APP14Flags1 = 0 + ColorTransform = 1 [YCbCr] + +*** Marker: SOF1 (Extended Sequential DCT, Huffman) (xFFC1) *** + OFFSET: 0x0000033C + Frame header length = 17 + Precision = 8 + Number of Lines = 496 + Samples per Line = 920 + Image Size = 920 x 496 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x00, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x02, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000034F + Huffman table length = 626 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 05 + Codes of length 03 bits (005 total): 02 03 04 06 07 + Codes of length 04 bits (001 total): 01 + Codes of length 05 bits (001 total): 08 + Codes of length 06 bits (001 total): 00 + Codes of length 07 bits (000 total): + Codes of length 08 bits (003 total): 09 0A 0B + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 02 03 + Codes of length 03 bits (003 total): 01 04 05 + Codes of length 04 bits (001 total): 00 + Codes of length 05 bits (001 total): 06 + Codes of length 06 bits (000 total): + Codes of length 07 bits (002 total): 07 08 + Codes of length 08 bits (003 total): 09 0A 0B + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + ---- + Destination ID = 2 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (003 total): 00 03 04 + Codes of length 04 bits (001 total): 05 + Codes of length 05 bits (000 total): + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (005 total): 07 08 09 0A 0B + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (002 total): 03 11 + Codes of length 04 bits (002 total): 04 12 + Codes of length 05 bits (001 total): 21 + Codes of length 06 bits (003 total): 00 05 13 + Codes of length 07 bits (002 total): 22 31 + Codes of length 08 bits (004 total): 06 14 32 41 + Codes of length 09 bits (002 total): 23 51 + Codes of length 10 bits (003 total): 15 42 61 + Codes of length 11 bits (005 total): 07 16 33 52 71 + Codes of length 12 bits (006 total): 24 43 62 81 91 F0 + Codes of length 13 bits (006 total): 25 34 72 A1 B1 C1 + Codes of length 14 bits (012 total): 08 18 26 46 53 63 82 92 93 D1 D2 F1 + Codes of length 15 bits (111 total): 09 0A 17 19 1A 27 28 29 2A 35 36 37 38 39 3A 44 + 45 47 48 49 4A 54 55 56 57 58 59 5A 64 65 66 67 + 68 69 6A 73 74 75 76 77 78 79 7A 83 84 85 86 87 + 88 89 8A 94 95 96 97 98 99 9A A2 A3 A4 A5 A6 A7 + A8 A9 AA B2 B3 B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 + C6 C7 C8 C9 CA D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 + E4 E5 E6 E7 E8 E9 EA F2 F3 F4 F5 F6 F7 F8 F9 + Codes of length 16 bits (001 total): FA + Total number of codes: 162 + + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (003 total): 02 21 31 + Codes of length 05 bits (004 total): 41 51 61 F0 + Codes of length 06 bits (006 total): 03 12 13 71 81 91 + Codes of length 07 bits (006 total): 14 A1 B1 C1 D1 E1 + Codes of length 08 bits (002 total): 04 F1 + Codes of length 09 bits (002 total): 22 32 + Codes of length 10 bits (002 total): 52 62 + Codes of length 11 bits (004 total): 05 42 72 A2 + Codes of length 12 bits (125 total): 06 07 08 09 0A 15 16 17 18 19 1A 23 24 25 26 27 + 28 29 2A 33 34 35 36 37 38 39 3A 43 44 45 46 47 + 48 49 4A 53 54 55 56 57 58 59 5A 63 64 65 66 67 + 68 69 6A 73 74 75 76 77 78 79 7A 82 83 84 85 86 + 87 88 89 8A 92 93 94 95 96 97 98 99 9A A3 A4 A5 + A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 B8 B9 BA C2 C3 + C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 D6 D7 D8 D9 DA + E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 F4 F5 + Codes of length 13 bits (005 total): F6 F7 F8 F9 FA + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 162 + + ---- + Destination ID = 2 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (001 total): 02 + Codes of length 05 bits (005 total): 12 21 31 41 51 + Codes of length 06 bits (004 total): 03 61 71 F0 + Codes of length 07 bits (004 total): 13 81 91 A1 + Codes of length 08 bits (006 total): 04 14 C1 D1 E1 F1 + Codes of length 09 bits (002 total): 22 B1 + Codes of length 10 bits (001 total): 42 + Codes of length 11 bits (004 total): 32 52 92 D2 + Codes of length 12 bits (131 total): 05 06 07 08 09 0A 15 16 17 18 19 1A 23 24 25 26 + 27 28 29 2A 33 34 35 36 37 38 39 3A 43 44 45 46 + 47 48 49 4A 53 54 55 56 57 58 59 5A 62 63 64 65 + 66 67 68 69 6A 72 73 74 75 76 77 78 79 7A 82 83 + 84 85 86 87 88 89 8A 93 94 95 96 97 98 99 9A A2 + A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 B8 B9 + BA C2 C3 C4 C5 C6 C7 C8 C9 CA D3 D4 D5 D6 D7 D8 + D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 F4 F5 F6 + F7 F8 F9 + Codes of length 13 bits (001 total): FA + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 162 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000005C3 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x00, table=0(DC),0(AC) + Component[2]: selector=0x01, table=1(DC),1(AC) + Component[3]: selector=0x02, table=2(DC),2(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x000005D1 + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x0003ED77.0 + + Compression stats: + Compression Ratio: 5.35:1 + Bits per pixel: 4.49:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 1109 ( 16%) + # codes of length 03 bits: 4934 ( 69%) + # codes of length 04 bits: 705 ( 10%) + # codes of length 05 bits: 22 ( 0%) + # codes of length 06 bits: 360 ( 5%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 2599 ( 36%) + # codes of length 03 bits: 2938 ( 41%) + # codes of length 04 bits: 1592 ( 22%) + # codes of length 05 bits: 1 ( 0%) + # codes of length 06 bits: 0 ( 0%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 2, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 3838 ( 54%) + # codes of length 03 bits: 3132 ( 44%) + # codes of length 04 bits: 156 ( 2%) + # codes of length 05 bits: 0 ( 0%) + # codes of length 06 bits: 4 ( 0%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 170962 ( 54%) + # codes of length 03 bits: 67518 ( 21%) + # codes of length 04 bits: 33616 ( 11%) + # codes of length 05 bits: 9306 ( 3%) + # codes of length 06 bits: 15458 ( 5%) + # codes of length 07 bits: 7462 ( 2%) + # codes of length 08 bits: 6393 ( 2%) + # codes of length 09 bits: 1640 ( 1%) + # codes of length 10 bits: 1220 ( 0%) + # codes of length 11 bits: 975 ( 0%) + # codes of length 12 bits: 581 ( 0%) + # codes of length 13 bits: 213 ( 0%) + # codes of length 14 bits: 134 ( 0%) + # codes of length 15 bits: 75 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 11236 ( 26%) + # codes of length 03 bits: 12123 ( 28%) + # codes of length 04 bits: 7424 ( 17%) + # codes of length 05 bits: 5864 ( 13%) + # codes of length 06 bits: 4420 ( 10%) + # codes of length 07 bits: 1997 ( 5%) + # codes of length 08 bits: 545 ( 1%) + # codes of length 09 bits: 244 ( 1%) + # codes of length 10 bits: 61 ( 0%) + # codes of length 11 bits: 41 ( 0%) + # codes of length 12 bits: 31 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 2, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 15434 ( 46%) + # codes of length 03 bits: 3540 ( 11%) + # codes of length 04 bits: 2524 ( 8%) + # codes of length 05 bits: 5638 ( 17%) + # codes of length 06 bits: 3224 ( 10%) + # codes of length 07 bits: 1556 ( 5%) + # codes of length 08 bits: 1170 ( 3%) + # codes of length 09 bits: 277 ( 1%) + # codes of length 10 bits: 14 ( 0%) + # codes of length 11 bits: 111 ( 0%) + # codes of length 12 bits: 34 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[ 97] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 762, 70, -70] RGB=[210,226,237] @ MCU[ 56, 4] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 61 + Next position in scan buffer: Offset 0x0003ED76.3 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0003ED77 + + +*** Searching Compression Signatures *** + + Signature: 01180AF3DE63318828A86409EF4013DD + Signature (Rotated): 01180AF3DE63318828A86409EF4013DD + File Offset: 0 bytes + Chroma subsampling: 1x1 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + SW :[Adobe Photoshop ] [Save As 08 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue517-No-EOI-Progressive.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue517-No-EOI-Progressive.jpg.txt new file mode 100644 index 0000000000..47e77a4f41 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue517-No-EOI-Progressive.jpg.txt @@ -0,0 +1,406 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue517-No-EOI-Progressive.jpg] + Filesize: [2192567] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 500 x 500 (aspect ratio) + thumbnail = 0 x 0 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000014 + Length = 248 + Identifier = [Exif] + Identifier TIFF = 0x[4D4D002A 00000008] + Endian = Motorola (big) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000026 + Dir Length = 0x0007 + [Orientation ] = 1 = Row 0: top, Col 0: left + [XResolution ] = 500/1 + [YResolution ] = 500/1 + [ResolutionUnit ] = Inch + [Software ] = "Adobe Photoshop CS6 (Macintosh)" + [DateTime ] = "2018:01:06 12:59:23" + [ExifOffset ] = @ 0x00A6 + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x000000C4 + Dir Length = 0x0004 + [DateTimeDigitized ] = "2018:01:06 04:40:19" + [ColorSpace ] = sRGB + [ExifImageWidth ] = 0x[000008CA] / 2250 + [ExifImageHeight ] = 0x[000008CA] / 2250 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x0000010E + Length = 4875 + Identifier = [http://ns.adobe.com/xap/1.0/] + XMP = + +*** Marker: APP13 (xFFED) *** + OFFSET: 0x0000141B + Length = 100 + Identifier = [Photoshop 3.0] + 8BIM: [0x0404] Name="" Len=[0x002C] DefinedName="IPTC-NAA record" + IPTC [001:090] Coded Character Set = "%G" + IPTC [002:000] Record Version = 2 + IPTC [002:062] Digital Creation Date = "20180106" + IPTC [002:063] Digital Creation Time = "044019-0500" + 8BIM: [0x0425] Name="" Len=[0x0010] DefinedName="Caption digest" + Caption digest = | 0x5D 51 F3 F0 D0 DE FC 5F 94 67 16 6F B1 02 A3 89 | ]Q....._.g.o.... + +*** Marker: SOF2 (Progressive DCT, Huffman) (xFFC2) *** + OFFSET: 0x00001481 + Frame header length = 17 + Precision = 8 + Number of Lines = 2250 + Samples per Line = 2250 + Image Size = 2250 x 2250 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00001494 + Huffman table length = 31 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 03 + Codes of length 03 bits (005 total): 02 04 01 05 00 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (001 total): 0A + Codes of length 09 bits (001 total): 0B + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000014B5 + Huffman table length = 195 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (003 total): 02 00 03 + Codes of length 04 bits (003 total): 11 04 12 + Codes of length 05 bits (002 total): 21 05 + Codes of length 06 bits (004 total): 31 13 22 10 + Codes of length 07 bits (003 total): 06 41 51 + Codes of length 08 bits (004 total): 32 14 61 71 + Codes of length 09 bits (006 total): 23 07 81 20 91 42 + Codes of length 10 bits (004 total): 15 A1 52 33 + Codes of length 11 bits (007 total): B1 24 62 30 16 C1 72 + Codes of length 12 bits (006 total): D1 43 92 34 82 08 + Codes of length 13 bits (004 total): E1 53 40 25 + Codes of length 14 bits (008 total): 63 17 35 F0 93 73 A2 50 + Codes of length 15 bits (006 total): 44 B2 83 F1 26 54 + Codes of length 16 bits (115 total): 36 64 94 74 C2 60 D2 84 A3 18 70 E2 27 45 37 65 + B3 55 75 A4 95 C3 85 F2 D3 46 76 80 E3 47 56 66 + B4 09 0A 19 1A 28 29 2A 38 39 3A 48 49 4A 57 58 + 59 5A 67 68 69 6A 77 78 79 7A 86 87 88 89 8A 90 + 96 97 98 99 9A A0 A5 A6 A7 A8 A9 AA B0 B5 B6 B7 + B8 B9 BA C0 C4 C5 C6 C7 C8 C9 CA D0 D4 D5 D6 D7 + D8 D9 DA E0 E4 E5 E6 E7 E8 E9 EA F3 F4 F5 F6 F7 + F8 F9 FA + Total number of codes: 176 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000157A + Huffman table length = 31 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 01 02 00 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (001 total): 07 + Codes of length 08 bits (001 total): 08 + Codes of length 09 bits (001 total): 09 + Codes of length 10 bits (001 total): 0A + Codes of length 11 bits (001 total): 0B + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000159B + Huffman table length = 195 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 00 + Codes of length 03 bits (002 total): 02 11 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (003 total): 10 12 21 + Codes of length 06 bits (003 total): 04 20 31 + Codes of length 07 bits (003 total): 41 13 05 + Codes of length 08 bits (002 total): 30 22 + Codes of length 09 bits (003 total): 32 51 14 + Codes of length 10 bits (005 total): 40 06 33 23 61 + Codes of length 11 bits (002 total): 42 15 + Codes of length 12 bits (005 total): 71 52 34 81 50 + Codes of length 13 bits (002 total): 24 91 + Codes of length 14 bits (004 total): A1 43 B1 16 + Codes of length 15 bits (004 total): 07 62 35 53 + Codes of length 16 bits (135 total): F0 D1 25 60 C1 44 E1 72 F1 17 82 63 36 70 26 45 + 54 92 27 A2 D2 08 09 0A 18 19 1A 28 29 2A 37 38 + 39 3A 46 47 48 49 4A 55 56 57 58 59 5A 64 65 66 + 67 68 69 6A 73 74 75 76 77 78 79 7A 80 83 84 85 + 86 87 88 89 8A 90 93 94 95 96 97 98 99 9A A0 A3 + A4 A5 A6 A7 A8 A9 AA B0 B2 B3 B4 B5 B6 B7 B8 B9 + BA C0 C2 C3 C4 C5 C6 C7 C8 C9 CA D0 D3 D4 D5 D6 + D7 D8 D9 DA E0 E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 + F4 F5 F6 F7 F8 F9 FA + Total number of codes: 176 + + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00001660 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 1 1 1 1 1 1 1 2 + DQT, Row #1: 1 1 1 1 1 1 1 2 + DQT, Row #2: 1 1 1 1 1 1 2 2 + DQT, Row #3: 1 1 1 1 1 2 2 3 + DQT, Row #4: 1 1 1 1 2 2 3 3 + DQT, Row #5: 1 1 1 2 2 3 3 3 + DQT, Row #6: 1 1 2 2 3 3 3 3 + DQT, Row #7: 2 2 2 3 3 3 3 3 + Approx quality factor = 98.11 (scaling=3.79 variance=4.10) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x000016A5 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 1 1 1 2 3 3 3 3 + DQT, Row #1: 1 1 1 2 3 3 3 3 + DQT, Row #2: 1 1 2 3 3 3 3 3 + DQT, Row #3: 2 2 3 3 3 3 3 3 + DQT, Row #4: 3 3 3 3 3 3 3 3 + DQT, Row #5: 3 3 3 3 3 3 3 3 + DQT, Row #6: 3 3 3 3 3 3 3 3 + DQT, Row #7: 3 3 3 3 3 3 3 3 + Approx quality factor = 98.36 (scaling=3.29 variance=0.42) + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000016EA + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00023AAF + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 5 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0003E82C + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0006B107 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=1(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0008AA32 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 6 .. 63 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000BA727 + Huffman table length = 51 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (003 total): 11 00 21 + Codes of length 04 bits (000 total): + Codes of length 05 bits (002 total): 31 41 + Codes of length 06 bits (002 total): 51 61 + Codes of length 07 bits (002 total): 71 81 + Codes of length 08 bits (002 total): 91 A1 + Codes of length 09 bits (002 total): B1 C1 + Codes of length 10 bits (003 total): F0 D1 10 + Codes of length 11 bits (001 total): E1 + Codes of length 12 bits (001 total): F1 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (002 total): 20 30 + Codes of length 16 bits (011 total): 40 50 60 70 80 90 A0 B0 C0 D0 E0 + Total number of codes: 032 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000BA75C + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x21 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000FE1E7 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x001056B6 + Huffman table length = 51 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (000 total): + Codes of length 05 bits (003 total): 21 31 10 + Codes of length 06 bits (000 total): + Codes of length 07 bits (001 total): 41 + Codes of length 08 bits (002 total): 51 61 + Codes of length 09 bits (005 total): 20 71 F0 91 81 + Codes of length 10 bits (005 total): A1 B1 D1 C1 E1 + Codes of length 11 bits (001 total): F1 + Codes of length 12 bits (001 total): 30 + Codes of length 13 bits (000 total): + Codes of length 14 bits (001 total): 40 + Codes of length 15 bits (001 total): 50 + Codes of length 16 bits (009 total): 60 70 80 90 A0 B0 C0 D0 E0 + Total number of codes: 032 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x001056EB + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0014E060 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=1(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x001879C8 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x002174B5 + + +*** Searching Compression Signatures *** + + Signature: 01DADDC4908E9BA57CC067EEAD54E67D + Signature (Rotated): 01DADDC4908E9BA57CC067EEAD54E67D + File Offset: 0 bytes + Chroma subsampling: 1x1 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: OK [Adobe Photoshop CS6 (Macintosh)] + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + SW :[Adobe Photoshop ] [Save As 12 ] + + NOTE: EXIF Software field recognized as from editor + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue518-Bad-RST-Progressive.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue518-Bad-RST-Progressive.jpg.txt new file mode 100644 index 0000000000..1b1027f273 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue518-Bad-RST-Progressive.jpg.txt @@ -0,0 +1,759 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue518-Bad-RST-Progressive.jpg] + Filesize: [3764739] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 300 x 300 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000014 + Length = 14215 + Identifier = [Exif] + Identifier TIFF = 0x[49492A00 08000000] + Endian = Intel (little) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000026 + Dir Length = 0x0009 + [Make ] = "OLYMPUS CORPORATION" + [Model ] = "E-1" + [Orientation ] = 1 = Row 0: top, Col 0: left + [XResolution ] = 300/1 + [YResolution ] = 300/1 + [ResolutionUnit ] = Inch + [Software ] = "GIMP 2.8.10" + [DateTime ] = "2017:04:18 16:37:56" + [ExifOffset ] = @ 0x00BE + Offset to Next IFD = 0x000001DC + + EXIF IFD1 @ Absolute 0x000001FA + Dir Length = 0x0006 + [Compression ] = JPEG + [XResolution ] = 72/1 + [YResolution ] = 72/1 + [ResolutionUnit ] = Inch + [JpegIFOffset ] = @ +0x023A = @ 0x0258 + [JpegIFByteCount ] = 0x[00003545] / 13637 + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x000000DC + Dir Length = 0x0011 + [ExposureTime ] = 1/4 s + [FNumber ] = F18.0 + [ExposureProgram ] = Manual + [ISOSpeedRatings ] = 100 + [ExifVersion ] = 02.21 + [DateTimeOriginal ] = "2005:07:20 20:08:42" + [ShutterSpeedValue ] = 2/1 + [ApertureValue ] = 833985/100000 + [ExposureBiasValue ] = 0.00 eV + [MaxApertureValue ] = 2972656/1000000 + [MeteringMode ] = CenterWeightedAverage + [Flash ] = Flash did not fire + [FocalLength ] = 14 mm + [FlashPixVersion ] = 01.00 + [ColorSpace ] = Uncalibrated + [ExifImageWidth ] = 0x[00000BB8] / 3000 + [ExifImageHeight ] = 0x[00000BB8] / 3000 + +*** Marker: COM (Comment) (xFFFE) *** + OFFSET: 0x0000379D + Comment length = 3 + Comment= + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x000037A2 + Length = 5091 + Identifier = [http://ns.adobe.com/xap/1.0/] + XMP = + | + | + | + | + | 3.1 + | _7201666.ORF + | Custom + | 7450 + | -7 + | -0.75 + | True + | 4 + | True + | 100 + | True + | 0 + | 0 + | 25 + | 0 + | 25 + | 0 + | 0 + | 0 + | 0 + | 0 + | 0 + | 0 + | 0 + | 0 + | 0 + | Medium Contrast + | ACR 2.4 + | True + | False + | True + | + | + | 0, 0 + | 32, 22 + | 64, 56 + | 128, 128 + | 192, 196 + | 255, 255 + | + | + | 3.1 + | _7201666.ORF + | Custom + | 7450 + | -7 + | -0.75 + | True + | 4 + | True + | 100 + | True + | 0 + | 0 + | 25 + | 0 + | 25 + | 0 + | 0 + | 0 + | 0 + | 0 + | 0 + | 0 + | 0 + | 0 + | 0 + | Medium Contrast + | ACR 2.4 + | True + | False + | True + | + | + | 0, 0 + | 32, 22 + | 64, 56 + | 128, 128 + | 192, 196 + | 255, 255 + | + | + | + | + | 0 + | Adobe Photoshop CS6 (Macintosh) + | 0 + | 2014-06-09T12:43:59-04:00 + | 2005-07-21T18:39:06-06:00 + | 2014-06-09T12:43:59-04:00 + | + | + | xmp.iid:0A801174072068118083C9374AAA53C2 + | uuid:021303F2FBA711D98B5DCD54C315AFD0 + | + | xmp.iid:0A801174072068118083C9374AAA53C2 + | uuid:021303F2FBA711D98B5DCD54C315AFD0 + | + | + | + | + | + | + | + | + | image/jpeg + | + | + | 2005-07-20T20:08:42 + | A8D68AA81537D1C7170A5C69A46C6C94 + | 3 + | Adobe RGB (1998) + | + | + | 100 + | + | + | + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x00004B87 + Length = 3160 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 1 of 1 + Profile Size : 3144 bytes + Preferred CMM Type : 'Lino' (0x4C696E6F) + Profile Version : 0.2.1.0 (0x02100000) + Profile Device/Class : Display Device profile ('mntr' (0x6D6E7472)) + Data Colour Space : rgbData ('RGB ' (0x52474220)) + Profile connection space (PCS) : 'XYZ ' (0x58595A20) + Profile creation date : 1998-02-09 06:49:00 + Profile file signature : 'acsp' (0x61637370) + Primary platform : Microsoft Corporation ('MSFT' (0x4D534654)) + Profile flags : 0x00000000 + Profile flags > Profile not embedded + Profile flags > Profile can't be used independently of embedded + Device Manufacturer : 'IEC ' (0x49454320) + Device Model : 'sRGB' (0x73524742) + Device attributes : 0x00000000_00000000 + Device attributes > Reflective + Device attributes > Glossy + Device attributes > Media polarity = negative + Device attributes > Black & white media + Rendering intent : Perceptual + Profile creator : 'HP ' (0x48502020) + Profile ID : 0x00000000_00000000_00000000_00000000 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x000057E1 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 3 2 2 3 5 8 10 12 + DQT, Row #1: 2 2 3 4 5 12 12 11 + DQT, Row #2: 3 3 3 5 8 11 14 11 + DQT, Row #3: 3 3 4 6 10 17 16 12 + DQT, Row #4: 4 4 7 11 14 22 21 15 + DQT, Row #5: 5 7 11 13 16 21 23 18 + DQT, Row #6: 10 13 16 17 21 24 24 20 + DQT, Row #7: 14 18 19 20 22 20 21 20 + Approx quality factor = 90.06 (scaling=19.88 variance=1.14) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00005826 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 3 4 5 9 20 20 20 20 + DQT, Row #1: 4 4 5 13 20 20 20 20 + DQT, Row #2: 5 5 11 20 20 20 20 20 + DQT, Row #3: 9 13 20 20 20 20 20 20 + DQT, Row #4: 20 20 20 20 20 20 20 20 + DQT, Row #5: 20 20 20 20 20 20 20 20 + DQT, Row #6: 20 20 20 20 20 20 20 20 + DQT, Row #7: 20 20 20 20 20 20 20 20 + Approx quality factor = 89.93 (scaling=20.14 variance=0.34) + +*** Marker: SOF2 (Progressive DCT, Huffman) (xFFC2) *** + OFFSET: 0x0000586B + Frame header length = 17 + Precision = 8 + Number of Lines = 3000 + Samples per Line = 3000 + Image Size = 3000 x 3000 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000587E + Huffman table length = 28 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 04 05 + Codes of length 03 bits (002 total): 03 06 + Codes of length 04 bits (003 total): 00 01 02 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000589C + Huffman table length = 27 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 01 02 03 + Codes of length 03 bits (001 total): 00 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (001 total): 07 + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 008 + + +*** Marker: DRI (Restart Interval) (xFFDD) *** + OFFSET: 0x000058B9 + Length = 4 + interval = 375 + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000058BF + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),0(AC) + Component[3]: selector=0x03, table=1(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0004A3DA + Huffman table length = 55 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 02 03 + Codes of length 03 bits (003 total): 01 04 05 + Codes of length 04 bits (000 total): + Codes of length 05 bits (001 total): 00 + Codes of length 06 bits (004 total): 06 11 12 13 + Codes of length 07 bits (001 total): 14 + Codes of length 08 bits (003 total): 15 21 22 + Codes of length 09 bits (003 total): 10 23 31 + Codes of length 10 bits (004 total): 07 20 24 41 + Codes of length 11 bits (002 total): 16 32 + Codes of length 12 bits (002 total): 30 33 + Codes of length 13 bits (002 total): 25 40 + Codes of length 14 bits (002 total): 17 42 + Codes of length 15 bits (000 total): + Codes of length 16 bits (007 total): 34 50 60 43 26 35 44 + Total number of codes: 036 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0004A413 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 5 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000B0746 + Huffman table length = 62 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (002 total): 02 11 + Codes of length 04 bits (001 total): 21 + Codes of length 05 bits (004 total): 03 10 12 31 + Codes of length 06 bits (001 total): 41 + Codes of length 07 bits (004 total): 13 20 22 51 + Codes of length 08 bits (002 total): 04 32 + Codes of length 09 bits (001 total): 61 + Codes of length 10 bits (003 total): 14 30 71 + Codes of length 11 bits (003 total): 23 40 42 + Codes of length 12 bits (003 total): 33 50 52 + Codes of length 13 bits (003 total): 05 81 91 + Codes of length 14 bits (003 total): 60 A1 B1 + Codes of length 15 bits (000 total): + Codes of length 16 bits (011 total): 15 24 62 43 53 C1 D1 72 F0 F1 E1 + Total number of codes: 043 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000B0786 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000E3DFE + Huffman table length = 69 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (002 total): 02 11 + Codes of length 04 bits (002 total): 03 21 + Codes of length 05 bits (002 total): 12 31 + Codes of length 06 bits (000 total): + Codes of length 07 bits (006 total): 04 10 13 22 41 51 + Codes of length 08 bits (001 total): 32 + Codes of length 09 bits (002 total): 20 61 + Codes of length 10 bits (005 total): 05 14 23 42 71 + Codes of length 11 bits (002 total): 30 52 + Codes of length 12 bits (004 total): 33 40 81 91 + Codes of length 13 bits (005 total): 15 50 60 A1 B1 + Codes of length 14 bits (002 total): 62 F0 + Codes of length 15 bits (000 total): + Codes of length 16 bits (015 total): 24 43 70 C1 D1 06 53 72 E1 F1 25 34 44 63 A2 + Total number of codes: 050 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000E3E45 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0012926F + Huffman table length = 79 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (003 total): 00 02 03 + Codes of length 04 bits (002 total): 11 12 + Codes of length 05 bits (005 total): 04 13 21 31 41 + Codes of length 06 bits (003 total): 22 32 51 + Codes of length 07 bits (003 total): 23 42 61 + Codes of length 08 bits (002 total): 05 14 + Codes of length 09 bits (003 total): 33 52 71 + Codes of length 10 bits (007 total): 10 24 43 62 81 91 A1 + Codes of length 11 bits (001 total): B1 + Codes of length 12 bits (006 total): 15 20 30 53 72 C1 + Codes of length 13 bits (004 total): 34 40 50 82 + Codes of length 14 bits (003 total): 60 63 92 + Codes of length 15 bits (002 total): A2 D1 + Codes of length 16 bits (015 total): 25 44 70 E1 F0 73 83 B2 F1 06 54 16 35 C2 74 + Total number of codes: 060 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x001292C0 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 6 .. 63 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x001CCECE + Huffman table length = 42 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (002 total): 21 31 + Codes of length 05 bits (003 total): 41 51 61 + Codes of length 06 bits (000 total): + Codes of length 07 bits (002 total): 71 81 + Codes of length 08 bits (002 total): 91 A1 + Codes of length 09 bits (002 total): 10 B1 + Codes of length 10 bits (002 total): C1 D1 + Codes of length 11 bits (003 total): 20 E1 F0 + Codes of length 12 bits (001 total): F1 + Codes of length 13 bits (001 total): 30 + Codes of length 14 bits (001 total): 40 + Codes of length 15 bits (001 total): 50 + Codes of length 16 bits (000 total): + Total number of codes: 023 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x001CCEFA + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x21 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0024E532 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=0(DC),0(AC) + Component[3]: selector=0x03, table=0(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0025B81A + Huffman table length = 42 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (001 total): 21 + Codes of length 05 bits (001 total): 31 + Codes of length 06 bits (001 total): 41 + Codes of length 07 bits (000 total): + Codes of length 08 bits (002 total): 10 51 + Codes of length 09 bits (002 total): 61 71 + Codes of length 10 bits (001 total): 81 + Codes of length 11 bits (003 total): 20 91 A1 + Codes of length 12 bits (005 total): 30 B1 C1 D1 F0 + Codes of length 13 bits (001 total): E1 + Codes of length 14 bits (001 total): 40 + Codes of length 15 bits (001 total): F1 + Codes of length 16 bits (001 total): 50 + Total number of codes: 023 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0025B846 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0029943F + Huffman table length = 43 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (001 total): 21 + Codes of length 05 bits (001 total): 31 + Codes of length 06 bits (000 total): + Codes of length 07 bits (002 total): 41 51 + Codes of length 08 bits (002 total): 10 61 + Codes of length 09 bits (001 total): 71 + Codes of length 10 bits (003 total): 81 91 A1 + Codes of length 11 bits (005 total): 20 B1 C1 D1 F0 + Codes of length 12 bits (001 total): E1 + Codes of length 13 bits (001 total): 30 + Codes of length 14 bits (001 total): F1 + Codes of length 15 bits (000 total): + Codes of length 16 bits (003 total): 40 50 60 + Total number of codes: 024 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0029946C + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x002E23F1 + Huffman table length = 41 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (002 total): 21 31 + Codes of length 05 bits (003 total): 41 51 61 + Codes of length 06 bits (001 total): 71 + Codes of length 07 bits (000 total): + Codes of length 08 bits (002 total): 81 91 + Codes of length 09 bits (002 total): A1 B1 + Codes of length 10 bits (002 total): C1 D1 + Codes of length 11 bits (003 total): 10 E1 F0 + Codes of length 12 bits (001 total): F1 + Codes of length 13 bits (001 total): 20 + Codes of length 14 bits (001 total): 30 + Codes of length 15 bits (001 total): 40 + Codes of length 16 bits (000 total): + Total number of codes: 022 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x002E241C + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x00397201 + + +*** Embedded JPEG Thumbnail *** + Offset: 0x00000258 + Length: 0x00003545 (13637) + + * Embedded Thumb Marker: SOI + + * Embedded Thumb Marker: APP0 + Length = 16 + + * Embedded Thumb Marker: DQT + Length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance, typically) + DQT, Row #0: 8 6 5 8 12 20 26 31 + DQT, Row #1: 6 6 7 10 13 29 30 28 + DQT, Row #2: 7 7 8 12 20 29 35 28 + DQT, Row #3: 7 9 11 15 26 44 40 31 + DQT, Row #4: 9 11 19 28 34 55 52 39 + DQT, Row #5: 12 18 28 32 41 52 57 46 + DQT, Row #6: 25 32 39 44 52 61 60 51 + DQT, Row #7: 36 46 48 49 56 50 52 50 + + * Embedded Thumb Marker: DQT + Length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance, typically) + DQT, Row #0: 9 9 12 24 50 50 50 50 + DQT, Row #1: 9 11 13 33 50 50 50 50 + DQT, Row #2: 12 13 28 50 50 50 50 50 + DQT, Row #3: 24 33 50 50 50 50 50 50 + DQT, Row #4: 50 50 50 50 50 50 50 50 + DQT, Row #5: 50 50 50 50 50 50 50 50 + DQT, Row #6: 50 50 50 50 50 50 50 50 + DQT, Row #7: 50 50 50 50 50 50 50 50 + + * Embedded Thumb Marker: SOF + Frame header length = 17 + Precision = 8 + Number of Lines = 196 + Samples per Line = 196 + Image Size = 196 x 196 + + * Embedded Thumb Marker: DHT + Length = 31 + + * Embedded Thumb Marker: DHT + Length = 181 + + * Embedded Thumb Marker: DHT + Length = 31 + + * Embedded Thumb Marker: DHT + Length = 181 + + * Embedded Thumb Marker: SOS + Skipping scan data + Skipped 13024 bytes + + * Embedded Thumb Marker: EOI + + * Embedded Thumb Signature: 0182408A81A4ABF04D4A34A8A5E98C58 + +*** Searching Compression Signatures *** + + Signature: 013BA18D5561625796E986FDBC09F846 + Signature (Rotated): 01AC57E12793DFA7C46C704625C5AF0F + File Offset: 0 bytes + Chroma subsampling: 1x1 + EXIF Make/Model: OK [OLYMPUS CORPORATION] [E-1] + EXIF Makernotes: NONE + EXIF Software: OK [GIMP 2.8.10] + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[??? ] [Treo 680 ] [ ] No + CAM:[Canon ] [Canon PowerShot Pro1 ] [fine ] No + CAM:[NIKON ] [E2500 ] [FINE ] No + CAM:[NIKON ] [E3100 ] [FINE ] No + CAM:[NIKON ] [E4500 ] [FINE ] No + CAM:[NIKON ] [E5000 ] [FINE ] No + CAM:[NIKON ] [E5700 ] [FINE ] No + CAM:[NIKON ] [E775 ] [FINE ] No + CAM:[NIKON ] [E885 ] [FINE ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C3040Z ] [ ] No + CAM:[PENTAX ] [PENTAX Optio 550 ] [ ] No + CAM:[Research In Motion ] [BlackBerry 9530 ] [Superfine ] No + CAM:[SEIKO EPSON CORP. ] [PhotoPC 3000Z ] [ ] No + CAM:[SONY ] [DSC-H7 ] [ ] No + CAM:[SONY ] [DSC-H9 ] [ ] No + CAM:[SONY ] [DSC-S90 ] [ ] No + CAM:[SONY ] [DSC-W1 ] [ ] No + CAM:[SONY ] [SONY ] [ ] No + SW :[ACDSee ] [ ] + SW :[FixFoto ] [fine ] + SW :[IJG Library ] [090 ] + SW :[ZoomBrowser EX ] [high ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [090 ] + SW :[IrfanView ] [090 ] + SW :[idImager ] [090 ] + SW :[FastStone Image Viewer ] [090 ] + SW :[NeatImage ] [090 ] + SW :[Paint.NET ] [090 ] + SW :[Photomatix ] [090 ] + SW :[XnView ] [090 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 2 - Image has high probability of being processed/edited + + diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue520-InvalidCast.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue520-InvalidCast.jpg.txt new file mode 100644 index 0000000000..aadf150e6d --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue520-InvalidCast.jpg.txt @@ -0,0 +1,364 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue520-InvalidCast.jpg] + Filesize: [7751] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 1 x 1 (aspect ratio) + thumbnail = 0 x 0 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000014 + Length = 499 + Identifier = [Exif] + Identifier TIFF = 0x[49492A00 08000000] + Endian = Intel (little) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000026 + Dir Length = 0x0011 + [DateTime ] = "2017:09:06 15:13:32" + [Model ] = "SAMSUNG-SM-J320AZ" + [Orientation ] = 1 = Row 0: top, Col 0: left + [WhiteBalance ] = Auto white balance + [DateTime ] = "2017:09:06 15:13:04" + [Make ] = "samsung" + [GPSOffset ] = @ 0x0124 + [ExifOffset ] = @ 0x01CD + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x000001EB + Dir Length = 0x0002 + + EXIF GPSIFD @ Absolute 0x00000142 + Dir Length = 0x0008 + [GPSTimeStamp ] = 115:8:12.00 + [GPSLatitudeRef ] = "N" + [GPSLongitude ] = 115 deg 8' 12.000" + [GPSLongitudeRef ] = "W" + [GPSDateStamp ] = "2017:08:08" + [GPSLatitude ] = 36 deg 11' 18.000" + [GPSAltitudeRef ] = Below Sea Level + [GPSAltitude ] = 0.000 m + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000209 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 1 1 1 1 1 1 1 1 + DQT, Row #1: 1 1 1 1 1 1 1 1 + DQT, Row #2: 1 1 1 1 1 1 1 1 + DQT, Row #3: 1 1 1 1 1 2 2 1 + DQT, Row #4: 1 1 1 1 1 2 2 2 + DQT, Row #5: 1 1 1 1 2 2 2 2 + DQT, Row #6: 1 1 2 2 2 2 2 2 + DQT, Row #7: 1 2 2 2 2 2 2 2 + Approx quality factor = 98.32 (scaling=3.35 variance=5.00) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x0000024E + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 1 1 1 1 2 2 2 2 + DQT, Row #1: 1 1 1 1 2 2 2 2 + DQT, Row #2: 1 1 1 2 2 2 2 2 + DQT, Row #3: 1 1 2 2 2 2 2 2 + DQT, Row #4: 2 2 2 2 2 2 2 2 + DQT, Row #5: 2 2 2 2 2 2 2 2 + DQT, Row #6: 2 2 2 2 2 2 2 2 + DQT, Row #7: 2 2 2 2 2 2 2 2 + Approx quality factor = 98.83 (scaling=2.34 variance=0.89) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x00000293 + Frame header length = 17 + Precision = 8 + Number of Lines = 100 + Samples per Line = 100 + Image Size = 100 x 100 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000002A6 + Huffman table length = 31 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 0A + Codes of length 03 bits (001 total): 09 + Codes of length 04 bits (004 total): 06 07 08 0B + Codes of length 05 bits (003 total): 03 04 05 + Codes of length 06 bits (001 total): 01 + Codes of length 07 bits (001 total): 02 + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000002C7 + Huffman table length = 60 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (004 total): 02 03 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (003 total): 00 07 11 + Codes of length 06 bits (003 total): 08 12 13 + Codes of length 07 bits (002 total): 14 21 + Codes of length 08 bits (003 total): 09 22 31 + Codes of length 09 bits (005 total): 0A 15 41 51 61 + Codes of length 10 bits (006 total): 16 23 24 32 33 71 + Codes of length 11 bits (003 total): 17 52 62 + Codes of length 12 bits (009 total): 18 42 54 73 91 93 A3 B1 B2 + Codes of length 13 bits (001 total): D2 + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 041 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000305 + Huffman table length = 28 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (005 total): 03 04 05 06 07 + Codes of length 04 bits (001 total): 08 + Codes of length 05 bits (001 total): 02 + Codes of length 06 bits (001 total): 01 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000323 + Huffman table length = 50 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 04 05 11 + Codes of length 05 bits (003 total): 00 12 21 + Codes of length 06 bits (003 total): 06 22 31 + Codes of length 07 bits (003 total): 13 41 51 + Codes of length 08 bits (002 total): 61 71 + Codes of length 09 bits (004 total): 07 14 32 91 + Codes of length 10 bits (005 total): 15 23 42 81 D1 + Codes of length 11 bits (005 total): 16 62 92 A1 B1 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 031 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000357 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x00000365 + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x00001E45.0 + + Compression stats: + Compression Ratio: 4.36:1 + Bits per pixel: 5.50:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 97 ( 49%) + # codes of length 03 bits: 25 ( 13%) + # codes of length 04 bits: 51 ( 26%) + # codes of length 05 bits: 18 ( 9%) + # codes of length 06 bits: 3 ( 2%) + # codes of length 07 bits: 2 ( 1%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 26 ( 27%) + # codes of length 03 bits: 62 ( 63%) + # codes of length 04 bits: 5 ( 5%) + # codes of length 05 bits: 3 ( 3%) + # codes of length 06 bits: 2 ( 2%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 971 ( 16%) + # codes of length 03 bits: 3435 ( 57%) + # codes of length 04 bits: 456 ( 8%) + # codes of length 05 bits: 579 ( 10%) + # codes of length 06 bits: 285 ( 5%) + # codes of length 07 bits: 96 ( 2%) + # codes of length 08 bits: 74 ( 1%) + # codes of length 09 bits: 59 ( 1%) + # codes of length 10 bits: 30 ( 0%) + # codes of length 11 bits: 7 ( 0%) + # codes of length 12 bits: 9 ( 0%) + # codes of length 13 bits: 1 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 1143 ( 50%) + # codes of length 03 bits: 254 ( 11%) + # codes of length 04 bits: 445 ( 19%) + # codes of length 05 bits: 228 ( 10%) + # codes of length 06 bits: 111 ( 5%) + # codes of length 07 bits: 48 ( 2%) + # codes of length 08 bits: 20 ( 1%) + # codes of length 09 bits: 22 ( 1%) + # codes of length 10 bits: 13 ( 1%) + # codes of length 11 bits: 5 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[193] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 1001, -7, 97] RGB=[255,244,251] @ MCU[ 5, 3] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x00001E44.1 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x00001E45 + + +*** Searching Compression Signatures *** + + Signature: 01C7F83908166C226C06A44017421732 + Signature (Rotated): 01D3EFDD3855C42AE3E0E6289F1A6726 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: OK [samsung] [SAMSUNG-SM-J320AZ] + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[Canon ] [Canon EOS-1Ds Mark II ] [fine ] No + CAM:[CASIO COMPUTER CO.,LTD. ] [EX-Z1000 ] [ ] Yes + CAM:[NIKON ] [NIKON D2X ] [FINE ] No + CAM:[NIKON ] [NIKON D3 ] [FINE ] No + CAM:[SIGMA ] [SIGMA SD10 ] [Qual:12 ] No + CAM:[SIGMA ] [SIGMA SD10 ] [Qual:12 ] No + CAM:[SIGMA ] [SIGMA SD14 ] [Qual:12 ] No + CAM:[SIGMA ] [SIGMA SD14 ] [Qual:12 ] No + CAM:[SONY ] [DSC-H2 ] [ ] No + CAM:[SONY ] [DSC-R1 ] [ ] No + CAM:[SONY ] [DSC-W7 ] [ ] No + SW :[Digital Photo Professiona] [09 ] + SW :[IJG Library ] [099 ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [099 ] + SW :[IrfanView ] [099 ] + SW :[idImager ] [099 ] + SW :[FastStone Image Viewer ] [099 ] + SW :[NeatImage ] [099 ] + SW :[Paint.NET ] [099 ] + SW :[Photomatix ] [099 ] + SW :[XnView ] [099 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 2 - Image has high probability of being processed/edited + + diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue624-DhtHasWrongLength-Progressive-N.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue624-DhtHasWrongLength-Progressive-N.jpg.txt new file mode 100644 index 0000000000..0ee1736d3c --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue624-DhtHasWrongLength-Progressive-N.jpg.txt @@ -0,0 +1,284 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue624-DhtHasWrongLength-Progressive-N.jpg] + Filesize: [30441] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 1 x 1 (aspect ratio) + thumbnail = 0 x 0 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000014 + Table length = 132 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 6 6 6 7 10 15 22 34 + DQT, Row #1: 6 7 8 11 14 16 21 30 + DQT, Row #2: 6 8 10 12 17 25 36 54 + DQT, Row #3: 7 11 12 16 21 30 42 62 + DQT, Row #4: 10 14 17 21 28 38 52 76 + DQT, Row #5: 15 16 25 30 38 50 68 95 + DQT, Row #6: 22 21 36 42 52 68 90 124 + DQT, Row #7: 34 30 54 62 76 95 124 167 + Approx quality factor = 71.19 (scaling=57.62 variance=593.35) + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 6 6 6 7 10 15 22 34 + DQT, Row #1: 6 7 8 11 14 16 21 30 + DQT, Row #2: 6 8 10 12 17 25 36 54 + DQT, Row #3: 7 11 12 16 21 30 42 62 + DQT, Row #4: 10 14 17 21 28 38 52 76 + DQT, Row #5: 15 16 25 30 38 50 68 95 + DQT, Row #6: 22 21 36 42 52 68 90 124 + DQT, Row #7: 34 30 54 62 76 95 124 167 + Approx quality factor = 80.24 (scaling=39.51 variance=961.47) + +*** Marker: SOF2 (Progressive DCT, Huffman) (xFFC2) *** + OFFSET: 0x0000009A + Frame header length = 17 + Precision = 8 + Number of Lines = 1080 + Samples per Line = 1080 + Image Size = 1080 x 1080 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000AD + Huffman table length = 51 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 05 06 + Codes of length 04 bits (003 total): 01 03 04 + Codes of length 05 bits (001 total): 02 + Codes of length 06 bits (001 total): 07 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 008 + + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (000 total): + Codes of length 03 bits (003 total): 02 04 06 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (001 total): 01 + Codes of length 06 bits (001 total): 05 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 007 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000000E2 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),0(AC) + Component[3]: selector=0x03, table=1(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000151B + Huffman table length = 2 + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000151F + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=0(DC),0(AC) + Component[3]: selector=0x03, table=0(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x21 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000022BA + Huffman table length = 2 + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000022BE + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=0(DC),0(AC) + Component[3]: selector=0x03, table=0(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000309F + Huffman table length = 53 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (002 total): 02 03 + Codes of length 04 bits (003 total): 00 04 20 + Codes of length 05 bits (007 total): 11 31 33 34 50 71 72 + Codes of length 06 bits (001 total): 05 + Codes of length 07 bits (005 total): 12 21 52 60 61 + Codes of length 08 bits (007 total): 10 13 22 32 41 51 A1 + Codes of length 09 bits (003 total): 23 53 62 + Codes of length 10 bits (005 total): 70 80 81 A0 F1 + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 034 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000030D6 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00003353 + Huffman table length = 54 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (002 total): 02 04 + Codes of length 04 bits (002 total): 03 05 + Codes of length 05 bits (008 total): 00 06 20 32 50 63 71 B1 + Codes of length 06 bits (005 total): 11 25 31 35 73 + Codes of length 07 bits (002 total): 12 21 + Codes of length 08 bits (004 total): 10 22 51 60 + Codes of length 09 bits (005 total): 13 23 41 42 52 + Codes of length 10 bits (005 total): 14 33 70 80 A1 + Codes of length 11 bits (001 total): A0 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 035 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000338B + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000378D + Huffman table length = 83 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (003 total): 02 03 04 + Codes of length 04 bits (001 total): 05 + Codes of length 05 bits (003 total): 06 07 11 + Codes of length 06 bits (005 total): 00 21 30 31 B2 + Codes of length 07 bits (009 total): 12 13 32 40 41 50 51 72 74 + Codes of length 08 bits (011 total): 14 16 22 33 35 42 52 61 71 B1 B3 + Codes of length 09 bits (009 total): 15 20 23 24 60 62 81 91 C2 + Codes of length 10 bits (006 total): 10 25 36 43 75 92 + Codes of length 11 bits (004 total): 82 93 A1 A2 + Codes of length 12 bits (006 total): 34 63 64 73 C3 D2 + Codes of length 13 bits (001 total): A3 + Codes of length 14 bits (005 total): 26 45 53 65 83 + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 064 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000037E2 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x000076E7 + + +*** Searching Compression Signatures *** + + Signature: 014D6128740A2927C9914C433E852F5A + Signature (Rotated): 014D6128740A2927C9914C433E852F5A + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue694-Decode-Exif-OutOfRange.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue694-Decode-Exif-OutOfRange.jpg.txt new file mode 100644 index 0000000000..9feef52cce --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue694-Decode-Exif-OutOfRange.jpg.txt @@ -0,0 +1,368 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue694-Decode-Exif-OutOfRange.jpg] + Filesize: [226421] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000002 + Length = 194 + Identifier = [Exif] + Identifier TIFF = 0x[49492A00 08000000] + Endian = Intel (little) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000014 + Dir Length = 0x0007 + [Orientation ] = 1 = Row 0: top, Col 0: left + [XResolution ] = 96/1 + [YResolution ] = 96/1 + [ResolutionUnit ] = Inch + [Software ] = "PhotoFiltre 7" + [DateTime ] = "2017:08:30 22:45:26" + [ExifOffset ] = @ 0x0094 + Offset to Next IFD = 0xFC5019BC + + EXIF IFD1 @ Absolute 0xFC5019C8 + Dir Length = 0x0000 + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x000000A0 + Dir Length = 0x0003 + [ExifVersion ] = 02.10 + [ExifImageWidth ] = 1400 + [ExifImageHeight ] = 1400 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x000000C6 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 1 1 1 1 1 1 1 1 + DQT, Row #1: 1 1 1 1 1 1 1 1 + DQT, Row #2: 1 1 1 1 1 1 1 1 + DQT, Row #3: 1 1 1 1 1 1 1 1 + DQT, Row #4: 1 1 1 1 1 1 1 1 + DQT, Row #5: 1 1 1 1 1 1 1 1 + DQT, Row #6: 1 1 1 1 1 1 1 1 + DQT, Row #7: 1 1 1 1 1 1 1 1 + Approx quality factor = 100.00 (scaling=2.99 variance=6.13) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x0000010B + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 1 1 1 1 1 1 1 1 + DQT, Row #1: 1 1 1 1 1 1 1 1 + DQT, Row #2: 1 1 1 1 1 1 1 1 + DQT, Row #3: 1 1 1 1 1 1 1 1 + DQT, Row #4: 1 1 1 1 1 1 1 1 + DQT, Row #5: 1 1 1 1 1 1 1 1 + DQT, Row #6: 1 1 1 1 1 1 1 1 + DQT, Row #7: 1 1 1 1 1 1 1 1 + Approx quality factor = 100.00 (scaling=1.54 variance=1.58) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x00000150 + Frame header length = 17 + Precision = 8 + Number of Lines = 1400 + Samples per Line = 1400 + Image Size = 1400 x 1400 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000163 + Huffman table length = 31 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (005 total): 01 02 03 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (001 total): 0A + Codes of length 09 bits (001 total): 0B + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000184 + Huffman table length = 181 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 05 12 21 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 06 13 51 61 + Codes of length 08 bits (003 total): 07 22 71 + Codes of length 09 bits (005 total): 14 32 81 91 A1 + Codes of length 10 bits (005 total): 08 23 42 B1 C1 + Codes of length 11 bits (004 total): 15 52 D1 F0 + Codes of length 12 bits (004 total): 24 33 62 72 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (001 total): 82 + Codes of length 16 bits (125 total): 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 + 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 + 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 + 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 + 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 + B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA + D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 + E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000023B + Huffman table length = 31 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (001 total): 07 + Codes of length 08 bits (001 total): 08 + Codes of length 09 bits (001 total): 09 + Codes of length 10 bits (001 total): 0A + Codes of length 11 bits (001 total): 0B + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000025C + Huffman table length = 181 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (002 total): 03 11 + Codes of length 05 bits (004 total): 04 05 21 31 + Codes of length 06 bits (004 total): 06 12 41 51 + Codes of length 07 bits (003 total): 07 61 71 + Codes of length 08 bits (004 total): 13 22 32 81 + Codes of length 09 bits (007 total): 08 14 42 91 A1 B1 C1 + Codes of length 10 bits (005 total): 09 23 33 52 F0 + Codes of length 11 bits (004 total): 15 62 72 D1 + Codes of length 12 bits (004 total): 0A 16 24 34 + Codes of length 13 bits (000 total): + Codes of length 14 bits (001 total): E1 + Codes of length 15 bits (002 total): 25 F1 + Codes of length 16 bits (119 total): 17 18 19 1A 26 27 28 29 2A 35 36 37 38 39 3A 43 + 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 + 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 82 + 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 + 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 + B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 + D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 + F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000313 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x00000321 + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x00037473.0 + + Compression stats: + Compression Ratio: 26.06:1 + Bits per pixel: 0.92:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 25384 ( 82%) + # codes of length 03 bits: 1101 ( 4%) + # codes of length 04 bits: 566 ( 2%) + # codes of length 05 bits: 758 ( 2%) + # codes of length 06 bits: 429 ( 1%) + # codes of length 07 bits: 616 ( 2%) + # codes of length 08 bits: 933 ( 3%) + # codes of length 09 bits: 1189 ( 4%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 13762 ( 89%) + # codes of length 03 bits: 146 ( 1%) + # codes of length 04 bits: 264 ( 2%) + # codes of length 05 bits: 354 ( 2%) + # codes of length 06 bits: 509 ( 3%) + # codes of length 07 bits: 335 ( 2%) + # codes of length 08 bits: 116 ( 1%) + # codes of length 09 bits: 2 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 48125 ( 26%) + # codes of length 03 bits: 20074 ( 11%) + # codes of length 04 bits: 54692 ( 30%) + # codes of length 05 bits: 21145 ( 12%) + # codes of length 06 bits: 3017 ( 2%) + # codes of length 07 bits: 14358 ( 8%) + # codes of length 08 bits: 8803 ( 5%) + # codes of length 09 bits: 2231 ( 1%) + # codes of length 10 bits: 5065 ( 3%) + # codes of length 11 bits: 1096 ( 1%) + # codes of length 12 bits: 224 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 6 ( 0%) + # codes of length 16 bits: 4924 ( 3%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 25772 ( 49%) + # codes of length 03 bits: 5924 ( 11%) + # codes of length 04 bits: 7056 ( 13%) + # codes of length 05 bits: 6378 ( 12%) + # codes of length 06 bits: 2891 ( 5%) + # codes of length 07 bits: 1200 ( 2%) + # codes of length 08 bits: 1082 ( 2%) + # codes of length 09 bits: 1030 ( 2%) + # codes of length 10 bits: 559 ( 1%) + # codes of length 11 bits: 299 ( 1%) + # codes of length 12 bits: 38 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 73 ( 0%) + # codes of length 15 bits: 67 ( 0%) + # codes of length 16 bits: 260 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[ 57] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 1016, 0, 0] RGB=[255,255,255] @ MCU[ 40, 2] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x00037472.4 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x00037473 + + +*** Searching Compression Signatures *** + + Signature: 01BBB1709AC9C1F89220D955A31A8F34 + Signature (Rotated): 01BBB1709AC9C1F89220D955A31A8F34 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: OK [PhotoFiltre 7] + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[CASIO COMPUTER CO.,LTD ] [EX-Z750 ] [ ] Yes + CAM:[CASIO COMPUTER CO.,LTD. ] [EX-Z1000 ] [ ] Yes + CAM:[PENTAX ] [PENTAX Optio S5i ] [ ] Yes + CAM:[SIGMA ] [SIGMA SD9 ] [ ] Yes + SW :[ACDSee ] [100 ] + SW :[Apple ImageIO.framework ] [100 (Best) ] + SW :[Digital Photo Professiona] [10 ] + SW :[IJG Library ] [100 ] + SW :[MS Office Pic Mgr ] [ ] + SW :[Nikon Scan ] [Excellent Qualit] + SW :[Picasa ] [100 (Maximum) ] + SW :[ZoomBrowser EX ] [highest ] + SW :[EOS Viewer Utility ] [ ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [100 ] + SW :[IrfanView ] [100 ] + SW :[idImager ] [100 ] + SW :[FastStone Image Viewer ] [100 ] + SW :[NeatImage ] [100 ] + SW :[Paint.NET ] [100 ] + SW :[Photomatix ] [100 ] + SW :[XnView ] [100 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue695-Invalid-EOI.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue695-Invalid-EOI.jpg.txt new file mode 100644 index 0000000000..8911896afa --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue695-Invalid-EOI.jpg.txt @@ -0,0 +1,39 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue695-Invalid-EOI.jpg] + Filesize: [4805575] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 72 x 72 (aspect ratio) + thumbnail = 0 x 0 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000014 + Length = 64 + Identifier = [Exif] + Identifier TIFF = 0x[49492A00 08000000] + Endian = Intel (little) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000026 + Dir Length = 0x0001 + [ExifOffset ] = @ 0x001A + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x00000038 + Dir Length = 0x0002 + [ExifImageWidth ] = 0x[000003E8] / 1000 + [ExifImageHeight ] = 0x[000003E8] / 1000 + +ERROR: Expected marker 0xFF, got 0x49 @ offset 0x00000056. Consider using [Tools->Img Search Fwd/Rev]. diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue696-Resize-Exif-OutOfRange.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue696-Resize-Exif-OutOfRange.jpg.txt new file mode 100644 index 0000000000..566291b47e --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue696-Resize-Exif-OutOfRange.jpg.txt @@ -0,0 +1,377 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue696-Resize-Exif-OutOfRange.jpg] + Filesize: [3196058] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 1 x 1 (aspect ratio) + thumbnail = 0 x 0 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000014 + Length = 201 + Identifier = [Exif] + Identifier TIFF = 0x[49492A00 08000000] + Endian = Intel (little) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000026 + Dir Length = 0x0007 + [Orientation ] = 1 = Row 0: top, Col 0: left + [XResolution ] = 96/1 + [YResolution ] = 96/1 + [ResolutionUnit ] = Inch + [Software ] = "PhotoFiltre Studio X" + [DateTime ] = "2017:09:12 23:47:30" + [ExifOffset ] = @ 0x009B + Offset to Next IFD = 0xFFFFFFFF + + EXIF IFD1 @ Absolute 0x0000001D + Dir Length = 0x4900 + Excessive # components (117440512). Limiting to first 4000. + Offset to Next IFD = 0x03011200 + + EXIF SubIFD @ Absolute 0x000000B9 + Dir Length = 0x0003 + [ExifVersion ] = 02.10 + [ExifImageWidth ] = 3000 + [ExifImageHeight ] = 3000 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x000000DF + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 1 1 1 1 1 1 1 1 + DQT, Row #1: 1 1 1 1 1 1 1 1 + DQT, Row #2: 1 1 1 1 1 1 1 1 + DQT, Row #3: 1 1 1 1 1 1 1 1 + DQT, Row #4: 1 1 1 1 1 1 1 1 + DQT, Row #5: 1 1 1 1 1 1 1 1 + DQT, Row #6: 1 1 1 1 1 1 1 1 + DQT, Row #7: 1 1 1 1 1 1 1 1 + Approx quality factor = 100.00 (scaling=2.99 variance=6.13) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000124 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 1 1 1 1 1 1 1 1 + DQT, Row #1: 1 1 1 1 1 1 1 1 + DQT, Row #2: 1 1 1 1 1 1 1 1 + DQT, Row #3: 1 1 1 1 1 1 1 1 + DQT, Row #4: 1 1 1 1 1 1 1 1 + DQT, Row #5: 1 1 1 1 1 1 1 1 + DQT, Row #6: 1 1 1 1 1 1 1 1 + DQT, Row #7: 1 1 1 1 1 1 1 1 + Approx quality factor = 100.00 (scaling=1.54 variance=1.58) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x00000169 + Frame header length = 17 + Precision = 8 + Number of Lines = 3000 + Samples per Line = 3000 + Image Size = 3000 x 3000 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000017C + Huffman table length = 31 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (005 total): 01 02 03 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (001 total): 0A + Codes of length 09 bits (001 total): 0B + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000019D + Huffman table length = 181 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 05 12 21 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 06 13 51 61 + Codes of length 08 bits (003 total): 07 22 71 + Codes of length 09 bits (005 total): 14 32 81 91 A1 + Codes of length 10 bits (005 total): 08 23 42 B1 C1 + Codes of length 11 bits (004 total): 15 52 D1 F0 + Codes of length 12 bits (004 total): 24 33 62 72 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (001 total): 82 + Codes of length 16 bits (125 total): 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 + 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 + 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 + 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 + 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 + B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA + D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 + E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000254 + Huffman table length = 31 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (001 total): 07 + Codes of length 08 bits (001 total): 08 + Codes of length 09 bits (001 total): 09 + Codes of length 10 bits (001 total): 0A + Codes of length 11 bits (001 total): 0B + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000275 + Huffman table length = 181 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (002 total): 03 11 + Codes of length 05 bits (004 total): 04 05 21 31 + Codes of length 06 bits (004 total): 06 12 41 51 + Codes of length 07 bits (003 total): 07 61 71 + Codes of length 08 bits (004 total): 13 22 32 81 + Codes of length 09 bits (007 total): 08 14 42 91 A1 B1 C1 + Codes of length 10 bits (005 total): 09 23 33 52 F0 + Codes of length 11 bits (004 total): 15 62 72 D1 + Codes of length 12 bits (004 total): 0A 16 24 34 + Codes of length 13 bits (000 total): + Codes of length 14 bits (001 total): E1 + Codes of length 15 bits (002 total): 25 F1 + Codes of length 16 bits (119 total): 17 18 19 1A 26 27 28 29 2A 35 36 37 38 39 3A 43 + 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 + 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 82 + 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 + 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 + B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 + D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 + F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000032C + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x0000033A + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x0030C498.0 + + Compression stats: + Compression Ratio: 8.45:1 + Bits per pixel: 2.84:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 35306 ( 25%) + # codes of length 03 bits: 79378 ( 56%) + # codes of length 04 bits: 10642 ( 8%) + # codes of length 05 bits: 5371 ( 4%) + # codes of length 06 bits: 3913 ( 3%) + # codes of length 07 bits: 2829 ( 2%) + # codes of length 08 bits: 2486 ( 2%) + # codes of length 09 bits: 1451 ( 1%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 45165 ( 64%) + # codes of length 03 bits: 10069 ( 14%) + # codes of length 04 bits: 6960 ( 10%) + # codes of length 05 bits: 3541 ( 5%) + # codes of length 06 bits: 2100 ( 3%) + # codes of length 07 bits: 1345 ( 2%) + # codes of length 08 bits: 1100 ( 2%) + # codes of length 09 bits: 324 ( 0%) + # codes of length 10 bits: 84 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 1981662 ( 53%) + # codes of length 03 bits: 213036 ( 6%) + # codes of length 04 bits: 749857 ( 20%) + # codes of length 05 bits: 410362 ( 11%) + # codes of length 06 bits: 173055 ( 5%) + # codes of length 07 bits: 94282 ( 3%) + # codes of length 08 bits: 61648 ( 2%) + # codes of length 09 bits: 36705 ( 1%) + # codes of length 10 bits: 19723 ( 1%) + # codes of length 11 bits: 10118 ( 0%) + # codes of length 12 bits: 2157 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 211 ( 0%) + # codes of length 16 bits: 9772 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 425513 ( 38%) + # codes of length 03 bits: 127308 ( 11%) + # codes of length 04 bits: 204956 ( 18%) + # codes of length 05 bits: 171523 ( 15%) + # codes of length 06 bits: 89715 ( 8%) + # codes of length 07 bits: 30159 ( 3%) + # codes of length 08 bits: 25054 ( 2%) + # codes of length 09 bits: 22104 ( 2%) + # codes of length 10 bits: 10243 ( 1%) + # codes of length 11 bits: 4250 ( 0%) + # codes of length 12 bits: 210 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 1829 ( 0%) + # codes of length 15 bits: 1498 ( 0%) + # codes of length 16 bits: 2262 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[127] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 992, -112, 17] RGB=[254,255,227] @ MCU[ 35, 79] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x0030C497.3 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0030C498 + + +*** Searching Compression Signatures *** + + Signature: 01BBB1709AC9C1F89220D955A31A8F34 + Signature (Rotated): 01BBB1709AC9C1F89220D955A31A8F34 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: OK [PhotoFiltre Studio X] + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[CASIO COMPUTER CO.,LTD ] [EX-Z750 ] [ ] Yes + CAM:[CASIO COMPUTER CO.,LTD. ] [EX-Z1000 ] [ ] Yes + CAM:[PENTAX ] [PENTAX Optio S5i ] [ ] Yes + CAM:[SIGMA ] [SIGMA SD9 ] [ ] Yes + SW :[ACDSee ] [100 ] + SW :[Apple ImageIO.framework ] [100 (Best) ] + SW :[Digital Photo Professiona] [10 ] + SW :[IJG Library ] [100 ] + SW :[MS Office Pic Mgr ] [ ] + SW :[Nikon Scan ] [Excellent Qualit] + SW :[Picasa ] [100 (Maximum) ] + SW :[ZoomBrowser EX ] [highest ] + SW :[EOS Viewer Utility ] [ ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [100 ] + SW :[IrfanView ] [100 ] + SW :[idImager ] [100 ] + SW :[FastStone Image Viewer ] [100 ] + SW :[NeatImage ] [100 ] + SW :[Paint.NET ] [100 ] + SW :[Photomatix ] [100 ] + SW :[XnView ] [100 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue721-InvalidAPP0.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue721-InvalidAPP0.jpg.txt new file mode 100644 index 0000000000..dc283bfcfc --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue721-InvalidAPP0.jpg.txt @@ -0,0 +1,446 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue721-InvalidAPP0.jpg] + Filesize: [1225163] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000002 + Length = 806 + Identifier = [Exif] + Identifier TIFF = 0x[4D4D002A 00000008] + Endian = Motorola (big) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000014 + Dir Length = 0x0007 + [Make ] = "NIKON CORPORATION" + [Model ] = "NIKON D300S" + [Software ] = "Adobe Bridge CS6 (Windows)" + [DateTime ] = "2017:06:07 16:49:51" + [Artist ] = ""Evgeniy Ivahiv Mr.Ivas"" + [Copyright ] = "Evgeniy Ivahiv Erich Krause" + [ExifOffset ] = @ 0x00EC + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x000000F8 + Dir Length = 0x0022 + [ExposureTime ] = 1/160 s + [FNumber ] = F10.0 + [ExposureProgram ] = Manual + [ISOSpeedRatings ] = 200 + [ExifVersion ] = 02.21 + [DateTimeOriginal ] = "2017:06:06 11:29:53" + [ShutterSpeedValue ] = 7321928/1000000 + [ApertureValue ] = 6643856/1000000 + [ExposureBiasValue ] = -3.00 eV + [MaxApertureValue ] = 50/10 + [MeteringMode ] = CenterWeightedAverage + [LightSource ] = unknown + [Flash ] = Flash did not fire + [FocalLength ] = 48 mm + [SubSecTimeOriginal ] = "24" + [ColorSpace ] = Uncalibrated + [ExifImageWidth ] = 2304 + [ExifImageHeight ] = 2998 + [SensingMethod ] = One-chip color area sensor + [FileSource ] = DSC + [SceneType ] = A directly photographed image + [ExposureMode ] = Manual exposure + [WhiteBalance ] = Manual white balance + [DigitalZoomRatio ] = 1/1 + [FocalLengthIn35mmFilm ] = 72 + [SceneCaptureType ] = Standard + [GainControl ] = 0 + [Contrast ] = 0 + [Saturation ] = 0 + [Sharpness ] = 2 + [SubjectDistanceRange ] = 0 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x0000032A + Length = 4442 + Identifier = [http://ns.adobe.com/xap/1.0/] + XMP = + +*** Marker: APP13 (xFFED) *** + OFFSET: 0x00001486 + Length = 160 + Identifier = [Photoshop 3.0] + 8BIM: [0x0404] Name="" Len=[0x0068] DefinedName="IPTC-NAA record" + IPTC [001:090] Coded Character Set = "%G" + IPTC [002:000] Record Version = 4 + IPTC [002:055] Date Created = "20170606" + IPTC [002:060] Time Created = "112953" + IPTC [002:080] By-line = "Evgeniy Ivahiv Mr.Ivas" + IPTC [002:116] Copyright Notice = "Evgeniy Ivahiv Erich Krause" + 8BIM: [0x0425] Name="" Len=[0x0010] DefinedName="Caption digest" + Caption digest = | 0x59 13 63 D2 BD 08 14 B4 2B E3 4F 37 D7 52 D2 6F | Y.c.....+.O7.R.o + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x00001528 + Length = 3160 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 1 of 1 + Profile Size : 3144 bytes + Preferred CMM Type : 'Lino' (0x4C696E6F) + Profile Version : 0.2.1.0 (0x02100000) + Profile Device/Class : Display Device profile ('mntr' (0x6D6E7472)) + Data Colour Space : rgbData ('RGB ' (0x52474220)) + Profile connection space (PCS) : 'XYZ ' (0x58595A20) + Profile creation date : 1998-02-09 06:49:00 + Profile file signature : 'acsp' (0x61637370) + Primary platform : Microsoft Corporation ('MSFT' (0x4D534654)) + Profile flags : 0x00000000 + Profile flags > Profile not embedded + Profile flags > Profile can't be used independently of embedded + Device Manufacturer : 'IEC ' (0x49454320) + Device Model : 'sRGB' (0x73524742) + Device attributes : 0x00000000_00000000 + Device attributes > Reflective + Device attributes > Glossy + Device attributes > Media polarity = negative + Device attributes > Black & white media + Rendering intent : Perceptual + Profile creator : 'HP ' (0x48502020) + Profile ID : 0x00000000_00000000_00000000_00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00002182 + Length = 12 + Identifier = [Adobe_CM] + Not known APP0 type. Skipping remainder. + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00002190 + Table length = 132 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 6 4 7 11 14 17 22 17 + DQT, Row #1: 4 5 6 10 14 19 12 12 + DQT, Row #2: 7 6 8 14 19 12 12 12 + DQT, Row #3: 11 10 14 19 12 12 12 12 + DQT, Row #4: 14 14 19 12 12 12 12 12 + DQT, Row #5: 17 19 12 12 12 12 12 12 + DQT, Row #6: 22 12 12 12 12 12 12 12 + DQT, Row #7: 17 12 12 12 12 12 12 12 + Approx quality factor = 83.88 (scaling=32.24 variance=430.71) + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 7 9 19 34 20 20 17 17 + DQT, Row #1: 9 12 19 14 14 12 12 12 + DQT, Row #2: 19 19 14 14 12 12 12 12 + DQT, Row #3: 34 14 14 12 12 12 12 12 + DQT, Row #4: 20 14 12 12 12 12 12 12 + DQT, Row #5: 20 12 12 12 12 12 12 12 + DQT, Row #6: 17 12 12 12 12 12 12 12 + DQT, Row #7: 17 12 12 12 12 12 12 12 + Approx quality factor = 89.11 (scaling=21.78 variance=377.49) + +*** Marker: DRI (Restart Interval) (xFFDD) *** + OFFSET: 0x00002216 + Length = 4 + interval = 288 + +*** Marker: APP14 (xFFEE) *** + OFFSET: 0x0000221C + Length = 14 + DCTEncodeVersion = 100 + APP14Flags0 = 49152 + APP14Flags1 = 0 + ColorTransform = 1 [YCbCr] + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x0000222C + Frame header length = 17 + Precision = 8 + Number of Lines = 2998 + Samples per Line = 2304 + Image Size = 2304 x 2998 + Raw Image Orientation = Portrait + Number of Img components = 3 + Component[1]: ID=0x00, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x02, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000223F + Huffman table length = 418 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (004 total): 03 04 05 06 + Codes of length 04 bits (003 total): 02 07 08 + Codes of length 05 bits (001 total): 01 + Codes of length 06 bits (001 total): 09 + Codes of length 07 bits (001 total): 0A + Codes of length 08 bits (001 total): 0B + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (001 total): 04 + Codes of length 06 bits (001 total): 05 + Codes of length 07 bits (001 total): 06 + Codes of length 08 bits (000 total): + Codes of length 09 bits (002 total): 07 08 + Codes of length 10 bits (003 total): 09 0A 0B + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (003 total): 02 03 11 + Codes of length 04 bits (002 total): 00 21 + Codes of length 05 bits (004 total): 04 12 31 41 + Codes of length 06 bits (002 total): 51 61 + Codes of length 07 bits (008 total): 05 13 22 71 81 91 A1 F0 + Codes of length 08 bits (004 total): 06 14 B1 C1 + Codes of length 09 bits (005 total): 23 32 D1 E1 F1 + Codes of length 10 bits (002 total): 07 42 + Codes of length 11 bits (003 total): 15 24 52 + Codes of length 12 bits (002 total): 33 62 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (001 total): 16 + Codes of length 16 bits (125 total): 34 43 72 82 92 A2 08 17 53 B2 C2 25 D2 E2 44 83 + 84 F2 09 0A 18 19 1A 26 27 28 29 2A 35 36 37 38 + 39 3A 45 46 47 48 49 4A 54 55 56 57 58 59 5A 63 + 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 85 + 86 87 88 89 8A 93 94 95 96 97 98 B3 C3 D3 99 9A + A3 A4 A5 A6 A7 A8 A9 AA B4 B5 B6 B7 B8 B9 BA C4 + C5 C6 C7 C8 C9 CA D4 D5 D6 D7 D8 D9 DA E3 E4 E5 + E6 E7 E8 E9 EA F3 F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (000 total): + Codes of length 04 bits (002 total): 02 11 + Codes of length 05 bits (001 total): 03 + Codes of length 06 bits (003 total): 12 21 31 + Codes of length 07 bits (001 total): 41 + Codes of length 08 bits (005 total): 04 13 51 61 71 + Codes of length 09 bits (002 total): 81 F0 + Codes of length 10 bits (006 total): 22 91 A1 B1 C1 D1 + Codes of length 11 bits (003 total): 14 32 E1 + Codes of length 12 bits (001 total): F1 + Codes of length 13 bits (000 total): + Codes of length 14 bits (127 total): 05 06 07 08 09 0A 15 16 17 18 19 1A 23 24 25 26 + 27 28 29 2A 33 34 35 36 37 38 39 3A 42 43 44 45 + 46 47 48 49 4A 52 53 54 55 56 57 58 59 5A 62 63 + 64 65 66 67 68 69 6A 72 73 74 75 76 77 78 79 7A + 82 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 + 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 + B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 + D5 D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA + Codes of length 15 bits (009 total): F2 F3 F4 F5 F6 F7 F8 F9 FA + Codes of length 16 bits (000 total): + Total number of codes: 162 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000023E3 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x00, table=0(DC),0(AC) + Component[2]: selector=0x01, table=1(DC),1(AC) + Component[3]: selector=0x02, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x000023F1 + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + +*** ERROR: Overread scan segment (after bitstring)! @ Offset: 0x00003EA5.1 +*** ERROR: Bad huffman code @ 0x00003EA5.6 +*** ERROR: Bad scan data in MCU(272,16): Lum CSS(0,0) @ Offset 0x00003EA5.1 + MCU located at pixel=(2176,128) +*** ERROR: Overread scan segment (before nCode)! @ Offset: 0x00003EA5.1 +*** ERROR: Bad huffman code @ 0x00003EA5.1 +*** ERROR: Bad scan data in MCU(272,16): Chr(Cb) CSS(0,0) @ Offset 0x00003EA5.1 + MCU located at pixel=(2176,128) +*** ERROR: Overread scan segment (before nCode)! @ Offset: 0x00003EA5.1 +*** ERROR: Bad huffman code @ 0x00003EA5.1 +*** ERROR: Bad scan data in MCU(272,16): Chr(Cr) CSS(0,0) @ Offset 0x00003EA5.1 + MCU located at pixel=(2176,128) +*** ERROR: Overread scan segment (before nCode)! @ Offset: 0x00003EA5.1 +*** ERROR: Bad huffman code @ 0x00003EA5.1 +*** ERROR: Bad scan data in MCU(0,17): Lum CSS(0,0) @ Offset 0x00003EA5.1 + MCU located at pixel=(0,136) +*** ERROR: Overread scan segment (before nCode)! @ Offset: 0x00003EA5.1 +*** ERROR: Bad huffman code @ 0x00003EA5.1 +*** ERROR: Bad scan data in MCU(0,17): Chr(Cb) CSS(0,0) @ Offset 0x00003EA5.1 + MCU located at pixel=(0,136) +*** ERROR: Overread scan segment (before nCode)! @ Offset: 0x00003EA5.1 +*** ERROR: Bad huffman code @ 0x00003EA5.1 +*** ERROR: Bad scan data in MCU(0,17): Chr(Cr) CSS(0,0) @ Offset 0x00003EA5.1 + MCU located at pixel=(0,136) +*** ERROR: Overread scan segment (before nCode)! @ Offset: 0x00003EA5.1 +*** ERROR: Bad huffman code @ 0x00003EA5.1 +*** ERROR: Bad scan data in MCU(0,18): Lum CSS(0,0) @ Offset 0x00003EA5.1 + MCU located at pixel=(0,144) + Only reported first 20 instances of this message... + + Compression stats: + Compression Ratio: 3031.33:1 + Bits per pixel: 0.01:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 4841 ( 99%) + # codes of length 03 bits: 33 ( 1%) + # codes of length 04 bits: 6 ( 0%) + # codes of length 05 bits: 1 ( 0%) + # codes of length 06 bits: 0 ( 0%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 9732 (100%) + # codes of length 02 bits: 23 ( 0%) + # codes of length 03 bits: 5 ( 0%) + # codes of length 04 bits: 0 ( 0%) + # codes of length 05 bits: 0 ( 0%) + # codes of length 06 bits: 0 ( 0%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 274 ( 5%) + # codes of length 03 bits: 297 ( 5%) + # codes of length 04 bits: 4903 ( 85%) + # codes of length 05 bits: 88 ( 2%) + # codes of length 06 bits: 26 ( 0%) + # codes of length 07 bits: 98 ( 2%) + # codes of length 08 bits: 25 ( 0%) + # codes of length 09 bits: 4 ( 0%) + # codes of length 10 bits: 14 ( 0%) + # codes of length 11 bits: 1 ( 0%) + # codes of length 12 bits: 2 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 17 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 9760 (100%) + # codes of length 02 bits: 5 ( 0%) + # codes of length 03 bits: 0 ( 0%) + # codes of length 04 bits: 14 ( 0%) + # codes of length 05 bits: 0 ( 0%) + # codes of length 06 bits: 2 ( 0%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[128] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 1014, 0, 0] RGB=[254,254,254] @ MCU[ 0, 0] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 17 + Next position in scan buffer: Offset 0x00003EA5.1 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0012B1C9 + + +*** Searching Compression Signatures *** + + Signature: 01A20F69263117021CD16AEF44D6E650 + Signature (Rotated): 01A20F69263117021CD16AEF44D6E650 + File Offset: 0 bytes + Chroma subsampling: 1x1 + EXIF Make/Model: OK [NIKON] [NIKON D300S] + EXIF Makernotes: NONE + EXIF Software: OK [Adobe Bridge CS6 (Windows)] + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + SW :[Adobe Photoshop ] [Save As 08 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 2 - Image has high probability of being processed/edited + + diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue723-Ordered-Interleaved-Progressive-A.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue723-Ordered-Interleaved-Progressive-A.jpg.txt new file mode 100644 index 0000000000..92adfb3159 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue723-Ordered-Interleaved-Progressive-A.jpg.txt @@ -0,0 +1,519 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue723-Ordered-Interleaved-Progressive-A.jpg] + Filesize: [42798] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 72 x 72 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000014 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 2 1 1 2 2 4 5 6 + DQT, Row #1: 1 1 1 2 3 6 6 6 + DQT, Row #2: 1 1 2 2 4 6 7 6 + DQT, Row #3: 1 2 2 3 5 9 8 6 + DQT, Row #4: 2 2 4 6 7 11 10 8 + DQT, Row #5: 2 4 6 6 8 10 11 9 + DQT, Row #6: 5 6 8 9 10 12 12 10 + DQT, Row #7: 7 9 10 10 11 10 10 10 + Approx quality factor = 95.04 (scaling=9.93 variance=1.25) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000059 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 2 2 2 5 10 10 10 10 + DQT, Row #1: 2 2 3 7 10 10 10 10 + DQT, Row #2: 2 3 6 10 10 10 10 10 + DQT, Row #3: 5 7 10 10 10 10 10 10 + DQT, Row #4: 10 10 10 10 10 10 10 10 + DQT, Row #5: 10 10 10 10 10 10 10 10 + DQT, Row #6: 10 10 10 10 10 10 10 10 + DQT, Row #7: 10 10 10 10 10 10 10 10 + Approx quality factor = 94.91 (scaling=10.18 variance=0.26) + +*** Marker: SOF2 (Progressive DCT, Huffman) (xFFC2) *** + OFFSET: 0x0000009E + Frame header length = 17 + Precision = 8 + Number of Lines = 600 + Samples per Line = 600 + Image Size = 600 x 600 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000B1 + Huffman table length = 29 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 01 02 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (005 total): 04 05 06 07 08 + Codes of length 06 bits (001 total): 09 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 010 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000000D0 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000A58 + Huffman table length = 27 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 01 02 + Codes of length 04 bits (003 total): 03 04 05 + Codes of length 05 bits (001 total): 06 + Codes of length 06 bits (001 total): 07 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 008 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000A75 + Scan header length = 10 + Number of img components = 2 + Component[1]: selector=0x02, table=1(DC),0(AC) + Component[2]: selector=0x03, table=1(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000E55 + Huffman table length = 37 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (000 total): + Codes of length 03 bits (005 total): 02 04 05 11 12 + Codes of length 04 bits (004 total): 00 01 03 06 + Codes of length 05 bits (001 total): 30 + Codes of length 06 bits (005 total): 10 13 14 15 16 + Codes of length 07 bits (001 total): 40 + Codes of length 08 bits (001 total): 20 + Codes of length 09 bits (001 total): A0 + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 018 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000E7C + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 2 + Successive approximation = 0x03 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000015BD + Huffman table length = 82 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (002 total): 02 03 + Codes of length 04 bits (004 total): 00 04 05 11 + Codes of length 05 bits (002 total): 12 21 + Codes of length 06 bits (002 total): 13 31 + Codes of length 07 bits (011 total): 06 10 14 22 30 32 40 41 51 61 92 + Codes of length 08 bits (012 total): 15 23 25 52 53 54 71 73 91 A1 B1 C1 + Codes of length 09 bits (007 total): 20 24 33 42 72 81 93 + Codes of length 10 bits (006 total): 43 44 55 62 63 A2 + Codes of length 11 bits (003 total): 82 83 D1 + Codes of length 12 bits (006 total): 16 26 34 50 A0 B2 + Codes of length 13 bits (007 total): 35 45 64 84 A3 C2 E1 + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 063 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00001611 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 3 .. 63 + Successive approximation = 0x03 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00002E3F + Huffman table length = 42 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 11 + Codes of length 03 bits (001 total): 00 + Codes of length 04 bits (002 total): 21 31 + Codes of length 05 bits (004 total): 10 41 51 61 + Codes of length 06 bits (005 total): 20 71 81 91 F0 + Codes of length 07 bits (005 total): 30 A1 B1 C1 D1 + Codes of length 08 bits (001 total): F1 + Codes of length 09 bits (001 total): E1 + Codes of length 10 bits (001 total): 40 + Codes of length 11 bits (001 total): A0 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 023 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00002E6B + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x32 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00003F38 + Huffman table length = 43 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (002 total): 21 31 + Codes of length 05 bits (002 total): 41 51 + Codes of length 06 bits (001 total): 61 + Codes of length 07 bits (004 total): 10 71 81 91 + Codes of length 08 bits (001 total): A1 + Codes of length 09 bits (005 total): 30 B1 C1 D1 F0 + Codes of length 10 bits (001 total): E1 + Codes of length 11 bits (001 total): F1 + Codes of length 12 bits (000 total): + Codes of length 13 bits (003 total): 20 40 A0 + Codes of length 14 bits (001 total): 50 + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 024 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00003F65 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x21 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00005B9B + Huffman table length = 58 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (003 total): 00 02 03 + Codes of length 04 bits (003 total): 04 11 12 + Codes of length 05 bits (001 total): 21 + Codes of length 06 bits (005 total): 05 13 14 31 51 + Codes of length 07 bits (005 total): 10 15 20 30 41 + Codes of length 08 bits (005 total): 22 23 32 33 71 + Codes of length 09 bits (005 total): 24 34 61 81 F0 + Codes of length 10 bits (008 total): 80 91 A1 B1 C1 D1 E1 F1 + Codes of length 11 bits (003 total): 42 52 62 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 039 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00005BD7 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00006270 + Huffman table length = 57 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (004 total): 00 02 03 11 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (003 total): 12 21 31 + Codes of length 06 bits (002 total): 05 30 + Codes of length 07 bits (004 total): 10 13 14 20 + Codes of length 08 bits (003 total): 32 41 51 + Codes of length 09 bits (007 total): 15 22 23 61 71 81 F0 + Codes of length 10 bits (002 total): 91 A1 + Codes of length 11 bits (004 total): 24 25 33 80 + Codes of length 12 bits (007 total): 42 62 B1 C1 D1 E1 F1 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 038 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000062AB + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00006827 + Huffman table length = 42 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (001 total): 11 + Codes of length 03 bits (000 total): + Codes of length 04 bits (002 total): 00 21 + Codes of length 05 bits (002 total): 31 41 + Codes of length 06 bits (002 total): 51 61 + Codes of length 07 bits (001 total): 71 + Codes of length 08 bits (004 total): 81 91 A1 B1 + Codes of length 09 bits (003 total): 30 C1 D1 + Codes of length 10 bits (001 total): F0 + Codes of length 11 bits (001 total): E1 + Codes of length 12 bits (001 total): 10 + Codes of length 13 bits (000 total): + Codes of length 14 bits (003 total): 40 A0 F1 + Codes of length 15 bits (001 total): 20 + Codes of length 16 bits (000 total): + Total number of codes: 023 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00006853 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000099AB + Huffman table length = 41 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (001 total): 21 + Codes of length 05 bits (003 total): 31 41 61 + Codes of length 06 bits (003 total): 10 51 91 + Codes of length 07 bits (004 total): 20 71 81 B1 + Codes of length 08 bits (002 total): 30 A1 + Codes of length 09 bits (002 total): C1 F0 + Codes of length 10 bits (003 total): 80 E1 F1 + Codes of length 11 bits (001 total): D1 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 022 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000099D6 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000A0BC + Huffman table length = 41 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (002 total): 21 31 + Codes of length 05 bits (001 total): 41 + Codes of length 06 bits (004 total): 10 51 61 71 + Codes of length 07 bits (002 total): 30 91 + Codes of length 08 bits (002 total): 20 81 + Codes of length 09 bits (002 total): A1 B1 + Codes of length 10 bits (002 total): C1 D1 + Codes of length 11 bits (003 total): 40 80 E1 + Codes of length 12 bits (001 total): F1 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 022 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000A0E7 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0000A72C + + +*** Searching Compression Signatures *** + + Signature: 01E764F3ECB6C14A51FF83F1FF6D546B + Signature (Rotated): 01E6610D026E8E6FE4BECEA9B3328A63 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[Minolta Co., Ltd. ] [DiMAGE F100 ] [ ] No + CAM:[NIKON ] [E2500 ] [FINE ] No + CAM:[NIKON ] [E4500 ] [FINE ] No + CAM:[NIKON ] [E5400 ] [FINE ] No + CAM:[NIKON ] [E5700 ] [FINE ] No + CAM:[NIKON ] [E775 ] [FINE ] No + CAM:[NIKON ] [E8700 ] [FINE ] No + CAM:[OLYMPUS CORPORATION ] [C8080WZ ] [ ] No + CAM:[PENTAX ] [PENTAX K10D ] [ ] No + CAM:[SAMSUNG TECHWIN ] [Pro 815 ] [ ] No + CAM:[SAMSUNG TECHWIN ] [VLUU NV 7, NV 7 ] [ ] No + CAM:[SAMSUNG TECHWIN ] [VLUU NV10, NV10 ] [ ] No + CAM:[SEIKO EPSON CORP. ] [PhotoPC 3000Z ] [ ] No + CAM:[SONY ] [DSC-F828 ] [ ] No + CAM:[SONY ] [DSC-H1 ] [ ] No + CAM:[SONY ] [DSC-H2 ] [ ] No + CAM:[SONY ] [DSC-H5 ] [ ] No + CAM:[SONY ] [DSC-H7 ] [ ] No + CAM:[SONY ] [DSC-H9 ] [ ] No + CAM:[SONY ] [DSC-L1 ] [ ] No + CAM:[SONY ] [DSC-N1 ] [ ] No + CAM:[SONY ] [DSC-P150 ] [ ] No + CAM:[SONY ] [DSC-P200 ] [ ] No + CAM:[SONY ] [DSC-S90 ] [ ] No + CAM:[SONY ] [DSC-V3 ] [ ] No + CAM:[SONY ] [DSC-W55 ] [ ] No + CAM:[SONY ] [DSC-W7 ] [ ] No + SW :[Apple ImageIO.framework ] [084 ] + SW :[IJG Library ] [095 ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [095 ] + SW :[IrfanView ] [095 ] + SW :[idImager ] [095 ] + SW :[FastStone Image Viewer ] [095 ] + SW :[NeatImage ] [095 ] + SW :[Paint.NET ] [095 ] + SW :[Photomatix ] [095 ] + SW :[XnView ] [095 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue723-Ordered-Interleaved-Progressive-B.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue723-Ordered-Interleaved-Progressive-B.jpg.txt new file mode 100644 index 0000000000..bcbe9f7f1d --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue723-Ordered-Interleaved-Progressive-B.jpg.txt @@ -0,0 +1,477 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue723-Ordered-Interleaved-Progressive-B.jpg] + Filesize: [36937] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 72 x 72 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000014 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 3 2 2 3 5 8 10 12 + DQT, Row #1: 2 2 3 4 5 12 12 11 + DQT, Row #2: 3 3 3 5 8 11 14 11 + DQT, Row #3: 3 3 4 6 10 17 16 12 + DQT, Row #4: 4 4 7 11 14 22 21 15 + DQT, Row #5: 5 7 11 13 16 21 23 18 + DQT, Row #6: 10 13 16 17 21 24 24 20 + DQT, Row #7: 14 18 19 20 22 20 21 20 + Approx quality factor = 90.06 (scaling=19.88 variance=1.14) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000059 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 3 4 5 9 20 20 20 20 + DQT, Row #1: 4 4 5 13 20 20 20 20 + DQT, Row #2: 5 5 11 20 20 20 20 20 + DQT, Row #3: 9 13 20 20 20 20 20 20 + DQT, Row #4: 20 20 20 20 20 20 20 20 + DQT, Row #5: 20 20 20 20 20 20 20 20 + DQT, Row #6: 20 20 20 20 20 20 20 20 + DQT, Row #7: 20 20 20 20 20 20 20 20 + Approx quality factor = 89.93 (scaling=20.14 variance=0.34) + +*** Marker: SOF2 (Progressive DCT, Huffman) (xFFC2) *** + OFFSET: 0x0000009E + Frame header length = 17 + Precision = 8 + Number of Lines = 600 + Samples per Line = 600 + Image Size = 600 x 600 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000B1 + Huffman table length = 30 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (000 total): + Codes of length 03 bits (000 total): + Codes of length 04 bits (007 total): 02 03 04 05 06 07 08 + Codes of length 05 bits (001 total): 01 + Codes of length 06 bits (001 total): 09 + Codes of length 07 bits (001 total): 0A + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 011 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000000D1 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000A95 + Huffman table length = 28 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 04 05 + Codes of length 04 bits (003 total): 01 03 06 + Codes of length 05 bits (001 total): 02 + Codes of length 06 bits (001 total): 07 + Codes of length 07 bits (001 total): 08 + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000AB3 + Scan header length = 10 + Number of img components = 2 + Component[1]: selector=0x02, table=1(DC),0(AC) + Component[2]: selector=0x03, table=1(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000E60 + Huffman table length = 54 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (000 total): + Codes of length 03 bits (006 total): 00 01 02 03 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (003 total): 11 12 13 + Codes of length 06 bits (002 total): 21 50 + Codes of length 07 bits (004 total): 07 10 14 15 + Codes of length 08 bits (004 total): 20 22 23 31 + Codes of length 09 bits (005 total): 16 24 30 32 36 + Codes of length 10 bits (003 total): 25 40 41 + Codes of length 11 bits (004 total): 17 42 43 80 + Codes of length 12 bits (003 total): 26 33 34 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 035 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000E98 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 5 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00001E73 + Huffman table length = 75 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (002 total): 00 11 + Codes of length 05 bits (002 total): 04 12 + Codes of length 06 bits (005 total): 13 21 31 41 51 + Codes of length 07 bits (006 total): 10 22 52 61 71 81 + Codes of length 08 bits (010 total): 05 14 20 32 42 50 91 A1 B1 C1 + Codes of length 09 bits (005 total): 23 33 62 72 D1 + Codes of length 10 bits (010 total): 24 30 40 53 63 73 82 92 E1 F0 + Codes of length 11 bits (005 total): 15 34 35 43 A2 + Codes of length 12 bits (004 total): 64 74 80 B2 + Codes of length 13 bits (003 total): 60 93 F1 + Codes of length 14 bits (001 total): A3 + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 056 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00001EC0 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 6 .. 63 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00003A1E + Huffman table length = 42 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (001 total): 21 + Codes of length 05 bits (002 total): 31 41 + Codes of length 06 bits (005 total): 10 51 61 71 81 + Codes of length 07 bits (004 total): 50 91 A1 B1 + Codes of length 08 bits (002 total): C1 F0 + Codes of length 09 bits (003 total): 20 D1 E1 + Codes of length 10 bits (001 total): F1 + Codes of length 11 bits (001 total): 40 + Codes of length 12 bits (001 total): 80 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 023 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00003A4A + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x21 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00005386 + Huffman table length = 43 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (001 total): 11 + Codes of length 03 bits (000 total): + Codes of length 04 bits (002 total): 00 21 + Codes of length 05 bits (002 total): 31 41 + Codes of length 06 bits (001 total): 51 + Codes of length 07 bits (003 total): 61 71 81 + Codes of length 08 bits (004 total): 50 91 A1 B1 + Codes of length 09 bits (002 total): 10 C1 + Codes of length 10 bits (003 total): D1 E1 F0 + Codes of length 11 bits (000 total): + Codes of length 12 bits (003 total): 20 80 F1 + Codes of length 13 bits (001 total): 30 + Codes of length 14 bits (001 total): 40 + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 024 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000053B3 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00007B5B + Huffman table length = 50 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 05 12 21 + Codes of length 06 bits (003 total): 13 31 40 + Codes of length 07 bits (003 total): 06 22 41 + Codes of length 08 bits (002 total): 14 32 + Codes of length 09 bits (004 total): 10 16 23 33 + Codes of length 10 bits (005 total): 15 20 42 60 61 + Codes of length 11 bits (005 total): 24 34 50 51 52 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 031 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00007B8F + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 12 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000081EE + Huffman table length = 49 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (004 total): 21 31 41 51 + Codes of length 05 bits (003 total): 02 61 71 + Codes of length 06 bits (007 total): 10 40 81 91 A1 B1 C1 + Codes of length 07 bits (003 total): D1 E1 F0 + Codes of length 08 bits (003 total): 03 50 F1 + Codes of length 09 bits (004 total): 12 22 30 60 + Codes of length 10 bits (003 total): 20 32 70 + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 030 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00008221 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 13 .. 63 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000084F2 + Huffman table length = 48 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (002 total): 03 04 + Codes of length 04 bits (001 total): 05 + Codes of length 05 bits (003 total): 00 11 12 + Codes of length 06 bits (003 total): 06 21 31 + Codes of length 07 bits (003 total): 13 22 40 + Codes of length 08 bits (002 total): 14 41 + Codes of length 09 bits (005 total): 07 10 15 32 50 + Codes of length 10 bits (003 total): 42 60 61 + Codes of length 11 bits (005 total): 20 23 33 43 51 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 029 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00008524 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 12 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00008C10 + Huffman table length = 51 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (002 total): 00 21 + Codes of length 05 bits (004 total): 02 31 41 51 + Codes of length 06 bits (002 total): 61 71 + Codes of length 07 bits (009 total): 03 12 40 81 91 A1 B1 C1 D1 + Codes of length 08 bits (003 total): 10 E1 F0 + Codes of length 09 bits (003 total): 22 32 F1 + Codes of length 10 bits (004 total): 04 50 52 70 + Codes of length 11 bits (003 total): 20 42 60 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 032 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00008C45 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 13 .. 63 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x00009047 + + +*** Searching Compression Signatures *** + + Signature: 013BA18D5561625796E986FDBC09F846 + Signature (Rotated): 01AC57E12793DFA7C46C704625C5AF0F + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[??? ] [Treo 680 ] [ ] Yes + CAM:[Canon ] [Canon PowerShot Pro1 ] [fine ] No + CAM:[NIKON ] [E2500 ] [FINE ] No + CAM:[NIKON ] [E3100 ] [FINE ] No + CAM:[NIKON ] [E4500 ] [FINE ] No + CAM:[NIKON ] [E5000 ] [FINE ] No + CAM:[NIKON ] [E5700 ] [FINE ] No + CAM:[NIKON ] [E775 ] [FINE ] No + CAM:[NIKON ] [E885 ] [FINE ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C3040Z ] [ ] No + CAM:[PENTAX ] [PENTAX Optio 550 ] [ ] No + CAM:[Research In Motion ] [BlackBerry 9530 ] [Superfine ] Yes + CAM:[SEIKO EPSON CORP. ] [PhotoPC 3000Z ] [ ] No + CAM:[SONY ] [DSC-H7 ] [ ] No + CAM:[SONY ] [DSC-H9 ] [ ] No + CAM:[SONY ] [DSC-S90 ] [ ] No + CAM:[SONY ] [DSC-W1 ] [ ] No + CAM:[SONY ] [SONY ] [ ] No + SW :[ACDSee ] [ ] + SW :[FixFoto ] [fine ] + SW :[IJG Library ] [090 ] + SW :[ZoomBrowser EX ] [high ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [090 ] + SW :[IrfanView ] [090 ] + SW :[idImager ] [090 ] + SW :[FastStone Image Viewer ] [090 ] + SW :[NeatImage ] [090 ] + SW :[Paint.NET ] [090 ] + SW :[Photomatix ] [090 ] + SW :[XnView ] [090 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue723-Ordered-Interleaved-Progressive-C.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue723-Ordered-Interleaved-Progressive-C.jpg.txt new file mode 100644 index 0000000000..c76b744313 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/Issue723-Ordered-Interleaved-Progressive-C.jpg.txt @@ -0,0 +1,484 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Issue723-Ordered-Interleaved-Progressive-C.jpg] + Filesize: [46799] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 38 x 38 DPcm (dots per cm) + thumbnail = 0 x 0 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000014 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 2 1 1 2 2 4 5 6 + DQT, Row #1: 1 1 1 2 3 6 6 6 + DQT, Row #2: 1 1 2 2 4 6 7 6 + DQT, Row #3: 1 2 2 3 5 9 8 6 + DQT, Row #4: 2 2 4 6 7 11 10 8 + DQT, Row #5: 2 4 6 6 8 10 11 9 + DQT, Row #6: 5 6 8 9 10 12 12 10 + DQT, Row #7: 7 9 10 10 11 10 10 10 + Approx quality factor = 95.04 (scaling=9.93 variance=1.25) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000059 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 2 2 2 5 10 10 10 10 + DQT, Row #1: 2 2 3 7 10 10 10 10 + DQT, Row #2: 2 3 6 10 10 10 10 10 + DQT, Row #3: 5 7 10 10 10 10 10 10 + DQT, Row #4: 10 10 10 10 10 10 10 10 + DQT, Row #5: 10 10 10 10 10 10 10 10 + DQT, Row #6: 10 10 10 10 10 10 10 10 + DQT, Row #7: 10 10 10 10 10 10 10 10 + Approx quality factor = 94.91 (scaling=10.18 variance=0.26) + +*** Marker: SOF2 (Progressive DCT, Huffman) (xFFC2) *** + OFFSET: 0x0000009E + Frame header length = 17 + Precision = 8 + Number of Lines = 517 + Samples per Line = 502 + Image Size = 502 x 517 + Raw Image Orientation = Portrait + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000B1 + Huffman table length = 30 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (000 total): + Codes of length 03 bits (001 total): 06 + Codes of length 04 bits (004 total): 04 05 07 08 + Codes of length 05 bits (003 total): 02 03 09 + Codes of length 06 bits (001 total): 01 + Codes of length 07 bits (001 total): 0A + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 011 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000000D1 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000858 + Huffman table length = 29 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (000 total): + Codes of length 03 bits (001 total): 05 + Codes of length 04 bits (005 total): 02 03 04 06 07 + Codes of length 05 bits (001 total): 01 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 010 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000877 + Scan header length = 10 + Number of img components = 2 + Component[1]: selector=0x02, table=1(DC),0(AC) + Component[2]: selector=0x03, table=1(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000012C2 + Huffman table length = 67 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 03 + Codes of length 03 bits (004 total): 01 02 04 05 + Codes of length 04 bits (002 total): 00 06 + Codes of length 05 bits (001 total): 11 + Codes of length 06 bits (002 total): 07 12 + Codes of length 07 bits (004 total): 13 14 21 50 + Codes of length 08 bits (004 total): 08 15 22 31 + Codes of length 09 bits (002 total): 17 23 + Codes of length 10 bits (006 total): 10 16 20 24 32 41 + Codes of length 11 bits (008 total): 25 33 34 42 51 52 54 62 + Codes of length 12 bits (004 total): 26 30 35 40 + Codes of length 13 bits (005 total): 18 27 37 55 56 + Codes of length 14 bits (005 total): 44 53 61 70 80 + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 048 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00001307 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 12 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00002FBE + Huffman table length = 80 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (002 total): 02 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 12 21 31 + Codes of length 06 bits (005 total): 13 22 41 51 61 + Codes of length 07 bits (011 total): 05 10 14 32 50 71 81 91 A1 B1 C1 + Codes of length 08 bits (007 total): 20 23 42 52 62 82 D1 + Codes of length 09 bits (009 total): 15 30 33 53 72 92 A2 B2 E1 + Codes of length 10 bits (006 total): 24 40 43 73 C2 E2 + Codes of length 11 bits (004 total): 06 63 D2 F0 + Codes of length 12 bits (005 total): 25 54 75 A3 B3 + Codes of length 13 bits (005 total): 44 64 70 80 83 + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 061 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00003010 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 13 .. 63 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000048A9 + Huffman table length = 43 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (002 total): 21 31 + Codes of length 05 bits (001 total): 41 + Codes of length 06 bits (003 total): 51 61 71 + Codes of length 07 bits (003 total): 10 81 A1 + Codes of length 08 bits (004 total): 50 91 B1 C1 + Codes of length 09 bits (003 total): 20 D1 F0 + Codes of length 10 bits (001 total): F1 + Codes of length 11 bits (001 total): E1 + Codes of length 12 bits (001 total): 30 + Codes of length 13 bits (001 total): 70 + Codes of length 14 bits (001 total): 80 + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 024 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000048D6 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x21 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000060D3 + Huffman table length = 42 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (001 total): 11 + Codes of length 03 bits (000 total): + Codes of length 04 bits (002 total): 00 21 + Codes of length 05 bits (002 total): 31 41 + Codes of length 06 bits (002 total): 51 61 + Codes of length 07 bits (001 total): 71 + Codes of length 08 bits (004 total): 50 81 91 A1 + Codes of length 09 bits (002 total): 10 B1 + Codes of length 10 bits (002 total): C1 F0 + Codes of length 11 bits (002 total): D1 F1 + Codes of length 12 bits (003 total): 30 70 E1 + Codes of length 13 bits (001 total): 20 + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 023 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000060FF + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00007FD1 + Huffman table length = 54 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 03 + Codes of length 03 bits (004 total): 01 02 04 05 + Codes of length 04 bits (001 total): 00 + Codes of length 05 bits (002 total): 06 11 + Codes of length 06 bits (006 total): 07 12 13 21 31 50 + Codes of length 07 bits (001 total): 14 + Codes of length 08 bits (002 total): 15 22 + Codes of length 09 bits (005 total): 10 23 32 33 41 + Codes of length 10 bits (003 total): 16 17 20 + Codes of length 11 bits (002 total): 24 34 + Codes of length 12 bits (007 total): 25 26 30 40 42 51 70 + Codes of length 13 bits (001 total): 80 + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 035 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00008009 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 8 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00008F76 + Huffman table length = 59 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (003 total): 00 02 11 + Codes of length 04 bits (002 total): 21 31 + Codes of length 05 bits (004 total): 03 12 41 51 + Codes of length 06 bits (003 total): 61 71 81 + Codes of length 07 bits (005 total): 10 22 50 91 A1 + Codes of length 08 bits (007 total): 32 52 B1 C1 D1 F0 F1 + Codes of length 09 bits (002 total): 04 13 + Codes of length 10 bits (004 total): 20 23 42 72 + Codes of length 11 bits (006 total): 62 70 80 82 92 E1 + Codes of length 12 bits (003 total): 30 60 A2 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 040 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00008FB3 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 9 .. 63 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00009B28 + Huffman table length = 51 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 03 + Codes of length 03 bits (004 total): 01 02 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (003 total): 00 07 11 + Codes of length 06 bits (003 total): 12 13 31 + Codes of length 07 bits (003 total): 08 21 50 + Codes of length 08 bits (003 total): 10 14 15 + Codes of length 09 bits (004 total): 20 23 32 41 + Codes of length 10 bits (001 total): 16 + Codes of length 11 bits (003 total): 22 24 40 + Codes of length 12 bits (005 total): 17 18 33 34 70 + Codes of length 13 bits (001 total): 80 + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 032 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00009B5D + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 8 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000AB99 + Huffman table length = 62 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (003 total): 00 02 11 + Codes of length 04 bits (002 total): 03 21 + Codes of length 05 bits (004 total): 12 31 41 51 + Codes of length 06 bits (003 total): 04 61 71 + Codes of length 07 bits (005 total): 10 22 32 50 81 + Codes of length 08 bits (004 total): 13 91 A1 B1 + Codes of length 09 bits (008 total): 20 23 42 52 62 72 C1 D1 + Codes of length 10 bits (005 total): 05 14 92 E1 F0 + Codes of length 11 bits (003 total): 30 33 F1 + Codes of length 12 bits (005 total): 70 80 82 90 A2 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 043 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000ABD9 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 9 .. 63 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0000B6CD + + +*** Searching Compression Signatures *** + + Signature: 01E764F3ECB6C14A51FF83F1FF6D546B + Signature (Rotated): 01E6610D026E8E6FE4BECEA9B3328A63 + File Offset: 0 bytes + Chroma subsampling: 1x1 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[Minolta Co., Ltd. ] [DiMAGE F100 ] [ ] No + CAM:[NIKON ] [E2500 ] [FINE ] No + CAM:[NIKON ] [E4500 ] [FINE ] No + CAM:[NIKON ] [E5400 ] [FINE ] No + CAM:[NIKON ] [E5700 ] [FINE ] No + CAM:[NIKON ] [E775 ] [FINE ] No + CAM:[NIKON ] [E8700 ] [FINE ] No + CAM:[OLYMPUS CORPORATION ] [C8080WZ ] [ ] No + CAM:[PENTAX ] [PENTAX K10D ] [ ] No + CAM:[SAMSUNG TECHWIN ] [Pro 815 ] [ ] No + CAM:[SAMSUNG TECHWIN ] [VLUU NV 7, NV 7 ] [ ] No + CAM:[SAMSUNG TECHWIN ] [VLUU NV10, NV10 ] [ ] No + CAM:[SEIKO EPSON CORP. ] [PhotoPC 3000Z ] [ ] No + CAM:[SONY ] [DSC-F828 ] [ ] No + CAM:[SONY ] [DSC-H1 ] [ ] No + CAM:[SONY ] [DSC-H2 ] [ ] No + CAM:[SONY ] [DSC-H5 ] [ ] No + CAM:[SONY ] [DSC-H7 ] [ ] No + CAM:[SONY ] [DSC-H9 ] [ ] No + CAM:[SONY ] [DSC-L1 ] [ ] No + CAM:[SONY ] [DSC-N1 ] [ ] No + CAM:[SONY ] [DSC-P150 ] [ ] No + CAM:[SONY ] [DSC-P200 ] [ ] No + CAM:[SONY ] [DSC-S90 ] [ ] No + CAM:[SONY ] [DSC-V3 ] [ ] No + CAM:[SONY ] [DSC-W55 ] [ ] No + CAM:[SONY ] [DSC-W7 ] [ ] No + SW :[Apple ImageIO.framework ] [084 ] + SW :[IJG Library ] [095 ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [095 ] + SW :[IrfanView ] [095 ] + SW :[idImager ] [095 ] + SW :[FastStone Image Viewer ] [095 ] + SW :[NeatImage ] [095 ] + SW :[Paint.NET ] [095 ] + SW :[Photomatix ] [095 ] + SW :[XnView ] [095 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/issue750-exif-load.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/issue750-exif-load.jpg.txt new file mode 100644 index 0000000000..6070e1cdab --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/issue750-exif-load.jpg.txt @@ -0,0 +1,772 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\issue750-exif-load.jpg] + Filesize: [36885] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000002 + Length = 3656 + Identifier = [Exif] + Identifier TIFF = 0x[49492A00 08000000] + Endian = Intel (little) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000014 + Dir Length = 0x0010 + [Make ] = "Canon" + [Model ] = "Canon EOS 70D" + [Orientation ] = 1 = Row 0: top, Col 0: left + [XResolution ] = 720000/10000 + [YResolution ] = 720000/10000 + [ResolutionUnit ] = Inch + [Software ] = "Adobe Photoshop CS6 (Windows)" + [DateTime ] = "2018:02:28 17:51:59" + [YCbCrPositioning ] = Centered + [ExifOffset ] = @ 0x012C + [GPSOffset ] = @ 0x04C8 + Offset to Next IFD = 0x000004DC + + EXIF IFD1 @ Absolute 0x000004E8 + Dir Length = 0x0006 + [Compression ] = JPEG + [XResolution ] = 72/1 + [YResolution ] = 72/1 + [ResolutionUnit ] = Inch + [JpegIFOffset ] = @ +0x053A = @ 0x0546 + [JpegIFByteCount ] = 0x[00000906] / 2310 + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x00000138 + Dir Length = 0x0025 + [ExposureTime ] = 1/60 s + [FNumber ] = F11.0 + [ExposureProgram ] = Manual + [ISOSpeedRatings ] = 100 + [ExifVersion ] = 02.30 + [DateTimeOriginal ] = "2017:09:14 14:41:54" + [DateTimeDigitized ] = "2017:09:14 14:41:54" + [ComponentsConfiguration ] = [Y Cb Cr .] + [ShutterSpeedValue ] = 393216/65536 + [ApertureValue ] = 458752/65536 + [ExposureBiasValue ] = 0.00 eV + [MeteringMode ] = Pattern + [Flash ] = Flash did not fire + [FocalLength ] = 50 mm + [UserComment ] = "" + [SubSecTime ] = "277" + [SubSecTimeOriginal ] = "00" + [SubSecTimeDigitized ] = "00" + [FlashPixVersion ] = 01.00 + [ColorSpace ] = sRGB + [ExifImageWidth ] = 0x[000001D3] / 467 + [ExifImageHeight ] = 0x[000002BC] / 700 + [ExifInteroperabilityOffset ] = @ 0x04A8 + [FocalPlaneXResolution ] = 5472000/899 + [FocalPlaneYResolution ] = 3648000/599 + [FocalPlaneResolutionUnit ] = Inch + [CustomRendered ] = Normal process + [ExposureMode ] = Manual exposure + [WhiteBalance ] = Auto white balance + [SceneCaptureType ] = Standard + + EXIF GPSIFD @ Absolute 0x000004D4 + Dir Length = 0x0001 + [GPSVersionID ] = 2.3.0.0 + + EXIF InteropIFD @ Absolute 0x000004B4 + Dir Length = 0x0002 + [InteroperabilityIndex ] = "R98" + [InteroperabilityVersion ] = 01.00 + +*** Marker: APP13 (xFFED) *** + OFFSET: 0x00000E4C + Length = 4648 + Identifier = [Photoshop 3.0] + 8BIM: [0x0404] Name="" Len=[0x002C] DefinedName="IPTC-NAA record" + IPTC [001:090] Coded Character Set = "%G" + IPTC [002:000] Record Version = 51658 + IPTC [002:055] Date Created = "20170914" + IPTC [002:060] Time Created = "144154+0000" + 8BIM: [0x0425] Name="" Len=[0x0010] DefinedName="Caption digest" + Caption digest = | 0x8B 58 80 D1 16 85 C7 6D 47 04 59 0B 61 59 FA 69 | .X.....mG.Y.aY.i + 8BIM: [0x043A] Name="" Len=[0x00E5] DefinedName="Print Information" + Print Information = + | 0x00 00 00 10 00 00 00 01 00 00 00 00 00 0B 70 72 | ..............pr + | 0x69 6E 74 4F 75 74 70 75 74 00 00 00 05 00 00 00 | intOutput....... + | 0x00 50 73 74 53 62 6F 6F 6C 01 00 00 00 00 49 6E | .PstSbool.....In + | 0x74 65 65 6E 75 6D 00 00 00 00 49 6E 74 65 00 00 | teenum....Inte.. + | 0x00 00 43 6C 72 6D 00 00 00 0F 70 72 69 6E 74 53 | ..Clrm....printS + | 0x69 78 74 65 65 6E 42 69 74 62 6F 6F 6C 00 00 00 | ixteenBitbool... + | 0x00 0B 70 72 69 6E 74 65 72 4E 61 6D 65 54 45 58 | ..printerNameTEX + | 0x54 00 00 00 01 00 00 00 00 00 0F 70 72 69 6E 74 | T..........print + | ... + 8BIM: [0x043B] Name="" Len=[0x022D] DefinedName="Print Style" + Print Style = + | 0x00 00 00 10 00 00 00 01 00 00 00 00 00 12 70 72 | ..............pr + | 0x69 6E 74 4F 75 74 70 75 74 4F 70 74 69 6F 6E 73 | intOutputOptions + | 0x00 00 00 17 00 00 00 00 43 70 74 6E 62 6F 6F 6C | ........Cptnbool + | 0x00 00 00 00 00 43 6C 62 72 62 6F 6F 6C 00 00 00 | .....Clbrbool... + | 0x00 00 52 67 73 4D 62 6F 6F 6C 00 00 00 00 00 43 | ..RgsMbool.....C + | 0x72 6E 43 62 6F 6F 6C 00 00 00 00 00 43 6E 74 43 | rnCbool.....CntC + | 0x62 6F 6F 6C 00 00 00 00 00 4C 62 6C 73 62 6F 6F | bool.....Lblsboo + | 0x6C 00 00 00 00 00 4E 67 74 76 62 6F 6F 6C 00 00 | l.....Ngtvbool.. + | ... + 8BIM: [0x03ED] Name="" Len=[0x0010] DefinedName="ResolutionInfo structure" + Horizontal resolution = 72 pixels per inch + Width unit = cm + Vertical resolution = 72 pixels per inch + Height unit = cm + 8BIM: [0x0426] Name="" Len=[0x000E] DefinedName="Print scale" + Style = centered + X location = 0.00000 + Y location = 0.00000 + Scale = 1.00000 + 8BIM: [0x040D] Name="" Len=[0x0004] DefinedName="Global Angle" + Global Angle = 30 degrees + 8BIM: [0x0419] Name="" Len=[0x0004] DefinedName="Global Altitude" + Global Altitude = 30 + 8BIM: [0x03F3] Name="" Len=[0x0009] DefinedName="Print flags" + Labels = false + Crop marks = false + Color bars = false + Registration marks = false + Negative = false + Flip = false + Interpolate = false + Caption = false + Print flags = true + 8BIM: [0x2710] Name="" Len=[0x000A] DefinedName="Print flags information" + Version = 1 + Center crop marks = 0 + Reserved = 0 + Bleed width value = 0 + Bleed width scale = 2 + 8BIM: [0x03F5] Name="" Len=[0x0048] DefinedName="Color halftoning information" + Color halftoning information = + | 0x00 2F 66 66 00 01 00 6C 66 66 00 06 00 00 00 00 | ./ff...lff...... + | 0x00 01 00 2F 66 66 00 01 00 A1 99 9A 00 06 00 00 | .../ff.......... + | 0x00 00 00 01 00 32 00 00 00 01 00 5A 00 00 00 06 | .....2.....Z.... + | 0x00 00 00 00 00 01 00 35 00 00 00 01 00 2D 00 00 | .......5.....-.. + | 0x00 06 00 00 00 00 00 01 | ........ + 8BIM: [0x03F8] Name="" Len=[0x0070] DefinedName="Color transfer functions" + Color transfer functions = + | 0x00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF | ................ + | 0xFF FF FF FF FF FF FF FF 03 E8 00 00 00 00 FF FF | ................ + | 0xFF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF | ................ + | 0xFF FF FF FF 03 E8 00 00 00 00 FF FF FF FF FF FF | ................ + | 0xFF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF | ................ + | 0x03 E8 00 00 00 00 FF FF FF FF FF FF FF FF FF FF | ................ + | 0xFF FF FF FF FF FF FF FF FF FF FF FF 03 E8 00 00 | ................ + 8BIM: [0x0408] Name="" Len=[0x0010] DefinedName="Grid and guides information" + Version = 1 + Grid Horizontal = 576 + Grid Vertical = 576 + Number of Guide Resources = 0 + 8BIM: [0x041E] Name="" Len=[0x0004] DefinedName="URL List" + URL List = | 0x00 00 00 00 | .... + 8BIM: [0x041A] Name="" Len=[0x0341] DefinedName="Slices" + Slice Header: + Version = 6 + Bound Rect (top) = 0 + Bound Rect (left) = 0 + Bound Rect (bottom) = 700 + Bound Rect (right) = 467 + Name of group of slices = "513566" + Number of slices = 1 + ----- + Slice #0: + Slice Resource: + ID = 0 + Group ID = 0 + Origin = 0 + Name = "" + Type = 1 + Position (top) = 0 + Position (left) = 0 + Position (bottom) = 467 + Position (right) = 700 + URL = "" + Target = "" + Message = "" + Alt Tag = "" + Cell text is HTML = true + Cell text = "" + Horizontal alignment = 0 + Vertical alignment = 0 + Alpha color = 0 + Red = 0 + Green = 0 + Blue = 0 + Descriptor version = 16 + Descriptor: + Name from classID = "" + classID = "null" + Num items in descriptor = 2 + ----- + Descriptor item #0: + Key = "bounds" + OSType key = "Objc" + Descriptor: + Name from classID = "" + classID = "Rct1" + Num items in descriptor = 4 + ----- + Descriptor item #0: + Key = "Top " + OSType key = "long" + Value = 0 + Descriptor item #1: + Key = "Left" + OSType key = "long" + Value = 0 + Descriptor item #2: + Key = "Btom" + OSType key = "long" + Value = 700 + Descriptor item #3: + Key = "Rght" + OSType key = "long" + Value = 467 + ----- + Descriptor item #1: + Key = "slices" + OSType key = "VlLs" + Num items in list = 1 + ----- + Item #0: + OSType key = "" + Descriptor: + Name from classID = "" + classID = "slice" + Num items in descriptor = 18 + ----- + Descriptor item #0: + Key = "sliceID" + OSType key = "long" + Value = 0 + Descriptor item #1: + Key = "groupID" + OSType key = "long" + Value = 0 + Descriptor item #2: + Key = "origin" + OSType key = "enum" + Type = "ESliceOrigin" + Enum = "autoGenerated" + Descriptor item #3: + Key = "Type" + OSType key = "enum" + Type = "ESliceType" + Enum = "Img " + Descriptor item #4: + Key = "bounds" + OSType key = "Objc" + Descriptor: + Name from classID = "" + classID = "Rct1" + Num items in descriptor = 4 + ----- + Descriptor item #0: + Key = "Top " + OSType key = "long" + Value = 0 + Descriptor item #1: + Key = "Left" + OSType key = "long" + Value = 0 + Descriptor item #2: + Key = "Btom" + OSType key = "long" + Value = 700 + Descriptor item #3: + Key = "Rght" + OSType key = "long" + Value = 467 + ----- + Descriptor item #5: + Key = "url" + OSType key = "TEXT" + String = "" + Descriptor item #6: + Key = "null" + OSType key = "TEXT" + String = "" + Descriptor item #7: + Key = "Msge" + OSType key = "TEXT" + String = "" + Descriptor item #8: + Key = "altTag" + OSType key = "TEXT" + String = "" + Descriptor item #9: + Key = "cellTextIsHTML" + OSType key = "bool" + Value = true + Descriptor item #10: + Key = "cellText" + OSType key = "TEXT" + String = "" + Descriptor item #11: + Key = "horzAlign" + OSType key = "enum" + Type = "ESliceHorzAlign" + Enum = "default" + Descriptor item #12: + Key = "vertAlign" + OSType key = "enum" + Type = "ESliceVertAlign" + Enum = "default" + Descriptor item #13: + Key = "bgColorType" + OSType key = "enum" + Type = "ESliceBGColorType" + Enum = "None" + Descriptor item #14: + Key = "topOutset" + OSType key = "long" + Value = 0 + Descriptor item #15: + Key = "leftOutset" + OSType key = "long" + Value = 0 + Descriptor item #16: + Key = "bottomOutset" + OSType key = "long" + Value = 0 + Descriptor item #17: + Key = "rightOutset" + OSType key = "long" + Value = 0 + ----- + ----- + ----- + ----- + 8BIM: [0x0428] Name="" Len=[0x000C] DefinedName="Pixel Aspect Ratio" + Version = 2 + X/Y Ratio = 1.00000 + 8BIM: [0x0414] Name="" Len=[0x0004] DefinedName="Document-specific IDs seed number" + Base value = 1 + 8BIM: [0x040C] Name="" Len=[0x0922] DefinedName="Thumbnail resources" + Format = 1 + Width of thumbnail = 107 pixels + Height of thumbnail = 160 pixels + Widthbytes = 324 bytes + Total size = 51840 bytes + Size after compression = 2310 bytes + Bits per pixel = 24 bits + Number of planes = 1 + JFIF data @ 0x000016FA + 8BIM: [0x0421] Name="" Len=[0x0055] DefinedName="Version Info" + Version = 1 + hasRealMergedData = 1 + Writer name = "Adobe Photoshop" + Reader name = "Adobe Photoshop CS6" + File version = 1 + 8BIM: [0x0406] Name="" Len=[0x0007] DefinedName="JPEG quality" + Photoshop Save As Quality = 7 + Photoshop Save Format = "Standard" + Photoshop Save Progressive Scans = "3 Scans" + ??? = 1 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00002076 + Length = 3752 + Identifier = [http://ns.adobe.com/xap/1.0/] + XMP = + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x00002F20 + Length = 3160 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 1 of 1 + Profile Size : 3144 bytes + Preferred CMM Type : 'Lino' (0x4C696E6F) + Profile Version : 0.2.1.0 (0x02100000) + Profile Device/Class : Display Device profile ('mntr' (0x6D6E7472)) + Data Colour Space : rgbData ('RGB ' (0x52474220)) + Profile connection space (PCS) : 'XYZ ' (0x58595A20) + Profile creation date : 1998-02-09 06:49:00 + Profile file signature : 'acsp' (0x61637370) + Primary platform : Microsoft Corporation ('MSFT' (0x4D534654)) + Profile flags : 0x00000000 + Profile flags > Profile not embedded + Profile flags > Profile can't be used independently of embedded + Device Manufacturer : 'IEC ' (0x49454320) + Device Model : 'sRGB' (0x73524742) + Device attributes : 0x00000000_00000000 + Device attributes > Reflective + Device attributes > Glossy + Device attributes > Media polarity = negative + Device attributes > Black & white media + Rendering intent : Perceptual + Profile creator : 'HP ' (0x48502020) + Profile ID : 0x00000000_00000000_00000000_00000000 + +*** Marker: APP14 (xFFEE) *** + OFFSET: 0x00003B7A + Length = 14 + DCTEncodeVersion = 100 + APP14Flags0 = 0 + APP14Flags1 = 0 + ColorTransform = 1 [YCbCr] + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00003B8A + Table length = 132 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 10 7 7 10 15 18 20 17 + DQT, Row #1: 7 8 8 10 13 16 12 12 + DQT, Row #2: 7 8 8 10 16 12 12 12 + DQT, Row #3: 10 10 10 18 12 12 12 12 + DQT, Row #4: 15 13 16 12 12 12 12 12 + DQT, Row #5: 18 16 12 12 12 12 12 12 + DQT, Row #6: 20 12 12 12 12 12 12 12 + DQT, Row #7: 17 12 12 12 12 12 12 12 + Approx quality factor = 83.48 (scaling=33.04 variance=462.13) + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 11 12 21 34 20 20 17 17 + DQT, Row #1: 12 19 24 14 14 12 12 12 + DQT, Row #2: 21 24 14 14 12 12 12 12 + DQT, Row #3: 34 14 14 12 12 12 12 12 + DQT, Row #4: 20 14 12 12 12 12 12 12 + DQT, Row #5: 20 12 12 12 12 12 12 12 + DQT, Row #6: 17 12 12 12 12 12 12 12 + DQT, Row #7: 17 12 12 12 12 12 12 12 + Approx quality factor = 87.98 (scaling=24.05 variance=592.80) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x00003C10 + Frame header length = 17 + Precision = 8 + Number of Lines = 700 + Samples per Line = 467 + Image Size = 467 x 700 + Raw Image Orientation = Portrait + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DRI (Restart Interval) (xFFDD) *** + OFFSET: 0x00003C23 + Length = 4 + interval = 59 + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00003C29 + Huffman table length = 418 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (000 total): + Codes of length 03 bits (007 total): 04 05 03 02 06 01 00 + Codes of length 04 bits (001 total): 07 + Codes of length 05 bits (001 total): 08 + Codes of length 06 bits (001 total): 09 + Codes of length 07 bits (001 total): 0A + Codes of length 08 bits (001 total): 0B + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 00 + Codes of length 03 bits (002 total): 02 03 + Codes of length 04 bits (003 total): 04 05 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (001 total): 0A + Codes of length 09 bits (001 total): 0B + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 11 04 00 + Codes of length 05 bits (003 total): 05 21 12 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 51 06 13 61 + Codes of length 08 bits (002 total): 22 71 + Codes of length 09 bits (006 total): 81 14 32 91 A1 07 + Codes of length 10 bits (007 total): 15 B1 42 23 C1 52 D1 + Codes of length 11 bits (003 total): E1 33 16 + Codes of length 12 bits (004 total): 62 F0 24 72 + Codes of length 13 bits (002 total): 82 F1 + Codes of length 14 bits (006 total): 25 43 34 53 92 A2 + Codes of length 15 bits (002 total): B2 63 + Codes of length 16 bits (115 total): 73 C2 35 44 27 93 A3 B3 36 17 54 64 74 C3 D2 E2 + 08 26 83 09 0A 18 19 84 94 45 46 A4 B4 56 D3 55 + 28 1A F2 E3 F3 C4 D4 E4 F4 65 75 85 95 A5 B5 C5 + D5 E5 F5 66 76 86 96 A6 B6 C6 D6 E6 F6 37 47 57 + 67 77 87 97 A7 B7 C7 D7 E7 F7 38 48 58 68 78 88 + 98 A8 B8 C8 D8 E8 F8 29 39 49 59 69 79 89 99 A9 + B9 C9 D9 E9 F9 2A 3A 4A 5A 6A 7A 8A 9A AA BA CA + DA EA FA + Total number of codes: 162 + + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 00 + Codes of length 03 bits (002 total): 02 11 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (002 total): 04 21 + Codes of length 06 bits (003 total): 12 31 41 + Codes of length 07 bits (005 total): 05 51 13 61 22 + Codes of length 08 bits (005 total): 06 71 81 91 32 + Codes of length 09 bits (004 total): A1 B1 F0 14 + Codes of length 10 bits (005 total): C1 D1 E1 23 42 + Codes of length 11 bits (006 total): 15 52 62 72 F1 33 + Codes of length 12 bits (004 total): 24 34 43 82 + Codes of length 13 bits (008 total): 16 92 53 25 A2 63 B2 C2 + Codes of length 14 bits (003 total): 07 73 D2 + Codes of length 15 bits (003 total): 35 E2 44 + Codes of length 16 bits (109 total): 83 17 54 93 08 09 0A 18 19 26 36 45 1A 27 64 74 + 55 37 F2 A3 B3 C3 28 29 D3 E3 F3 84 94 A4 B4 C4 + D4 E4 F4 65 75 85 95 A5 B5 C5 D5 E5 F5 46 56 66 + 76 86 96 A6 B6 C6 D6 E6 F6 47 57 67 77 87 97 A7 + B7 C7 D7 E7 F7 38 48 58 68 78 88 98 A8 B8 C8 D8 + E8 F8 39 49 59 69 79 89 99 A9 B9 C9 D9 E9 F9 2A + 3A 4A 5A 6A 7A 8A 9A AA BA CA DA EA FA + Total number of codes: 162 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00003DCD + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x00003DDB + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x00009013.0 + + Compression stats: + Compression Ratio: 46.60:1 + Bits per pixel: 0.52:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 0 ( 0%) + # codes of length 03 bits: 4910 ( 95%) + # codes of length 04 bits: 280 ( 5%) + # codes of length 05 bits: 2 ( 0%) + # codes of length 06 bits: 0 ( 0%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 9997 ( 96%) + # codes of length 03 bits: 335 ( 3%) + # codes of length 04 bits: 52 ( 1%) + # codes of length 05 bits: 0 ( 0%) + # codes of length 06 bits: 0 ( 0%) + # codes of length 07 bits: 0 ( 0%) + # codes of length 08 bits: 0 ( 0%) + # codes of length 09 bits: 0 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 5649 ( 33%) + # codes of length 03 bits: 1560 ( 9%) + # codes of length 04 bits: 6758 ( 39%) + # codes of length 05 bits: 1189 ( 7%) + # codes of length 06 bits: 349 ( 2%) + # codes of length 07 bits: 488 ( 3%) + # codes of length 08 bits: 255 ( 1%) + # codes of length 09 bits: 351 ( 2%) + # codes of length 10 bits: 254 ( 1%) + # codes of length 11 bits: 70 ( 0%) + # codes of length 12 bits: 76 ( 0%) + # codes of length 13 bits: 14 ( 0%) + # codes of length 14 bits: 115 ( 1%) + # codes of length 15 bits: 41 ( 0%) + # codes of length 16 bits: 88 ( 1%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 10917 ( 93%) + # codes of length 03 bits: 435 ( 4%) + # codes of length 04 bits: 77 ( 1%) + # codes of length 05 bits: 75 ( 1%) + # codes of length 06 bits: 121 ( 1%) + # codes of length 07 bits: 47 ( 0%) + # codes of length 08 bits: 36 ( 0%) + # codes of length 09 bits: 5 ( 0%) + # codes of length 10 bits: 15 ( 0%) + # codes of length 11 bits: 39 ( 0%) + # codes of length 12 bits: 11 ( 0%) + # codes of length 13 bits: 3 ( 0%) + # codes of length 14 bits: 2 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 1 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[222] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 1020, 0, 0] RGB=[255,255,255] @ MCU[ 0, 0] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 87 + Next position in scan buffer: Offset 0x00009012.5 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x00009013 + + +*** Embedded JPEG Thumbnail *** + Offset: 0x00000546 + Length: 0x00000906 (2310) + + * Embedded Thumb Marker: SOI + + * Embedded Thumb Marker: APP13 + Length = 12 + + * Embedded Thumb Marker: APP14 + Length = 14 + + * Embedded Thumb Marker: DQT + Length = 132 + ---- + Precision=8 bits + Destination ID=0 (Luminance, typically) + DQT, Row #0: 12 8 8 12 17 21 24 17 + DQT, Row #1: 8 9 9 11 15 19 12 12 + DQT, Row #2: 8 9 10 12 19 12 12 12 + DQT, Row #3: 12 11 12 21 12 12 12 12 + DQT, Row #4: 17 15 19 12 12 12 12 12 + DQT, Row #5: 21 19 12 12 12 12 12 12 + DQT, Row #6: 24 12 12 12 12 12 12 12 + DQT, Row #7: 17 12 12 12 12 12 12 12 + ---- + Precision=8 bits + Destination ID=1 (Chrominance, typically) + DQT, Row #0: 13 11 13 16 20 20 17 17 + DQT, Row #1: 11 14 14 14 14 12 12 12 + DQT, Row #2: 13 14 14 14 12 12 12 12 + DQT, Row #3: 16 14 14 12 12 12 12 12 + DQT, Row #4: 20 14 12 12 12 12 12 12 + DQT, Row #5: 20 12 12 12 12 12 12 12 + DQT, Row #6: 17 12 12 12 12 12 12 12 + DQT, Row #7: 17 12 12 12 12 12 12 12 + + * Embedded Thumb Marker: SOF + Frame header length = 17 + Precision = 8 + Number of Lines = 160 + Samples per Line = 107 + Image Size = 107 x 160 + + * Embedded Thumb Marker: DRI + Length = 4 + + * Embedded Thumb Marker: DHT + Length = 319 + + * Embedded Thumb Marker: SOS + Skipping scan data + Skipped 1785 bytes + + * Embedded Thumb Marker: EOI + + * Embedded Thumb Signature: 01C2DDA29A1B5DCCD5E217CF9C558A62 + +*** Searching Compression Signatures *** + + Signature: 0165B3F1B409A4D8D5F2ADFFA970D3A5 + Signature (Rotated): 0165B3F1B409A4D8D5F2ADFFA970D3A5 + File Offset: 0 bytes + Chroma subsampling: 1x1 + EXIF Make/Model: OK [Canon] [Canon EOS 70D] + EXIF Makernotes: NONE + EXIF Software: OK [Adobe Photoshop CS6 (Windows)] + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[NIKON ] [E885 ] [FINE ] Yes + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C700UZ ] [ ] Yes + SW :[Adobe Photoshop 7.0 ] [Save As 07 ] + + NOTE: Photoshop IRB detected + NOTE: EXIF Software field recognized as from editor + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + diff --git a/tests/Images/Input/Jpg/issues/JpegSnoopReports/issue750-exif-tranform.jpg.txt b/tests/Images/Input/Jpg/issues/JpegSnoopReports/issue750-exif-tranform.jpg.txt new file mode 100644 index 0000000000..48760626be --- /dev/null +++ b/tests/Images/Input/Jpg/issues/JpegSnoopReports/issue750-exif-tranform.jpg.txt @@ -0,0 +1,435 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\issue750-exif-tranform.jpg] + Filesize: [5587341] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 300 x 300 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000014 + Length = 8272 + Identifier = [Exif] + Identifier TIFF = 0x[4D4D002A 00000008] + Endian = Motorola (big) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x00000026 + Dir Length = 0x000A + [Make ] = "Canon" + [Model ] = "Canon EOS 500D" + [Orientation ] = 1 = Row 0: top, Col 0: left + [DateTime ] = "2017:12:06 15:48:51" + [Artist ] = "" + [YCbCrPositioning ] = Co-sited + [Copyright ] = "" + [ExifOffset ] = @ 0x00B0 + [GPSOffset ] = @ 0x2034 + [XPAuthor ] = "??" + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x000000CE + Dir Length = 0x0020 + [ExposureTime ] = 1/160 s + [FNumber ] = F9.0 + [ExposureProgram ] = Normal program + [ISOSpeedRatings ] = 3200 + [ExifVersion ] = 02.21 + [DateTimeOriginal ] = "2017:12:06 15:48:51" + [DateTimeDigitized ] = "2017:12:06 15:48:51" + [ComponentsConfiguration ] = [Y Cb Cr .] + [ShutterSpeedValue ] = 483328/65536 + [ApertureValue ] = 417792/65536 + [ExposureBiasValue ] = 1.00 eV + [MeteringMode ] = Pattern + [Flash ] = Flash did not fire + [FocalLength ] = 24 mm + [MakerNote ] = @ 0x028E + [UserComment ] = "" + [SubSecTime ] = "80" + [SubSecTimeOriginal ] = "80" + [SubSecTimeDigitized ] = "80" + [FlashPixVersion ] = 01.00 + [ColorSpace ] = sRGB + [ExifImageWidth ] = 4752 + [ExifImageHeight ] = 3168 + [ExifInteroperabilityOffset ] = @ 0x2010 + [FocalPlaneXResolution ] = 4752000/894 + [FocalPlaneYResolution ] = 3168000/593 + [FocalPlaneResolutionUnit ] = Inch + [CustomRendered ] = Normal process + [ExposureMode ] = Auto exposure + [WhiteBalance ] = Auto white balance + [SceneCaptureType ] = Standard + + EXIF MakerIFD @ Absolute 0x000002AC + Makernote decode option not enabled. + + EXIF GPSIFD @ Absolute 0x00002052 + Dir Length = 0x0001 + [GPSVersionID ] = 2.2.0.0 + + EXIF InteropIFD @ Absolute 0x0000202E + Dir Length = 0x0001 + [InteroperabilityVersion ] = 01.00 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00002066 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 1 1 1 1 1 2 3 4 + DQT, Row #1: 1 1 1 1 2 3 4 3 + DQT, Row #2: 1 1 1 1 2 3 4 3 + DQT, Row #3: 1 1 1 2 3 5 5 4 + DQT, Row #4: 1 1 2 3 4 7 6 5 + DQT, Row #5: 1 2 3 4 5 6 7 6 + DQT, Row #6: 3 4 5 5 6 7 7 6 + DQT, Row #7: 4 6 6 6 7 6 6 6 + Approx quality factor = 96.95 (scaling=6.11 variance=1.09) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x000020AB + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 1 1 1 3 6 6 6 6 + DQT, Row #1: 1 1 2 4 6 6 6 6 + DQT, Row #2: 1 2 3 6 6 6 6 6 + DQT, Row #3: 3 4 6 6 6 6 6 6 + DQT, Row #4: 6 6 6 6 6 6 6 6 + DQT, Row #5: 6 6 6 6 6 6 6 6 + DQT, Row #6: 6 6 6 6 6 6 6 6 + DQT, Row #7: 6 6 6 6 6 6 6 6 + Approx quality factor = 96.99 (scaling=6.01 variance=0.24) + +*** Marker: SOF0 (Baseline DCT) (xFFC0) *** + OFFSET: 0x000020F0 + Frame header length = 17 + Precision = 8 + Number of Lines = 3168 + Samples per Line = 4752 + Image Size = 4752 x 3168 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00002103 + Huffman table length = 31 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (005 total): 01 02 03 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 09 + Codes of length 08 bits (001 total): 0A + Codes of length 09 bits (001 total): 0B + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00002124 + Huffman table length = 181 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (003 total): 00 04 11 + Codes of length 05 bits (003 total): 05 12 21 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 06 13 51 61 + Codes of length 08 bits (003 total): 07 22 71 + Codes of length 09 bits (005 total): 14 32 81 91 A1 + Codes of length 10 bits (005 total): 08 23 42 B1 C1 + Codes of length 11 bits (004 total): 15 52 D1 F0 + Codes of length 12 bits (004 total): 24 33 62 72 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (001 total): 82 + Codes of length 16 bits (125 total): 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36 + 37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56 + 57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76 + 77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95 + 96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 + B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA + D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7 + E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000021DB + Huffman table length = 31 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 02 + Codes of length 03 bits (001 total): 03 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (001 total): 05 + Codes of length 06 bits (001 total): 06 + Codes of length 07 bits (001 total): 07 + Codes of length 08 bits (001 total): 08 + Codes of length 09 bits (001 total): 09 + Codes of length 10 bits (001 total): 0A + Codes of length 11 bits (001 total): 0B + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 012 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000021FC + Huffman table length = 181 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (002 total): 03 11 + Codes of length 05 bits (004 total): 04 05 21 31 + Codes of length 06 bits (004 total): 06 12 41 51 + Codes of length 07 bits (003 total): 07 61 71 + Codes of length 08 bits (004 total): 13 22 32 81 + Codes of length 09 bits (007 total): 08 14 42 91 A1 B1 C1 + Codes of length 10 bits (005 total): 09 23 33 52 F0 + Codes of length 11 bits (004 total): 15 62 72 D1 + Codes of length 12 bits (004 total): 0A 16 24 34 + Codes of length 13 bits (000 total): + Codes of length 14 bits (001 total): E1 + Codes of length 15 bits (002 total): 25 F1 + Codes of length 16 bits (119 total): 17 18 19 1A 26 27 28 29 2A 35 36 37 38 39 3A 43 + 44 45 46 47 48 49 4A 53 54 55 56 57 58 59 5A 63 + 64 65 66 67 68 69 6A 73 74 75 76 77 78 79 7A 82 + 83 84 85 86 87 88 89 8A 92 93 94 95 96 97 98 99 + 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3 B4 B5 B6 B7 + B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA D2 D3 D4 D5 + D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3 + F4 F5 F6 F7 F8 F9 FA + Total number of codes: 162 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000022B3 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),1(AC) + Component[3]: selector=0x03, table=1(DC),1(AC) + Spectral selection = 0 .. 63 + Successive approximation = 0x00 + + +*** Decoding SCAN Data *** + OFFSET: 0x000022C1 + Scan Decode Mode: No IDCT (DC only) + NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] + + Scan Data encountered marker 0xFFD9 @ 0x0055418B.0 + + Compression stats: + Compression Ratio: 8.10:1 + Bits per pixel: 2.96:1 + + Huffman code histogram stats: + Huffman Table: (Dest ID: 0, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 7852 ( 3%) + # codes of length 03 bits: 194801 ( 83%) + # codes of length 04 bits: 18114 ( 8%) + # codes of length 05 bits: 9703 ( 4%) + # codes of length 06 bits: 3623 ( 2%) + # codes of length 07 bits: 941 ( 0%) + # codes of length 08 bits: 188 ( 0%) + # codes of length 09 bits: 2 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 1, Class: DC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 53609 ( 46%) + # codes of length 03 bits: 36337 ( 31%) + # codes of length 04 bits: 20089 ( 17%) + # codes of length 05 bits: 4404 ( 4%) + # codes of length 06 bits: 2062 ( 2%) + # codes of length 07 bits: 903 ( 1%) + # codes of length 08 bits: 206 ( 0%) + # codes of length 09 bits: 2 ( 0%) + # codes of length 10 bits: 0 ( 0%) + # codes of length 11 bits: 0 ( 0%) + # codes of length 12 bits: 0 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 0 ( 0%) + # codes of length 16 bits: 0 ( 0%) + + Huffman Table: (Dest ID: 0, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 3801677 ( 49%) + # codes of length 03 bits: 1263986 ( 16%) + # codes of length 04 bits: 1288745 ( 17%) + # codes of length 05 bits: 606891 ( 8%) + # codes of length 06 bits: 282047 ( 4%) + # codes of length 07 bits: 273734 ( 4%) + # codes of length 08 bits: 85749 ( 1%) + # codes of length 09 bits: 90483 ( 1%) + # codes of length 10 bits: 39213 ( 1%) + # codes of length 11 bits: 19089 ( 0%) + # codes of length 12 bits: 6439 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 0 ( 0%) + # codes of length 15 bits: 136 ( 0%) + # codes of length 16 bits: 7545 ( 0%) + + Huffman Table: (Dest ID: 1, Class: AC) + # codes of length 01 bits: 0 ( 0%) + # codes of length 02 bits: 309037 ( 51%) + # codes of length 03 bits: 124353 ( 21%) + # codes of length 04 bits: 87742 ( 14%) + # codes of length 05 bits: 43060 ( 7%) + # codes of length 06 bits: 28928 ( 5%) + # codes of length 07 bits: 2442 ( 0%) + # codes of length 08 bits: 8544 ( 1%) + # codes of length 09 bits: 1150 ( 0%) + # codes of length 10 bits: 376 ( 0%) + # codes of length 11 bits: 126 ( 0%) + # codes of length 12 bits: 30 ( 0%) + # codes of length 13 bits: 0 ( 0%) + # codes of length 14 bits: 50 ( 0%) + # codes of length 15 bits: 24 ( 0%) + # codes of length 16 bits: 4 ( 0%) + + YCC clipping in DC: + Y component: [<0= 0] [>255= 0] + Cb component: [<0= 0] [>255= 0] + Cr component: [<0= 0] [>255= 0] + + RGB clipping in DC: + R component: [<0= 0] [>255= 0] + G component: [<0= 0] [>255= 0] + B component: [<0= 0] [>255= 0] + + Average Pixel Luminance (Y): + Y=[215] (range: 0..255) + + Brightest Pixel Search: + YCC=[ 1016, -3, 3] RGB=[255,255,253] @ MCU[ 92, 26] + + Finished Decoding SCAN Data + Number of RESTART markers decoded: 0 + Next position in scan buffer: Offset 0x0055418A.7 + + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0055418B + + +*** Searching Compression Signatures *** + + Signature: 010564D93F295ADB889B91604DC82EE1 + Signature (Rotated): 014302FE54745F4DBB58A0D51CDC66BD + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: OK [Canon] [Canon EOS 500D] + EXIF Makernotes: OK + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[Leica Camera AG ] [M8 Digital Camera ] [ ] No + CAM:[Leica Camera AG ] [M8 Digital Camera ] [ ] No + CAM:[Leica Camera AG ] [M8 Digital Camera ] [ ] No + CAM:[NIKON ] [E4500 ] [FINE ] No + CAM:[NIKON ] [E5400 ] [FINE ] No + CAM:[NIKON ] [E775 ] [FINE ] No + CAM:[NIKON ] [E775 ] [FINE ] No + CAM:[OLYMPUS CORPORATION ] [C8080WZ ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C3040Z ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C40Z,D40Z ] [ ] No + CAM:[Samsung Techwin ] [Digimax V50/a5 ] [ ] No + CAM:[SAMSUNG TECHWIN ] [Pro 815 ] [ ] No + CAM:[SAMSUNG TECHWIN ] [VLUU NV 7, NV 7 ] [ ] No + CAM:[SAMSUNG TECHWIN ] [VLUU NV10, NV10 ] [ ] No + CAM:[SONY ] [CYBERSHOT ] [ ] No + CAM:[SONY ] [DSC-H1 ] [ ] No + CAM:[SONY ] [DSC-H2 ] [ ] No + CAM:[SONY ] [DSC-H5 ] [ ] No + CAM:[SONY ] [DSC-H7 ] [ ] No + CAM:[SONY ] [DSC-H9 ] [ ] No + CAM:[SONY ] [DSC-L1 ] [ ] No + CAM:[SONY ] [DSC-N2 ] [ ] No + CAM:[SONY ] [DSC-P150 ] [ ] No + CAM:[SONY ] [DSC-P200 ] [ ] No + CAM:[SONY ] [DSC-R1 ] [ ] No + CAM:[SONY ] [DSC-S90 ] [ ] No + CAM:[SONY ] [DSC-V1 ] [ ] No + CAM:[SONY ] [DSC-V3 ] [ ] No + CAM:[SONY ] [DSC-W35 ] [ ] No + CAM:[SONY ] [DSC-W7 ] [ ] No + CAM:[SONY ] [SONY ] [ ] No + SW :[IJG Library ] [097 ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [097 ] + SW :[IrfanView ] [097 ] + SW :[idImager ] [097 ] + SW :[FastStone Image Viewer ] [097 ] + SW :[NeatImage ] [097 ] + SW :[Paint.NET ] [097 ] + SW :[Photomatix ] [097 ] + SW :[XnView ] [097 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 4 - Uncertain if processed or original + While the EXIF fields indicate original, no compression signatures + in the current database were found matching this make/model + + Appears to be new signature for known camera. + If the camera/software doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/issues/issue750-exif-load.jpg b/tests/Images/Input/Jpg/issues/issue750-exif-load.jpg new file mode 100644 index 0000000000..4753cd526e Binary files /dev/null and b/tests/Images/Input/Jpg/issues/issue750-exif-load.jpg differ diff --git a/tests/Images/Input/Jpg/issues/issue750-exif-tranform.jpg b/tests/Images/Input/Jpg/issues/issue750-exif-tranform.jpg new file mode 100644 index 0000000000..5a906c4375 Binary files /dev/null and b/tests/Images/Input/Jpg/issues/issue750-exif-tranform.jpg differ diff --git a/tests/Images/Input/Jpg/progressive/JpegSnoopReports/BadEofProgressive.jpg.txt b/tests/Images/Input/Jpg/progressive/JpegSnoopReports/BadEofProgressive.jpg.txt new file mode 100644 index 0000000000..b6a1fe8094 --- /dev/null +++ b/tests/Images/Input/Jpg/progressive/JpegSnoopReports/BadEofProgressive.jpg.txt @@ -0,0 +1,452 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\BadEofProgressive.jpg] + Filesize: [67503] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.2] + density = 1 x 1 (aspect ratio) + thumbnail = 0 x 0 + +*** Marker: APP13 (xFFED) *** + OFFSET: 0x00000014 + Length = 124 + Identifier = [Photoshop 3.0] + 8BIM: [0x0404] Name="" Len=[0x005F] DefinedName="IPTC-NAA record" + IPTC [002:040] Special Instructions = "FBMD2300098903000068210000c735000008450000e88e0000fab00000c6cd000002f80000191a0100653f0100" + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x00000092 + Length = 540 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 1 of 1 + Profile Size : 524 bytes + Preferred CMM Type : 'lcms' (0x6C636D73) + Profile Version : 0.2.1.0 (0x02100000) + Profile Device/Class : Display Device profile ('mntr' (0x6D6E7472)) + Data Colour Space : rgbData ('RGB ' (0x52474220)) + Profile connection space (PCS) : 'XYZ ' (0x58595A20) + Profile creation date : 2012-01-25 03:41:57 + Profile file signature : 'acsp' (0x61637370) + Primary platform : Apple Computer, Inc. ('APPL' (0x4150504C)) + Profile flags : 0x00000000 + Profile flags > Profile not embedded + Profile flags > Profile can't be used independently of embedded + Device Manufacturer : '....' (0x00000000) + Device Model : '....' (0x00000000) + Device attributes : 0x00000000_00000000 + Device attributes > Reflective + Device attributes > Glossy + Device attributes > Media polarity = negative + Device attributes > Black & white media + Rendering intent : Perceptual + Profile creator : 'lcms' (0x6C636D73) + Profile ID : 0x00000000_00000000_00000000_00000000 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x000002B0 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 6 4 4 6 10 16 20 24 + DQT, Row #1: 5 5 6 8 10 23 24 22 + DQT, Row #2: 6 5 6 10 16 23 28 22 + DQT, Row #3: 6 7 9 12 20 35 32 25 + DQT, Row #4: 7 9 15 22 27 44 41 31 + DQT, Row #5: 10 14 22 26 32 42 45 37 + DQT, Row #6: 20 26 31 35 41 48 48 40 + DQT, Row #7: 29 37 38 39 45 40 41 40 + Approx quality factor = 79.94 (scaling=40.12 variance=1.43) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x000002F5 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 7 7 10 19 40 40 40 40 + DQT, Row #1: 7 8 10 26 40 40 40 40 + DQT, Row #2: 10 10 22 40 40 40 40 40 + DQT, Row #3: 19 26 40 40 40 40 40 40 + DQT, Row #4: 40 40 40 40 40 40 40 40 + DQT, Row #5: 40 40 40 40 40 40 40 40 + DQT, Row #6: 40 40 40 40 40 40 40 40 + DQT, Row #7: 40 40 40 40 40 40 40 40 + Approx quality factor = 79.87 (scaling=40.26 variance=0.36) + +*** Marker: SOF2 (Progressive DCT, Huffman) (xFFC2) *** + OFFSET: 0x0000033A + Frame header length = 17 + Precision = 8 + Number of Lines = 640 + Samples per Line = 640 + Image Size = 640 x 640 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x00, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x01, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000034D + Huffman table length = 29 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (000 total): + Codes of length 03 bits (007 total): 01 02 03 04 05 06 07 + Codes of length 04 bits (001 total): 00 + Codes of length 05 bits (001 total): 08 + Codes of length 06 bits (001 total): 09 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 010 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000036C + Huffman table length = 27 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (003 total): 00 03 04 + Codes of length 04 bits (001 total): 05 + Codes of length 05 bits (001 total): 06 + Codes of length 06 bits (001 total): 07 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 008 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000389 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x00, table=0(DC),0(AC) + Component[2]: selector=0x01, table=1(DC),0(AC) + Component[3]: selector=0x02, table=1(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00002127 + Huffman table length = 63 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (004 total): 00 02 03 11 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (003 total): 12 21 31 + Codes of length 06 bits (002 total): 05 41 + Codes of length 07 bits (003 total): 13 22 51 + Codes of length 08 bits (004 total): 10 32 61 A1 + Codes of length 09 bits (007 total): 14 20 71 81 91 B1 F0 + Codes of length 10 bits (007 total): 06 23 42 52 C1 D1 E1 + Codes of length 11 bits (002 total): 15 30 + Codes of length 12 bits (005 total): 16 24 33 34 62 + Codes of length 13 bits (005 total): 25 40 50 72 F1 + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 044 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00002168 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00003588 + Huffman table length = 61 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (002 total): 02 11 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (003 total): 04 12 21 + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (004 total): 05 10 13 51 + Codes of length 08 bits (003 total): 14 20 22 + Codes of length 09 bits (005 total): 32 42 61 71 91 + Codes of length 10 bits (007 total): 15 23 33 81 A1 B1 C1 + Codes of length 11 bits (003 total): 06 30 D1 + Codes of length 12 bits (002 total): E1 F0 + Codes of length 13 bits (007 total): 16 24 34 40 43 50 52 + Codes of length 14 bits (001 total): 62 + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 042 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000035C7 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x00 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000044BF + Huffman table length = 71 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (003 total): 00 02 03 + Codes of length 04 bits (002 total): 04 11 + Codes of length 05 bits (003 total): 12 21 31 + Codes of length 06 bits (004 total): 13 22 41 51 + Codes of length 07 bits (007 total): 05 10 20 32 61 71 81 + Codes of length 08 bits (005 total): 14 23 42 52 91 + Codes of length 09 bits (005 total): 30 33 62 72 A1 + Codes of length 10 bits (007 total): 15 34 43 53 82 92 B1 + Codes of length 11 bits (003 total): 06 24 40 + Codes of length 12 bits (004 total): 44 A2 C1 D1 + Codes of length 13 bits (001 total): E1 + Codes of length 14 bits (004 total): 25 35 50 F0 + Codes of length 15 bits (003 total): 54 63 73 + Codes of length 16 bits (000 total): + Total number of codes: 052 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00004508 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x00, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00008EC5 + Huffman table length = 33 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (000 total): + Codes of length 05 bits (003 total): 10 21 31 + Codes of length 06 bits (001 total): 41 + Codes of length 07 bits (001 total): 51 + Codes of length 08 bits (001 total): 20 + Codes of length 09 bits (001 total): 61 + Codes of length 10 bits (001 total): 71 + Codes of length 11 bits (001 total): 30 + Codes of length 12 bits (001 total): 81 + Codes of length 13 bits (001 total): 40 + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 014 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00008EE8 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x00, table=0(DC),0(AC) + Spectral selection = 1 .. 10 + Successive approximation = 0x21 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000B0CE + Huffman table length = 42 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (002 total): 21 31 + Codes of length 05 bits (006 total): 10 41 51 61 71 91 + Codes of length 06 bits (002 total): 20 81 + Codes of length 07 bits (001 total): 30 + Codes of length 08 bits (005 total): 40 A1 B1 C1 F0 + Codes of length 09 bits (001 total): D1 + Codes of length 10 bits (001 total): F1 + Codes of length 11 bits (001 total): E1 + Codes of length 12 bits (001 total): 50 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 023 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000B0FA + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x00, table=0(DC),0(AC) + Spectral selection = 11 .. 63 + Successive approximation = 0x21 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000CDA4 + Huffman table length = 32 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (001 total): 21 + Codes of length 05 bits (001 total): 31 + Codes of length 06 bits (001 total): 10 + Codes of length 07 bits (001 total): 41 + Codes of length 08 bits (001 total): 51 + Codes of length 09 bits (001 total): 20 + Codes of length 10 bits (001 total): 61 + Codes of length 11 bits (001 total): 71 + Codes of length 12 bits (001 total): 81 + Codes of length 13 bits (001 total): 91 + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 013 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000CDC6 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x00, table=0(DC),0(AC) + Spectral selection = 1 .. 10 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000F7DF + Huffman table length = 33 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (002 total): 21 31 + Codes of length 05 bits (002 total): 10 41 + Codes of length 06 bits (003 total): 51 61 71 + Codes of length 07 bits (001 total): 20 + Codes of length 08 bits (001 total): 81 + Codes of length 09 bits (001 total): 30 + Codes of length 10 bits (001 total): 40 + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 014 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000F802 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x00, table=0(DC),0(AC) + Spectral selection = 11 .. 19 + Successive approximation = 0x10 +ERROR: Ran out of buffer before EOI during phase 1 of Scan decode @ 0x000107B0 + + NOTE: Scan parsing doesn't support this SOF mode. + +ERROR: Early EOF - file may be missing EOI + +*** Searching Compression Signatures *** + + Signature: 01DC499064BA9264D591FDE9071DFD89 + Signature (Rotated): 0175BAF3251040E0EFB2930B73328E7F + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C2000Z ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C40Z,D40Z ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C700UZ ] [ ] No + CAM:[SONY ] [DSC-H9 ] [ ] No + SW :[Apple ImageIO.framework ] [050 (Normal) ] + SW :[IJG Library ] [080 ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [080 ] + SW :[IrfanView ] [080 ] + SW :[idImager ] [080 ] + SW :[FastStone Image Viewer ] [080 ] + SW :[NeatImage ] [080 ] + SW :[Paint.NET ] [080 ] + SW :[Photomatix ] [080 ] + SW :[XnView ] [080 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/progressive/JpegSnoopReports/ExifUndefType.jpg.txt b/tests/Images/Input/Jpg/progressive/JpegSnoopReports/ExifUndefType.jpg.txt new file mode 100644 index 0000000000..397343c5e5 --- /dev/null +++ b/tests/Images/Input/Jpg/progressive/JpegSnoopReports/ExifUndefType.jpg.txt @@ -0,0 +1,535 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\ExifUndefType.jpg] + Filesize: [6582] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 72 x 72 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: APP1 (xFFE1) *** + OFFSET: 0x00000014 + Length = 804 + Identifier = [Exif] + Identifier TIFF = 0x[49492A00 86020000] + Endian = Intel (little) + TAG Mark x002A = 0x002A + + EXIF IFD0 @ Absolute 0x000002A4 + Dir Length = 0x000C + [Orientation ] = 1 = Row 0: top, Col 0: left + [XResolution ] = 72/1 + [YResolution ] = 72/1 + [ResolutionUnit ] = Inch + [Software ] = "Adobe Photoshop CS4 Windows" + [DateTime ] = "2014:03:28 16:44:10" + [WhitePoint ] = 0/1000000, 0/1000000 + [PrimChromaticities ] = 0/1000000, 0/1000000, 0/1000000, 0/1000000, 0/1000000, 0/1000000 + [YCbCrCoefficients ] = 0/1000000, 0/1000000, 0/1000000 + [YCbCrPositioning ] = 0 + [ReferenceBlackWhite ] = 0/1000000, 0/1000000, 0/1000000, 0/1000000, 0/1000000, 0/1000000 + [ExifOffset ] = @ 0x0138 + Offset to Next IFD = 0x00000000 + + EXIF SubIFD @ Absolute 0x00000156 + Dir Length = 0x001B + [ExposureTime ] = 0/1000000 s + [FNumber ] = F0.0 + [ExposureProgram ] = Not defined + [ISOSpeedRatings ] = 0, 0 + [ExifVersion ] = 12.20 + [CompressedBitsPerPixel ] = 0/1000000 + [ShutterSpeedValue ] = 0/1000000 + [ApertureValue ] = 0/1000000 + [BrightnessValue ] = 0/1000000 + [ExposureBiasValue ] = 0.00 eV + [MaxApertureValue ] = 0/1000000 + [SubjectDistance ] = 0/1000000 + [MeteringMode ] = Unknown + [LightSource ] = unknown + [Flash ] = Flash did not fire + [FocalLength ] = 0 mm + [FlashPixVersion ] = + [ColorSpace ] = sRGB + [ExifImageWidth ] = 850 + [ExifImageHeight ] = 638 + [FocalPlaneXResolution ] = 0/1000000 + [FocalPlaneYResolution ] = 0/1000000 + [FocalPlaneResolutionUnit ] = 0 + [ExposureIndex ] = 0/1000000 + [SensingMethod ] = 0 + [FileSource ] = 0 + [SceneType ] = 0 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x0000033A + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 3 2 2 3 5 8 10 12 + DQT, Row #1: 2 2 3 4 5 12 12 11 + DQT, Row #2: 3 3 3 5 8 11 14 11 + DQT, Row #3: 3 3 4 6 10 17 16 12 + DQT, Row #4: 4 4 7 11 14 22 21 15 + DQT, Row #5: 5 7 11 13 16 21 23 18 + DQT, Row #6: 10 13 16 17 21 24 24 20 + DQT, Row #7: 14 18 19 20 22 20 21 20 + Approx quality factor = 90.06 (scaling=19.88 variance=1.14) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x0000037F + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 3 4 5 9 20 20 20 20 + DQT, Row #1: 4 4 5 13 20 20 20 20 + DQT, Row #2: 5 5 11 20 20 20 20 20 + DQT, Row #3: 9 13 20 20 20 20 20 20 + DQT, Row #4: 20 20 20 20 20 20 20 20 + DQT, Row #5: 20 20 20 20 20 20 20 20 + DQT, Row #6: 20 20 20 20 20 20 20 20 + DQT, Row #7: 20 20 20 20 20 20 20 20 + Approx quality factor = 89.93 (scaling=20.14 variance=0.34) + +*** Marker: SOF2 (Progressive DCT, Huffman) (xFFC2) *** + OFFSET: 0x000003C4 + Frame header length = 17 + Precision = 8 + Number of Lines = 165 + Samples per Line = 220 + Image Size = 220 x 165 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000003D7 + Huffman table length = 28 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 03 07 + Codes of length 04 bits (003 total): 02 04 05 + Codes of length 05 bits (001 total): 06 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (001 total): 01 + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000003F5 + Huffman table length = 22 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (000 total): + Codes of length 05 bits (000 total): + Codes of length 06 bits (000 total): + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 003 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000040D + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),0(AC) + Component[3]: selector=0x03, table=1(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000581 + Huffman table length = 41 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (000 total): + Codes of length 03 bits (006 total): 00 01 02 03 04 05 + Codes of length 04 bits (001 total): 06 + Codes of length 05 bits (003 total): 11 15 16 + Codes of length 06 bits (004 total): 12 13 14 21 + Codes of length 07 bits (002 total): 07 10 + Codes of length 08 bits (002 total): 22 40 + Codes of length 09 bits (003 total): 17 30 60 + Codes of length 10 bits (001 total): 24 + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 022 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000005AC + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 5 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000883 + Huffman table length = 27 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 11 + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (000 total): + Codes of length 04 bits (002 total): 01 10 + Codes of length 05 bits (003 total): 20 30 40 + Codes of length 06 bits (001 total): 50 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 008 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000008A0 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000008BF + Huffman table length = 28 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (001 total): 11 + Codes of length 03 bits (001 total): 01 + Codes of length 04 bits (000 total): + Codes of length 05 bits (003 total): 10 21 30 + Codes of length 06 bits (000 total): + Codes of length 07 bits (003 total): 20 40 50 + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000008DD + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000905 + Huffman table length = 65 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (000 total): + Codes of length 03 bits (004 total): 00 01 02 03 + Codes of length 04 bits (002 total): 04 11 + Codes of length 05 bits (004 total): 12 21 31 34 + Codes of length 06 bits (008 total): 13 32 33 35 91 92 93 D2 + Codes of length 07 bits (009 total): 05 10 14 22 41 51 71 A1 D1 + Codes of length 08 bits (010 total): 20 23 40 42 52 61 81 A2 A3 B1 + Codes of length 09 bits (007 total): 15 24 60 62 72 E1 E2 + Codes of length 10 bits (001 total): 30 + Codes of length 11 bits (001 total): B2 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 046 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000948 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 6 .. 63 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000E49 + Huffman table length = 42 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (001 total): 21 + Codes of length 05 bits (002 total): 31 41 + Codes of length 06 bits (004 total): 51 61 71 D1 + Codes of length 07 bits (006 total): 81 91 A1 C1 F0 F1 + Codes of length 08 bits (002 total): 40 B1 + Codes of length 09 bits (003 total): 10 20 60 + Codes of length 10 bits (001 total): 30 + Codes of length 11 bits (001 total): E1 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 023 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000E75 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x21 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00001266 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=0(DC),0(AC) + Component[3]: selector=0x03, table=0(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000012E8 + Huffman table length = 28 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 11 + Codes of length 03 bits (001 total): 10 + Codes of length 04 bits (000 total): + Codes of length 05 bits (002 total): 30 51 + Codes of length 06 bits (003 total): 21 40 50 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00001306 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000133E + Huffman table length = 30 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 11 + Codes of length 03 bits (000 total): + Codes of length 04 bits (003 total): 10 30 51 + Codes of length 05 bits (000 total): + Codes of length 06 bits (002 total): 20 61 + Codes of length 07 bits (003 total): 21 40 50 + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 011 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000135E + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000139C + Huffman table length = 42 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (001 total): 11 + Codes of length 03 bits (000 total): + Codes of length 04 bits (002 total): 00 21 + Codes of length 05 bits (001 total): 31 + Codes of length 06 bits (003 total): 41 51 61 + Codes of length 07 bits (003 total): 71 81 B1 + Codes of length 08 bits (003 total): 91 A1 D1 + Codes of length 09 bits (004 total): 10 40 E1 F0 + Codes of length 10 bits (003 total): 20 C1 F1 + Codes of length 11 bits (001 total): 60 + Codes of length 12 bits (001 total): 30 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 023 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000013C8 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x000019B4 + + +*** Searching Compression Signatures *** + + Signature: 013BA18D5561625796E986FDBC09F846 + Signature (Rotated): 01AC57E12793DFA7C46C704625C5AF0F + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: OK [Adobe Photoshop CS4 Windows] + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[??? ] [Treo 680 ] [ ] Yes + CAM:[Canon ] [Canon PowerShot Pro1 ] [fine ] No + CAM:[NIKON ] [E2500 ] [FINE ] No + CAM:[NIKON ] [E3100 ] [FINE ] No + CAM:[NIKON ] [E4500 ] [FINE ] No + CAM:[NIKON ] [E5000 ] [FINE ] No + CAM:[NIKON ] [E5700 ] [FINE ] No + CAM:[NIKON ] [E775 ] [FINE ] No + CAM:[NIKON ] [E885 ] [FINE ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C3040Z ] [ ] No + CAM:[PENTAX ] [PENTAX Optio 550 ] [ ] No + CAM:[Research In Motion ] [BlackBerry 9530 ] [Superfine ] Yes + CAM:[SEIKO EPSON CORP. ] [PhotoPC 3000Z ] [ ] No + CAM:[SONY ] [DSC-H7 ] [ ] No + CAM:[SONY ] [DSC-H9 ] [ ] No + CAM:[SONY ] [DSC-S90 ] [ ] No + CAM:[SONY ] [DSC-W1 ] [ ] No + CAM:[SONY ] [SONY ] [ ] No + SW :[ACDSee ] [ ] + SW :[FixFoto ] [fine ] + SW :[IJG Library ] [090 ] + SW :[ZoomBrowser EX ] [high ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [090 ] + SW :[IrfanView ] [090 ] + SW :[idImager ] [090 ] + SW :[FastStone Image Viewer ] [090 ] + SW :[NeatImage ] [090 ] + SW :[Paint.NET ] [090 ] + SW :[Photomatix ] [090 ] + SW :[XnView ] [090 ] + + NOTE: EXIF Software field recognized as from editor + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + Appears to be new signature for known software. + If the camera/software doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/progressive/JpegSnoopReports/Festzug.jpg.txt b/tests/Images/Input/Jpg/progressive/JpegSnoopReports/Festzug.jpg.txt new file mode 100644 index 0000000000..445e80a7e7 --- /dev/null +++ b/tests/Images/Input/Jpg/progressive/JpegSnoopReports/Festzug.jpg.txt @@ -0,0 +1,459 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\Festzug.jpg] + Filesize: [49977] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 229 x 229 DPI (dots per inch) + thumbnail = 0 x 0 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000014 + Table length = 131 + ---- + Precision=16 bits + Destination ID=0 (Luminance) + DQT, Row #0: 53 37 33 53 80 133 170 203 + DQT, Row #1: 40 40 47 63 87 193 200 183 + DQT, Row #2: 47 43 53 80 133 190 230 186 + DQT, Row #3: 47 57 73 97 170 290 266 206 + DQT, Row #4: 60 73 123 186 226 363 343 256 + DQT, Row #5: 80 117 183 213 270 346 376 306 + DQT, Row #6: 163 213 260 290 343 403 400 336 + DQT, Row #7: 240 306 316 326 373 333 343 330 + Approx quality factor = 15.01 (scaling=333.00 variance=1.25) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000099 + Table length = 131 + ---- + Precision=16 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 57 60 80 157 330 330 330 330 + DQT, Row #1: 60 70 87 220 330 330 330 330 + DQT, Row #2: 80 87 186 330 330 330 330 330 + DQT, Row #3: 157 220 330 330 330 330 330 330 + DQT, Row #4: 330 330 330 330 330 330 330 330 + DQT, Row #5: 330 330 330 330 330 330 330 330 + DQT, Row #6: 330 330 330 330 330 330 330 330 + DQT, Row #7: 330 330 330 330 330 330 330 330 + Approx quality factor = 15.00 (scaling=333.41 variance=0.14) + +*** Marker: SOF2 (Progressive DCT, Huffman) (xFFC2) *** + OFFSET: 0x0000011E + Frame header length = 17 + Precision = 8 + Number of Lines = 1071 + Samples per Line = 1443 + Image Size = 1443 x 1071 + Raw Image Orientation = Landscape + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000131 + Huffman table length = 25 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (001 total): 03 + Codes of length 05 bits (001 total): 04 + Codes of length 06 bits (001 total): 05 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 006 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000014C + Huffman table length = 22 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (001 total): 00 + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (000 total): + Codes of length 05 bits (000 total): + Codes of length 06 bits (000 total): + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 003 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000164 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),0(AC) + Component[3]: selector=0x03, table=1(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000028E0 + Huffman table length = 38 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (004 total): 02 10 20 30 + Codes of length 05 bits (003 total): 12 31 40 + Codes of length 06 bits (000 total): + Codes of length 07 bits (003 total): 21 41 50 + Codes of length 08 bits (001 total): 60 + Codes of length 09 bits (001 total): 03 + Codes of length 10 bits (000 total): + Codes of length 11 bits (003 total): 22 32 42 + Codes of length 12 bits (001 total): 13 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 019 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00002908 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 5 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00003D97 + Huffman table length = 29 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (003 total): 00 10 11 + Codes of length 04 bits (001 total): 20 + Codes of length 05 bits (000 total): + Codes of length 06 bits (002 total): 30 60 + Codes of length 07 bits (003 total): 80 90 C0 + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 010 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00003DB6 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00003DE1 + Huffman table length = 28 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 10 11 + Codes of length 04 bits (003 total): 00 20 60 + Codes of length 05 bits (000 total): + Codes of length 06 bits (003 total): 80 90 C0 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00003DFF + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00003E21 + Huffman table length = 23 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 21 + Codes of length 02 bits (001 total): D0 + Codes of length 03 bits (001 total): A0 + Codes of length 04 bits (001 total): B0 + Codes of length 05 bits (000 total): + Codes of length 06 bits (000 total): + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 004 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00003E3A + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 6 .. 63 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00003E4C + Huffman table length = 36 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (002 total): 10 11 + Codes of length 04 bits (002 total): 21 31 + Codes of length 05 bits (003 total): 20 30 41 + Codes of length 06 bits (000 total): + Codes of length 07 bits (003 total): 40 50 51 + Codes of length 08 bits (001 total): 61 + Codes of length 09 bits (001 total): 60 + Codes of length 10 bits (001 total): 71 + Codes of length 11 bits (001 total): 81 + Codes of length 12 bits (001 total): B1 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 017 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00003E72 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x21 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00006325 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=0(DC),0(AC) + Component[3]: selector=0x03, table=0(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00007512 + Huffman table length = 30 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 11 + Codes of length 03 bits (000 total): + Codes of length 04 bits (003 total): 10 21 60 + Codes of length 05 bits (001 total): 31 + Codes of length 06 bits (000 total): + Codes of length 07 bits (003 total): 20 41 80 + Codes of length 08 bits (001 total): C0 + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 011 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00007532 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000075CE + Huffman table length = 30 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 11 + Codes of length 03 bits (001 total): 10 + Codes of length 04 bits (000 total): + Codes of length 05 bits (003 total): 21 31 60 + Codes of length 06 bits (001 total): 41 + Codes of length 07 bits (000 total): + Codes of length 08 bits (003 total): 20 80 C0 + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 011 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000075EE + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00007676 + Huffman table length = 43 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (000 total): + Codes of length 05 bits (002 total): 21 31 + Codes of length 06 bits (002 total): 10 41 + Codes of length 07 bits (002 total): 20 51 + Codes of length 08 bits (002 total): 30 61 + Codes of length 09 bits (003 total): 40 50 71 + Codes of length 10 bits (000 total): + Codes of length 11 bits (003 total): 60 81 91 + Codes of length 12 bits (001 total): A1 + Codes of length 13 bits (001 total): B1 + Codes of length 14 bits (000 total): + Codes of length 15 bits (002 total): C1 D1 + Codes of length 16 bits (003 total): E1 F0 F1 + Total number of codes: 024 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000076A3 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0000C337 + + +*** Searching Compression Signatures *** + + Signature: 0105A3D95D2D36DE9351313E30D8E945 + Signature (Rotated): 013C3A43642D2E8325A76C3818B3C324 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + SW :[IJG Library ] [015 ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [015 ] + SW :[IrfanView ] [015 ] + SW :[idImager ] [015 ] + SW :[FastStone Image Viewer ] [015 ] + SW :[NeatImage ] [015 ] + SW :[Paint.NET ] [015 ] + SW :[Photomatix ] [015 ] + SW :[XnView ] [015 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/progressive/JpegSnoopReports/fb.jpg.txt b/tests/Images/Input/Jpg/progressive/JpegSnoopReports/fb.jpg.txt new file mode 100644 index 0000000000..6f20fc1ee8 --- /dev/null +++ b/tests/Images/Input/Jpg/progressive/JpegSnoopReports/fb.jpg.txt @@ -0,0 +1,525 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\fb.jpg] + Filesize: [15787] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.2] + density = 1 x 1 (aspect ratio) + thumbnail = 0 x 0 + +*** Marker: COM (Comment) (xFFFE) *** + OFFSET: 0x00000014 + Comment length = 4 + Comment=*. + +*** Marker: APP2 (xFFE2) *** + OFFSET: 0x0000001A + Length = 540 + Identifier = [ICC_PROFILE] + ICC Profile: + Marker Number = 1 of 1 + Profile Size : 524 bytes + Preferred CMM Type : 'lcms' (0x6C636D73) + Profile Version : 0.2.1.0 (0x02100000) + Profile Device/Class : Display Device profile ('mntr' (0x6D6E7472)) + Data Colour Space : rgbData ('RGB ' (0x52474220)) + Profile connection space (PCS) : 'XYZ ' (0x58595A20) + Profile creation date : 2012-01-25 03:41:57 + Profile file signature : 'acsp' (0x61637370) + Primary platform : Apple Computer, Inc. ('APPL' (0x4150504C)) + Profile flags : 0x00000000 + Profile flags > Profile not embedded + Profile flags > Profile can't be used independently of embedded + Device Manufacturer : '....' (0x00000000) + Device Model : '....' (0x00000000) + Device attributes : 0x00000000_00000000 + Device attributes > Reflective + Device attributes > Glossy + Device attributes > Media polarity = negative + Device attributes > Black & white media + Rendering intent : Perceptual + Profile creator : 'lcms' (0x6C636D73) + Profile ID : 0x00000000_00000000_00000000_00000000 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000238 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 8 6 5 8 12 20 26 31 + DQT, Row #1: 6 6 7 10 13 29 30 28 + DQT, Row #2: 7 7 8 12 20 29 35 28 + DQT, Row #3: 7 9 11 15 26 44 40 31 + DQT, Row #4: 9 11 19 28 34 55 52 39 + DQT, Row #5: 12 18 28 32 41 52 57 46 + DQT, Row #6: 25 32 39 44 52 61 60 51 + DQT, Row #7: 36 46 48 49 56 50 52 50 + Approx quality factor = 74.75 (scaling=50.51 variance=0.81) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x0000027D + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 9 9 12 24 50 50 50 50 + DQT, Row #1: 9 11 13 33 50 50 50 50 + DQT, Row #2: 12 13 28 50 50 50 50 50 + DQT, Row #3: 24 33 50 50 50 50 50 50 + DQT, Row #4: 50 50 50 50 50 50 50 50 + DQT, Row #5: 50 50 50 50 50 50 50 50 + DQT, Row #6: 50 50 50 50 50 50 50 50 + DQT, Row #7: 50 50 50 50 50 50 50 50 + Approx quality factor = 74.74 (scaling=50.52 variance=0.19) + +*** Marker: SOF2 (Progressive DCT, Huffman) (xFFC2) *** + OFFSET: 0x000002C2 + Frame header length = 17 + Precision = 8 + Number of Lines = 336 + Samples per Line = 276 + Image Size = 276 x 336 + Raw Image Orientation = Portrait + Number of Img components = 3 + Component[1]: ID=0x00, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x01, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000002D5 + Huffman table length = 27 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 04 + Codes of length 03 bits (005 total): 00 02 03 05 06 + Codes of length 04 bits (001 total): 01 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (000 total): + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 008 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000002F2 + Huffman table length = 24 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 03 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (000 total): + Codes of length 06 bits (000 total): + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 005 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000030C + Huffman table length = 24 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (003 total): 00 01 03 + Codes of length 03 bits (001 total): 02 + Codes of length 04 bits (001 total): 04 + Codes of length 05 bits (000 total): + Codes of length 06 bits (000 total): + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 005 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000326 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x00, table=0(DC),0(AC) + Component[2]: selector=0x01, table=1(DC),1(AC) + Component[3]: selector=0x02, table=1(DC),1(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000935 + Huffman table length = 43 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 02 03 + Codes of length 03 bits (002 total): 01 04 + Codes of length 04 bits (001 total): 00 + Codes of length 05 bits (002 total): 11 12 + Codes of length 06 bits (006 total): 05 10 13 20 31 32 + Codes of length 07 bits (002 total): 21 33 + Codes of length 08 bits (002 total): 14 22 + Codes of length 09 bits (002 total): 34 41 + Codes of length 10 bits (003 total): 15 23 30 + Codes of length 11 bits (001 total): 24 + Codes of length 12 bits (001 total): 42 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 024 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000962 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x00, table=0(DC),0(AC) + Spectral selection = 1 .. 5 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000012EE + Huffman table length = 39 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (002 total): 02 11 + Codes of length 04 bits (001 total): 10 + Codes of length 05 bits (004 total): 03 20 21 31 + Codes of length 06 bits (001 total): 12 + Codes of length 07 bits (004 total): 13 30 41 51 + Codes of length 08 bits (001 total): 22 + Codes of length 09 bits (005 total): 32 40 42 50 61 + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 020 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00001317 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=1(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000142A + Huffman table length = 36 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (002 total): 02 11 + Codes of length 04 bits (001 total): 10 + Codes of length 05 bits (004 total): 03 20 21 31 + Codes of length 06 bits (002 total): 12 30 + Codes of length 07 bits (002 total): 13 41 + Codes of length 08 bits (003 total): 22 40 51 + Codes of length 09 bits (001 total): 50 + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 017 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00001450 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=1(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x0000155A + Huffman table length = 51 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (002 total): 00 02 + Codes of length 04 bits (003 total): 03 10 11 + Codes of length 05 bits (006 total): 12 20 21 31 41 51 + Codes of length 06 bits (005 total): 22 32 61 71 81 + Codes of length 07 bits (002 total): 13 30 + Codes of length 08 bits (005 total): 04 33 42 72 91 + Codes of length 09 bits (004 total): 14 52 62 A1 + Codes of length 10 bits (003 total): 23 82 B1 + Codes of length 11 bits (001 total): 43 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 032 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000158F + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x00, table=0(DC),0(AC) + Spectral selection = 6 .. 63 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00001D3E + Huffman table length = 40 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (002 total): 11 21 + Codes of length 04 bits (001 total): 31 + Codes of length 05 bits (003 total): 10 41 51 + Codes of length 06 bits (004 total): 20 61 71 A1 + Codes of length 07 bits (002 total): 81 91 + Codes of length 08 bits (002 total): B1 F0 + Codes of length 09 bits (003 total): 30 C1 D1 + Codes of length 10 bits (001 total): E1 + Codes of length 11 bits (001 total): F1 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 021 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00001D68 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x00, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x21 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000028B9 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x00, table=0(DC),0(AC) + Component[2]: selector=0x01, table=1(DC),1(AC) + Component[3]: selector=0x02, table=1(DC),1(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000029E4 + Huffman table length = 32 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (000 total): + Codes of length 05 bits (003 total): 10 21 31 + Codes of length 06 bits (000 total): + Codes of length 07 bits (003 total): 20 41 51 + Codes of length 08 bits (001 total): 30 + Codes of length 09 bits (001 total): 61 + Codes of length 10 bits (001 total): 40 + Codes of length 11 bits (001 total): 50 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 013 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00002A06 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=1(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00002B55 + Huffman table length = 32 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (003 total): 10 21 31 + Codes of length 05 bits (001 total): 20 + Codes of length 06 bits (001 total): 41 + Codes of length 07 bits (000 total): + Codes of length 08 bits (003 total): 30 51 61 + Codes of length 09 bits (001 total): 71 + Codes of length 10 bits (001 total): 40 + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 013 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00002B77 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=1(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00002CEA + Huffman table length = 40 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (001 total): 21 + Codes of length 05 bits (003 total): 31 41 51 + Codes of length 06 bits (003 total): 10 61 71 + Codes of length 07 bits (004 total): 81 91 A1 B1 + Codes of length 08 bits (003 total): 20 C1 F0 + Codes of length 09 bits (001 total): D1 + Codes of length 10 bits (001 total): E1 + Codes of length 11 bits (001 total): F1 + Codes of length 12 bits (001 total): 30 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 021 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00002D14 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x00, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x00003DA9 + + +*** Searching Compression Signatures *** + + Signature: 0182408A81A4ABF04D4A34A8A5E98C58 + Signature (Rotated): 012D821C6AB210E2A753BE053B8F55D0 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[SONY ] [CYBERSHOT U ] [ ] Yes + SW :[Adobe Photoshop 7.0 ] [Save As 07 ] + SW :[Apple Quicktime ] [0466-0467 ] + SW :[Digital Photo Professiona] [05 ] + SW :[IJG Library ] [075 ] + SW :[MS Paint ] [ ] + SW :[MS Visio ] [ ] + SW :[ZoomBrowser EX ] [low ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [075 ] + SW :[IrfanView ] [075 ] + SW :[idImager ] [075 ] + SW :[FastStone Image Viewer ] [075 ] + SW :[NeatImage ] [075 ] + SW :[Paint.NET ] [075 ] + SW :[Photomatix ] [075 ] + SW :[XnView ] [075 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Jpg/progressive/JpegSnoopReports/progress.jpg.txt b/tests/Images/Input/Jpg/progressive/JpegSnoopReports/progress.jpg.txt new file mode 100644 index 0000000000..f38a115328 --- /dev/null +++ b/tests/Images/Input/Jpg/progressive/JpegSnoopReports/progress.jpg.txt @@ -0,0 +1,468 @@ + +JPEGsnoop 1.8.0 by Calvin Hass + http://www.impulseadventure.com/photo/ + ------------------------------------- + + Filename: [.\progress.jpg] + Filesize: [44884] Bytes + +Start Offset: 0x00000000 +*** Marker: SOI (xFFD8) *** + OFFSET: 0x00000000 + +*** Marker: APP0 (xFFE0) *** + OFFSET: 0x00000002 + Length = 16 + Identifier = [JFIF] + version = [1.1] + density = 1 x 1 (aspect ratio) + thumbnail = 0 x 0 + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000014 + Table length = 67 + ---- + Precision=8 bits + Destination ID=0 (Luminance) + DQT, Row #0: 5 3 3 5 7 12 15 18 + DQT, Row #1: 4 4 4 6 8 17 18 17 + DQT, Row #2: 4 4 5 7 12 17 21 17 + DQT, Row #3: 4 5 7 9 15 26 24 19 + DQT, Row #4: 5 7 11 17 20 33 31 23 + DQT, Row #5: 7 11 17 19 24 31 34 28 + DQT, Row #6: 15 19 23 26 31 36 36 30 + DQT, Row #7: 22 28 29 29 34 30 31 30 + Approx quality factor = 84.93 (scaling=30.13 variance=1.05) + +*** Marker: DQT (xFFDB) *** + Define a Quantization Table. + OFFSET: 0x00000059 + Table length = 67 + ---- + Precision=8 bits + Destination ID=1 (Chrominance) + DQT, Row #0: 5 5 7 14 30 30 30 30 + DQT, Row #1: 5 6 8 20 30 30 30 30 + DQT, Row #2: 7 8 17 30 30 30 30 30 + DQT, Row #3: 14 20 30 30 30 30 30 30 + DQT, Row #4: 30 30 30 30 30 30 30 30 + DQT, Row #5: 30 30 30 30 30 30 30 30 + DQT, Row #6: 30 30 30 30 30 30 30 30 + DQT, Row #7: 30 30 30 30 30 30 30 30 + Approx quality factor = 84.93 (scaling=30.15 variance=0.29) + +*** Marker: SOF2 (Progressive DCT, Huffman) (xFFC2) *** + OFFSET: 0x0000009E + Frame header length = 17 + Precision = 8 + Number of Lines = 486 + Samples per Line = 341 + Image Size = 341 x 486 + Raw Image Orientation = Portrait + Number of Img components = 3 + Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) + Component[2]: ID=0x02, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cb) + Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr) + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000B1 + Huffman table length = 28 + ---- + Destination ID = 0 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 04 + Codes of length 03 bits (005 total): 01 02 03 05 06 + Codes of length 04 bits (001 total): 00 + Codes of length 05 bits (001 total): 07 + Codes of length 06 bits (001 total): 08 + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 009 + + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000000CF + Huffman table length = 26 + ---- + Destination ID = 1 + Class = 0 (DC / Lossless Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 02 03 + Codes of length 03 bits (003 total): 00 01 04 + Codes of length 04 bits (001 total): 05 + Codes of length 05 bits (001 total): 06 + Codes of length 06 bits (000 total): + Codes of length 07 bits (000 total): + Codes of length 08 bits (000 total): + Codes of length 09 bits (000 total): + Codes of length 10 bits (000 total): + Codes of length 11 bits (000 total): + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 007 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000000EB + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=1(DC),0(AC) + Component[3]: selector=0x03, table=1(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00000CCC + Huffman table length = 45 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 01 02 + Codes of length 03 bits (002 total): 00 03 + Codes of length 04 bits (002 total): 04 11 + Codes of length 05 bits (001 total): 12 + Codes of length 06 bits (003 total): 05 13 21 + Codes of length 07 bits (003 total): 10 22 31 + Codes of length 08 bits (004 total): 14 20 32 41 + Codes of length 09 bits (002 total): 06 23 + Codes of length 10 bits (002 total): 30 33 + Codes of length 11 bits (003 total): 15 24 42 + Codes of length 12 bits (001 total): 34 + Codes of length 13 bits (001 total): 43 + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 026 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00000CFB + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 5 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000020DC + Huffman table length = 46 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (002 total): 02 03 + Codes of length 04 bits (001 total): 11 + Codes of length 05 bits (004 total): 04 10 21 31 + Codes of length 06 bits (001 total): 12 + Codes of length 07 bits (003 total): 13 20 41 + Codes of length 08 bits (003 total): 05 22 51 + Codes of length 09 bits (003 total): 23 32 61 + Codes of length 10 bits (005 total): 14 30 33 42 71 + Codes of length 11 bits (000 total): + Codes of length 12 bits (003 total): 15 52 91 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 027 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000210C + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00002604 + Huffman table length = 41 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (002 total): 00 01 + Codes of length 03 bits (002 total): 02 11 + Codes of length 04 bits (002 total): 03 10 + Codes of length 05 bits (002 total): 21 31 + Codes of length 06 bits (002 total): 12 20 + Codes of length 07 bits (001 total): 13 + Codes of length 08 bits (004 total): 04 22 41 61 + Codes of length 09 bits (002 total): 42 51 + Codes of length 10 bits (002 total): 14 32 + Codes of length 11 bits (003 total): 33 71 F0 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 022 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000262F + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x01 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000029A7 + Huffman table length = 61 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (000 total): + Codes of length 02 bits (001 total): 01 + Codes of length 03 bits (003 total): 00 02 11 + Codes of length 04 bits (003 total): 03 12 21 + Codes of length 05 bits (001 total): 31 + Codes of length 06 bits (005 total): 10 22 41 51 61 + Codes of length 07 bits (006 total): 04 13 20 32 71 81 + Codes of length 08 bits (005 total): 23 30 42 52 91 + Codes of length 09 bits (002 total): A1 B1 + Codes of length 10 bits (004 total): 62 72 C1 F0 + Codes of length 11 bits (006 total): 14 33 40 82 D1 E1 + Codes of length 12 bits (001 total): 24 + Codes of length 13 bits (005 total): 05 43 53 73 F1 + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 042 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000029E6 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 6 .. 63 + Successive approximation = 0x02 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00003FF3 + Huffman table length = 39 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (002 total): 21 31 + Codes of length 05 bits (002 total): 41 51 + Codes of length 06 bits (001 total): 61 + Codes of length 07 bits (003 total): 71 81 91 + Codes of length 08 bits (004 total): 10 A1 B1 C1 + Codes of length 09 bits (003 total): D1 E1 F0 + Codes of length 10 bits (001 total): 20 + Codes of length 11 bits (001 total): F1 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 020 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x0000401C + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x21 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00006299 + Scan header length = 12 + Number of img components = 3 + Component[1]: selector=0x01, table=0(DC),0(AC) + Component[2]: selector=0x02, table=0(DC),0(AC) + Component[3]: selector=0x03, table=0(DC),0(AC) + Spectral selection = 0 .. 0 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x000064A9 + Huffman table length = 39 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (000 total): + Codes of length 03 bits (002 total): 00 11 + Codes of length 04 bits (001 total): 21 + Codes of length 05 bits (003 total): 31 41 51 + Codes of length 06 bits (003 total): 10 61 71 + Codes of length 07 bits (004 total): 81 91 D1 F0 + Codes of length 08 bits (003 total): A1 B1 E1 + Codes of length 09 bits (001 total): C1 + Codes of length 10 bits (001 total): F1 + Codes of length 11 bits (001 total): 20 + Codes of length 12 bits (000 total): + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 020 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x000064D2 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x03, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00006ACB + Huffman table length = 38 + ---- + Destination ID = 1 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (001 total): 00 + Codes of length 03 bits (001 total): 11 + Codes of length 04 bits (001 total): 21 + Codes of length 05 bits (000 total): + Codes of length 06 bits (002 total): 31 41 + Codes of length 07 bits (002 total): 10 51 + Codes of length 08 bits (001 total): 61 + Codes of length 09 bits (003 total): 91 D1 F0 + Codes of length 10 bits (005 total): 71 81 A1 B1 C1 + Codes of length 11 bits (001 total): 20 + Codes of length 12 bits (001 total): E1 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 019 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00006AF3 + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x02, table=0(DC),1(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: DHT (Define Huffman Table) (xFFC4) *** + OFFSET: 0x00006F95 + Huffman table length = 38 + ---- + Destination ID = 0 + Class = 1 (AC Table) + Codes of length 01 bits (001 total): 01 + Codes of length 02 bits (001 total): 11 + Codes of length 03 bits (000 total): + Codes of length 04 bits (002 total): 00 21 + Codes of length 05 bits (002 total): 31 41 + Codes of length 06 bits (002 total): 51 61 + Codes of length 07 bits (002 total): 71 81 + Codes of length 08 bits (002 total): 91 A1 + Codes of length 09 bits (002 total): B1 F0 + Codes of length 10 bits (003 total): C1 D1 E1 + Codes of length 11 bits (001 total): F1 + Codes of length 12 bits (001 total): 10 + Codes of length 13 bits (000 total): + Codes of length 14 bits (000 total): + Codes of length 15 bits (000 total): + Codes of length 16 bits (000 total): + Total number of codes: 019 + + +*** Marker: SOS (Start of Scan) (xFFDA) *** + OFFSET: 0x00006FBD + Scan header length = 8 + Number of img components = 1 + Component[1]: selector=0x01, table=0(DC),0(AC) + Spectral selection = 1 .. 63 + Successive approximation = 0x10 + + NOTE: Scan parsing doesn't support this SOF mode. + +*** Marker: EOI (End of Image) (xFFD9) *** + OFFSET: 0x0000AF52 + + +*** Searching Compression Signatures *** + + Signature: 0155D875C95B74D0F3C5835A62516F48 + Signature (Rotated): 01D38A25358EB7649A254E19F1D46600 + File Offset: 0 bytes + Chroma subsampling: 2x2 + EXIF Make/Model: NONE + EXIF Makernotes: NONE + EXIF Software: NONE + + Searching Compression Signatures: (3347 built-in, 0 user(*) ) + + EXIF.Make / Software EXIF.Model Quality Subsamp Match? + ------------------------- ----------------------------------- ---------------- -------------- + CAM:[NIKON ] [E2500 ] [FINE ] No + CAM:[Nokia ] [N73 ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C2000Z ] [ ] No + CAM:[OLYMPUS OPTICAL CO.,LTD ] [C3040Z ] [ ] No + CAM:[PENTAX ] [PENTAX Optio 550 ] [ ] No + CAM:[Research In Motion ] [BlackBerry 8100 ] [ ] No + CAM:[SEIKO EPSON CORP. ] [PhotoPC 3000Z ] [ ] No + SW :[IJG Library ] [085 ] + SW :[Picasa ] [085 (Normal) ] + SW :[ZoomBrowser EX ] [medium ] + + The following IJG-based editors also match this signature: + SW :[GIMP ] [085 ] + SW :[IrfanView ] [085 ] + SW :[idImager ] [085 ] + SW :[FastStone Image Viewer ] [085 ] + SW :[NeatImage ] [085 ] + SW :[Paint.NET ] [085 ] + SW :[Photomatix ] [085 ] + SW :[XnView ] [085 ] + + Based on the analysis of compression characteristics and EXIF metadata: + + ASSESSMENT: Class 1 - Image is processed/edited + + This may be a new software editor for the database. + If this file is processed, and editor doesn't appear in list above, + PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] + diff --git a/tests/Images/Input/Png/gray-1-trns.png b/tests/Images/Input/Png/gray-1-trns.png new file mode 100644 index 0000000000..99b288d526 Binary files /dev/null and b/tests/Images/Input/Png/gray-1-trns.png differ diff --git a/tests/Images/Input/Png/gray-2-tRNS.png b/tests/Images/Input/Png/gray-2-tRNS.png new file mode 100644 index 0000000000..8e04cb5020 Binary files /dev/null and b/tests/Images/Input/Png/gray-2-tRNS.png differ diff --git a/tests/Images/Input/Png/gray-4-tRNS.png b/tests/Images/Input/Png/gray-4-tRNS.png new file mode 100644 index 0000000000..14c4f1fb3a Binary files /dev/null and b/tests/Images/Input/Png/gray-4-tRNS.png differ diff --git a/tests/Images/Input/Png/gray-8-tRNS.png b/tests/Images/Input/Png/gray-8-tRNS.png new file mode 100644 index 0000000000..842245f1d9 Binary files /dev/null and b/tests/Images/Input/Png/gray-8-tRNS.png differ