Browse Source

Master cleanup (#952)

* Fix gitignore and line endings

* Update README.md
af/merge-core
James Jackson-South 7 years ago
committed by GitHub
parent
commit
5abca49055
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .gitattributes
  2. 9
      .gitignore
  3. 8
      README.md
  4. 196
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
  5. 88
      src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs
  6. 324
      src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs
  7. 326
      src/ImageSharp/Configuration.cs
  8. 402
      src/ImageSharp/Formats/ImageFormatManager.cs
  9. BIN
      src/ImageSharp/Formats/Jpeg/5116.DCT_Filter.pdf
  10. BIN
      src/ImageSharp/Formats/Jpeg/itu-t81.pdf
  11. 352
      src/ImageSharp/GraphicsOptions.cs
  12. 6
      src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs
  13. 7618
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs
  14. 226
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt
  15. 4322
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs
  16. 364
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt
  17. 474
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs
  18. 338
      src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs
  19. 2
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs
  20. 2
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs
  21. 2
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs
  22. 2
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs
  23. 2
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs
  24. 2
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs
  25. 2
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs
  26. 2
      src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs
  27. 432
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs
  28. 352
      tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs
  29. 292
      tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs
  30. 110
      tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs
  31. 110
      tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs
  32. 532
      tests/Images/Input/Jpg/baseline/JpegSnoopReports/Floorplan.jpg.txt
  33. 868
      tests/Images/Input/Jpg/baseline/JpegSnoopReports/badrst.jpg.txt

2
.gitattributes

@ -44,7 +44,7 @@
*.sql text eol=lf *.sql text eol=lf
*.svg text eol=lf *.svg text eol=lf
*.targets text eol=lf *.targets text eol=lf
*.tt text eol=lf *.tt text eol=crlf
*.ttinclude text eol=crlf *.ttinclude text eol=crlf
*.txt text eol=lf *.txt text eol=lf
*.vb text eol=lf *.vb text eol=lf

9
.gitignore

@ -204,8 +204,6 @@ FakesAssemblies/
**/node_modules **/node_modules
**/node_modules/* **/node_modules/*
**/Images/ActualOutput
**/Images/ReferenceOutput
# ASP.NET 5 # ASP.NET 5
project.lock.json project.lock.json
@ -218,7 +216,8 @@ artifacts/
*.csproj.bak *.csproj.bak
#CodeCoverage #CodeCoverage
**/CodeCoverage/*
docs/
/samples/AvatarWithRoundedCorner/output
/ImageSharp.Coverage.xml /ImageSharp.Coverage.xml
# Tests
**/Images/ActualOutput
**/Images/ReferenceOutput

8
README.md

@ -118,12 +118,18 @@ Alternatively, you can work from command line and/or with a lightweight editor o
- [Visual Studio Code](https://code.visualstudio.com/) with [C# Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp) - [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: To clone ImageSharp locally, click the "Clone in [YOUR_OS]" button above or run the following git commands:
```bash ```bash
git clone https://github.com/SixLabors/ImageSharp git clone https://github.com/SixLabors/ImageSharp
``` ```
If working with Windows please ensure that you have enabled log file paths in git (run as Administrator).
```bash
git config --system core.longpaths true
```
### Submodules ### Submodules
This repository contains [git submodules](https://blog.github.com/2016-02-01-working-with-submodules/). To add the submodules to the project, navigate to the repository root and type: This repository contains [git submodules](https://blog.github.com/2016-02-01-working-with-submodules/). To add the submodules to the project, navigate to the repository root and type:

196
src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs

@ -1,99 +1,99 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Buffers; using System.Buffers;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{ {
/// <summary> /// <summary>
/// Combines two images together by blending the pixels. /// Combines two images together by blending the pixels.
/// </summary> /// </summary>
public class DrawImageProcessor : IImageProcessor public class DrawImageProcessor : IImageProcessor
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor"/> class. /// Initializes a new instance of the <see cref="DrawImageProcessor"/> class.
/// </summary> /// </summary>
/// <param name="image">The image to blend.</param> /// <param name="image">The image to blend.</param>
/// <param name="location">The location to draw the blended image.</param> /// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param> /// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param>
/// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param> /// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
/// <param name="opacity">The opacity of the image to blend.</param> /// <param name="opacity">The opacity of the image to blend.</param>
public DrawImageProcessor( public DrawImageProcessor(
Image image, Image image,
Point location, Point location,
PixelColorBlendingMode colorBlendingMode, PixelColorBlendingMode colorBlendingMode,
PixelAlphaCompositionMode alphaCompositionMode, PixelAlphaCompositionMode alphaCompositionMode,
float opacity) float opacity)
{ {
this.Image = image; this.Image = image;
this.Location = location; this.Location = location;
this.ColorBlendingMode = colorBlendingMode; this.ColorBlendingMode = colorBlendingMode;
this.AlphaCompositionMode = alphaCompositionMode; this.AlphaCompositionMode = alphaCompositionMode;
this.Opacity = opacity; this.Opacity = opacity;
} }
/// <summary> /// <summary>
/// Gets the image to blend. /// Gets the image to blend.
/// </summary> /// </summary>
public Image Image { get; } public Image Image { get; }
/// <summary> /// <summary>
/// Gets the location to draw the blended image. /// Gets the location to draw the blended image.
/// </summary> /// </summary>
public Point Location { get; } public Point Location { get; }
/// <summary> /// <summary>
/// Gets the blending mode to use when drawing the image. /// Gets the blending mode to use when drawing the image.
/// </summary> /// </summary>
public PixelColorBlendingMode ColorBlendingMode { get; } public PixelColorBlendingMode ColorBlendingMode { get; }
/// <summary> /// <summary>
/// Gets the Alpha blending mode to use when drawing the image. /// Gets the Alpha blending mode to use when drawing the image.
/// </summary> /// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode { get; } public PixelAlphaCompositionMode AlphaCompositionMode { get; }
/// <summary> /// <summary>
/// Gets the opacity of the image to blend. /// Gets the opacity of the image to blend.
/// </summary> /// </summary>
public float Opacity { get; } public float Opacity { get; }
/// <inheritdoc /> /// <inheritdoc />
public IImageProcessor<TPixelBg> CreatePixelSpecificProcessor<TPixelBg>() public IImageProcessor<TPixelBg> CreatePixelSpecificProcessor<TPixelBg>()
where TPixelBg : struct, IPixel<TPixelBg> where TPixelBg : struct, IPixel<TPixelBg>
{ {
var visitor = new ProcessorFactoryVisitor<TPixelBg>(this); var visitor = new ProcessorFactoryVisitor<TPixelBg>(this);
this.Image.AcceptVisitor(visitor); this.Image.AcceptVisitor(visitor);
return visitor.Result; return visitor.Result;
} }
private class ProcessorFactoryVisitor<TPixelBg> : IImageVisitor private class ProcessorFactoryVisitor<TPixelBg> : IImageVisitor
where TPixelBg : struct, IPixel<TPixelBg> where TPixelBg : struct, IPixel<TPixelBg>
{ {
private readonly DrawImageProcessor definition; private readonly DrawImageProcessor definition;
public ProcessorFactoryVisitor(DrawImageProcessor definition) public ProcessorFactoryVisitor(DrawImageProcessor definition)
{ {
this.definition = definition; this.definition = definition;
} }
public IImageProcessor<TPixelBg> Result { get; private set; } public IImageProcessor<TPixelBg> Result { get; private set; }
public void Visit<TPixelFg>(Image<TPixelFg> image) public void Visit<TPixelFg>(Image<TPixelFg> image)
where TPixelFg : struct, IPixel<TPixelFg> where TPixelFg : struct, IPixel<TPixelFg>
{ {
this.Result = new DrawImageProcessor<TPixelBg, TPixelFg>( this.Result = new DrawImageProcessor<TPixelBg, TPixelFg>(
image, image,
this.definition.Location, this.definition.Location,
this.definition.ColorBlendingMode, this.definition.ColorBlendingMode,
this.definition.AlphaCompositionMode, this.definition.AlphaCompositionMode,
this.definition.Opacity); this.definition.Opacity);
} }
} }
} }
} }

88
src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs

@ -1,45 +1,45 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{ {
/// <summary> /// <summary>
/// Defines a processor to fill an <see cref="Image"/> with the given <see cref="IBrush"/> /// Defines a processor to fill an <see cref="Image"/> with the given <see cref="IBrush"/>
/// using blending defined by the given <see cref="GraphicsOptions"/>. /// using blending defined by the given <see cref="GraphicsOptions"/>.
/// </summary> /// </summary>
public class FillProcessor : IImageProcessor public class FillProcessor : IImageProcessor
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FillProcessor"/> class. /// Initializes a new instance of the <see cref="FillProcessor"/> class.
/// </summary> /// </summary>
/// <param name="brush">The brush to use for filling.</param> /// <param name="brush">The brush to use for filling.</param>
/// <param name="options">The <see cref="GraphicsOptions"/> defining how to blend the brush pixels over the image pixels.</param> /// <param name="options">The <see cref="GraphicsOptions"/> defining how to blend the brush pixels over the image pixels.</param>
public FillProcessor(IBrush brush, GraphicsOptions options) public FillProcessor(IBrush brush, GraphicsOptions options)
{ {
this.Brush = brush; this.Brush = brush;
this.Options = options; this.Options = options;
} }
/// <summary> /// <summary>
/// Gets the <see cref="IBrush"/> used for filling the destination image. /// Gets the <see cref="IBrush"/> used for filling the destination image.
/// </summary> /// </summary>
public IBrush Brush { get; } public IBrush Brush { get; }
/// <summary> /// <summary>
/// Gets the <see cref="GraphicsOptions"/> defining how to blend the brush pixels over the image pixels. /// Gets the <see cref="GraphicsOptions"/> defining how to blend the brush pixels over the image pixels.
/// </summary> /// </summary>
public GraphicsOptions Options { get; } public GraphicsOptions Options { get; }
/// <inheritdoc /> /// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>() public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>()
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
return new FillProcessor<TPixel>(this); return new FillProcessor<TPixel>(this);
} }
} }
} }

324
src/ImageSharp.Drawing/Processing/TextGraphicsOptions.cs

@ -1,163 +1,163 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.Fonts; using SixLabors.Fonts;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing namespace SixLabors.ImageSharp.Processing
{ {
/// <summary> /// <summary>
/// Options for influencing the drawing functions. /// Options for influencing the drawing functions.
/// </summary> /// </summary>
public struct TextGraphicsOptions public struct TextGraphicsOptions
{ {
private const int DefaultTextDpi = 72; private const int DefaultTextDpi = 72;
/// <summary> /// <summary>
/// Represents the default <see cref="TextGraphicsOptions"/>. /// Represents the default <see cref="TextGraphicsOptions"/>.
/// </summary> /// </summary>
public static readonly TextGraphicsOptions Default = new TextGraphicsOptions(true); public static readonly TextGraphicsOptions Default = new TextGraphicsOptions(true);
private float? blendPercentage; private float? blendPercentage;
private int? antialiasSubpixelDepth; private int? antialiasSubpixelDepth;
private bool? antialias; private bool? antialias;
private bool? applyKerning; private bool? applyKerning;
private float? tabWidth; private float? tabWidth;
private float? dpiX; private float? dpiX;
private float? dpiY; private float? dpiY;
private HorizontalAlignment? horizontalAlignment; private HorizontalAlignment? horizontalAlignment;
private VerticalAlignment? verticalAlignment; private VerticalAlignment? verticalAlignment;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="TextGraphicsOptions" /> struct. /// Initializes a new instance of the <see cref="TextGraphicsOptions" /> struct.
/// </summary> /// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param> /// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
public TextGraphicsOptions(bool enableAntialiasing) public TextGraphicsOptions(bool enableAntialiasing)
{ {
this.applyKerning = true; this.applyKerning = true;
this.tabWidth = 4; this.tabWidth = 4;
this.WrapTextWidth = 0; this.WrapTextWidth = 0;
this.horizontalAlignment = HorizontalAlignment.Left; this.horizontalAlignment = HorizontalAlignment.Left;
this.verticalAlignment = VerticalAlignment.Top; this.verticalAlignment = VerticalAlignment.Top;
this.antialiasSubpixelDepth = 16; this.antialiasSubpixelDepth = 16;
this.ColorBlendingMode = PixelColorBlendingMode.Normal; this.ColorBlendingMode = PixelColorBlendingMode.Normal;
this.AlphaCompositionMode = PixelAlphaCompositionMode.SrcOver; this.AlphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = 1; this.blendPercentage = 1;
this.antialias = enableAntialiasing; this.antialias = enableAntialiasing;
this.dpiX = DefaultTextDpi; this.dpiX = DefaultTextDpi;
this.dpiY = DefaultTextDpi; this.dpiY = DefaultTextDpi;
} }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether antialiasing should be applied. /// Gets or sets a value indicating whether antialiasing should be applied.
/// </summary> /// </summary>
public bool Antialias { get => this.antialias ?? true; set => this.antialias = value; } public bool Antialias { get => this.antialias ?? true; set => this.antialias = value; }
/// <summary> /// <summary>
/// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled.
/// </summary> /// </summary>
public int AntialiasSubpixelDepth { get => this.antialiasSubpixelDepth ?? 16; set => this.antialiasSubpixelDepth = value; } public int AntialiasSubpixelDepth { get => this.antialiasSubpixelDepth ?? 16; set => this.antialiasSubpixelDepth = value; }
/// <summary> /// <summary>
/// Gets or sets a value indicating the blending percentage to apply to the drawing operation /// Gets or sets a value indicating the blending percentage to apply to the drawing operation
/// </summary> /// </summary>
public float BlendPercentage { get => (this.blendPercentage ?? 1).Clamp(0, 1); set => this.blendPercentage = value; } public float BlendPercentage { get => (this.blendPercentage ?? 1).Clamp(0, 1); set => this.blendPercentage = value; }
// In the future we could expose a PixelBlender<TPixel> directly on here // In the future we could expose a PixelBlender<TPixel> directly on here
// or some forms of PixelBlender factory for each pixel type. Will need // or some forms of PixelBlender factory for each pixel type. Will need
// some API thought post V1. // some API thought post V1.
/// <summary> /// <summary>
/// Gets or sets a value indicating the color blending percentage to apply to the drawing operation /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation
/// </summary> /// </summary>
public PixelColorBlendingMode ColorBlendingMode { get; set; } public PixelColorBlendingMode ColorBlendingMode { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating the color blending percentage to apply to the drawing operation /// Gets or sets a value indicating the color blending percentage to apply to the drawing operation
/// </summary> /// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode { get; set; } public PixelAlphaCompositionMode AlphaCompositionMode { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether the text should be drawing with kerning enabled. /// Gets or sets a value indicating whether the text should be drawing with kerning enabled.
/// </summary> /// </summary>
public bool ApplyKerning { get => this.applyKerning ?? true; set => this.applyKerning = value; } public bool ApplyKerning { get => this.applyKerning ?? true; set => this.applyKerning = value; }
/// <summary> /// <summary>
/// Gets or sets a value indicating the number of space widths a tab should lock to. /// Gets or sets a value indicating the number of space widths a tab should lock to.
/// </summary> /// </summary>
public float TabWidth { get => this.tabWidth ?? 4; set => this.tabWidth = value; } public float TabWidth { get => this.tabWidth ?? 4; set => this.tabWidth = value; }
/// <summary> /// <summary>
/// Gets or sets a value indicating if greater than zero determine the width at which text should wrap. /// Gets or sets a value indicating if greater than zero determine the width at which text should wrap.
/// </summary> /// </summary>
public float WrapTextWidth { get; set; } public float WrapTextWidth { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating the DPI to render text along the X axis. /// Gets or sets a value indicating the DPI to render text along the X axis.
/// </summary> /// </summary>
public float DpiX { get => this.dpiX ?? DefaultTextDpi; set => this.dpiX = value; } public float DpiX { get => this.dpiX ?? DefaultTextDpi; set => this.dpiX = value; }
/// <summary> /// <summary>
/// Gets or sets a value indicating the DPI to render text along the Y axis. /// Gets or sets a value indicating the DPI to render text along the Y axis.
/// </summary> /// </summary>
public float DpiY { get => this.dpiY ?? DefaultTextDpi; set => this.dpiY = value; } public float DpiY { get => this.dpiY ?? DefaultTextDpi; set => this.dpiY = value; }
/// <summary> /// <summary>
/// Gets or sets a value indicating how to align the text relative to the rendering space. /// Gets or sets a value indicating how to align the text relative to the rendering space.
/// If <see cref="WrapTextWidth"/> is greater than zero it will align relative to the space /// If <see cref="WrapTextWidth"/> is greater than zero it will align relative to the space
/// defined by the location and width, if <see cref="WrapTextWidth"/> equals zero, and thus /// defined by the location and width, if <see cref="WrapTextWidth"/> equals zero, and thus
/// wrapping disabled, then the alignment is relative to the drawing location. /// wrapping disabled, then the alignment is relative to the drawing location.
/// </summary> /// </summary>
public HorizontalAlignment HorizontalAlignment { get => this.horizontalAlignment ?? HorizontalAlignment.Left; set => this.horizontalAlignment = value; } public HorizontalAlignment HorizontalAlignment { get => this.horizontalAlignment ?? HorizontalAlignment.Left; set => this.horizontalAlignment = value; }
/// <summary> /// <summary>
/// Gets or sets a value indicating how to align the text relative to the rendering space. /// Gets or sets a value indicating how to align the text relative to the rendering space.
/// </summary> /// </summary>
public VerticalAlignment VerticalAlignment { get => this.verticalAlignment ?? VerticalAlignment.Top; set => this.verticalAlignment = value; } public VerticalAlignment VerticalAlignment { get => this.verticalAlignment ?? VerticalAlignment.Top; set => this.verticalAlignment = value; }
/// <summary> /// <summary>
/// Performs an implicit conversion from <see cref="GraphicsOptions"/> to <see cref="TextGraphicsOptions"/>. /// Performs an implicit conversion from <see cref="GraphicsOptions"/> to <see cref="TextGraphicsOptions"/>.
/// </summary> /// </summary>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns> /// <returns>
/// The result of the conversion. /// The result of the conversion.
/// </returns> /// </returns>
public static implicit operator TextGraphicsOptions(GraphicsOptions options) public static implicit operator TextGraphicsOptions(GraphicsOptions options)
{ {
return new TextGraphicsOptions(options.Antialias) return new TextGraphicsOptions(options.Antialias)
{ {
AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, AntialiasSubpixelDepth = options.AntialiasSubpixelDepth,
blendPercentage = options.BlendPercentage, blendPercentage = options.BlendPercentage,
ColorBlendingMode = options.ColorBlendingMode, ColorBlendingMode = options.ColorBlendingMode,
AlphaCompositionMode = options.AlphaCompositionMode AlphaCompositionMode = options.AlphaCompositionMode
}; };
} }
/// <summary> /// <summary>
/// Performs an explicit conversion from <see cref="TextGraphicsOptions"/> to <see cref="GraphicsOptions"/>. /// Performs an explicit conversion from <see cref="TextGraphicsOptions"/> to <see cref="GraphicsOptions"/>.
/// </summary> /// </summary>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns> /// <returns>
/// The result of the conversion. /// The result of the conversion.
/// </returns> /// </returns>
public static explicit operator GraphicsOptions(TextGraphicsOptions options) public static explicit operator GraphicsOptions(TextGraphicsOptions options)
{ {
return new GraphicsOptions(options.Antialias) return new GraphicsOptions(options.Antialias)
{ {
AntialiasSubpixelDepth = options.AntialiasSubpixelDepth, AntialiasSubpixelDepth = options.AntialiasSubpixelDepth,
ColorBlendingMode = options.ColorBlendingMode, ColorBlendingMode = options.ColorBlendingMode,
AlphaCompositionMode = options.AlphaCompositionMode, AlphaCompositionMode = options.AlphaCompositionMode,
BlendPercentage = options.BlendPercentage BlendPercentage = options.BlendPercentage
}; };
} }
} }
} }

326
src/ImageSharp/Configuration.cs

@ -1,164 +1,164 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.Memory; using SixLabors.Memory;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
{ {
/// <summary> /// <summary>
/// Provides configuration code which allows altering default behaviour or extending the library. /// Provides configuration code which allows altering default behaviour or extending the library.
/// </summary> /// </summary>
public sealed class Configuration public sealed class Configuration
{ {
/// <summary> /// <summary>
/// A lazily initialized configuration default instance. /// A lazily initialized configuration default instance.
/// </summary> /// </summary>
private static readonly Lazy<Configuration> Lazy = new Lazy<Configuration>(CreateDefaultInstance); private static readonly Lazy<Configuration> Lazy = new Lazy<Configuration>(CreateDefaultInstance);
private int maxDegreeOfParallelism = Environment.ProcessorCount; private int maxDegreeOfParallelism = Environment.ProcessorCount;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class. /// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary> /// </summary>
public Configuration() public Configuration()
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Configuration" /> class. /// Initializes a new instance of the <see cref="Configuration" /> class.
/// </summary> /// </summary>
/// <param name="configurationModules">A collection of configuration modules to register</param> /// <param name="configurationModules">A collection of configuration modules to register</param>
public Configuration(params IConfigurationModule[] configurationModules) public Configuration(params IConfigurationModule[] configurationModules)
{ {
if (configurationModules != null) if (configurationModules != null)
{ {
foreach (IConfigurationModule p in configurationModules) foreach (IConfigurationModule p in configurationModules)
{ {
p.Configure(this); p.Configure(this);
} }
} }
} }
/// <summary> /// <summary>
/// Gets the default <see cref="Configuration"/> instance. /// Gets the default <see cref="Configuration"/> instance.
/// </summary> /// </summary>
public static Configuration Default { get; } = Lazy.Value; public static Configuration Default { get; } = Lazy.Value;
/// <summary> /// <summary>
/// Gets or sets the maximum number of concurrent tasks enabled in ImageSharp algorithms /// Gets or sets the maximum number of concurrent tasks enabled in ImageSharp algorithms
/// configured with this <see cref="Configuration"/> instance. /// configured with this <see cref="Configuration"/> instance.
/// Initialized with <see cref="Environment.ProcessorCount"/> by default. /// Initialized with <see cref="Environment.ProcessorCount"/> by default.
/// </summary> /// </summary>
public int MaxDegreeOfParallelism public int MaxDegreeOfParallelism
{ {
get => this.maxDegreeOfParallelism; get => this.maxDegreeOfParallelism;
set set
{ {
if (value <= 0) if (value <= 0)
{ {
throw new ArgumentOutOfRangeException(nameof(this.MaxDegreeOfParallelism)); throw new ArgumentOutOfRangeException(nameof(this.MaxDegreeOfParallelism));
} }
this.maxDegreeOfParallelism = value; this.maxDegreeOfParallelism = value;
} }
} }
/// <summary> /// <summary>
/// Gets the currently registered <see cref="IImageFormat"/>s. /// Gets the currently registered <see cref="IImageFormat"/>s.
/// </summary> /// </summary>
public IEnumerable<IImageFormat> ImageFormats => this.ImageFormatsManager.ImageFormats; public IEnumerable<IImageFormat> ImageFormats => this.ImageFormatsManager.ImageFormats;
/// <summary> /// <summary>
/// Gets or sets the position in a stream to use for reading when using a seekable stream as an image data source. /// Gets or sets the position in a stream to use for reading when using a seekable stream as an image data source.
/// </summary> /// </summary>
public ReadOrigin ReadOrigin { get; set; } = ReadOrigin.Current; public ReadOrigin ReadOrigin { get; set; } = ReadOrigin.Current;
/// <summary> /// <summary>
/// Gets or sets the <see cref="ImageFormatManager"/> that is currently in use. /// Gets or sets the <see cref="ImageFormatManager"/> that is currently in use.
/// </summary> /// </summary>
public ImageFormatManager ImageFormatsManager { get; set; } = new ImageFormatManager(); public ImageFormatManager ImageFormatsManager { get; set; } = new ImageFormatManager();
/// <summary> /// <summary>
/// Gets or sets the <see cref="MemoryAllocator"/> that is currently in use. /// Gets or sets the <see cref="MemoryAllocator"/> that is currently in use.
/// </summary> /// </summary>
public MemoryAllocator MemoryAllocator { get; set; } = ArrayPoolMemoryAllocator.CreateDefault(); public MemoryAllocator MemoryAllocator { get; set; } = ArrayPoolMemoryAllocator.CreateDefault();
/// <summary> /// <summary>
/// Gets the maximum header size of all the formats. /// Gets the maximum header size of all the formats.
/// </summary> /// </summary>
internal int MaxHeaderSize => this.ImageFormatsManager.MaxHeaderSize; internal int MaxHeaderSize => this.ImageFormatsManager.MaxHeaderSize;
/// <summary> /// <summary>
/// Gets or sets the filesystem helper for accessing the local file system. /// Gets or sets the filesystem helper for accessing the local file system.
/// </summary> /// </summary>
internal IFileSystem FileSystem { get; set; } = new LocalFileSystem(); internal IFileSystem FileSystem { get; set; } = new LocalFileSystem();
/// <summary> /// <summary>
/// Gets or sets the working buffer size hint for image processors. /// Gets or sets the working buffer size hint for image processors.
/// The default value is 1MB. /// The default value is 1MB.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Currently only used by Resize. /// Currently only used by Resize.
/// </remarks> /// </remarks>
internal int WorkingBufferSizeHintInBytes { get; set; } = 1 * 1024 * 1024; internal int WorkingBufferSizeHintInBytes { get; set; } = 1 * 1024 * 1024;
/// <summary> /// <summary>
/// Gets or sets the image operations provider factory. /// Gets or sets the image operations provider factory.
/// </summary> /// </summary>
internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory(); internal IImageProcessingContextFactory ImageOperationsProvider { get; set; } = new DefaultImageOperationsProviderFactory();
/// <summary> /// <summary>
/// Registers a new format provider. /// Registers a new format provider.
/// </summary> /// </summary>
/// <param name="configuration">The configuration provider to call configure on.</param> /// <param name="configuration">The configuration provider to call configure on.</param>
public void Configure(IConfigurationModule configuration) public void Configure(IConfigurationModule configuration)
{ {
Guard.NotNull(configuration, nameof(configuration)); Guard.NotNull(configuration, nameof(configuration));
configuration.Configure(this); configuration.Configure(this);
} }
/// <summary> /// <summary>
/// Creates a shallow copy of the <see cref="Configuration"/>. /// Creates a shallow copy of the <see cref="Configuration"/>.
/// </summary> /// </summary>
/// <returns>A new configuration instance.</returns> /// <returns>A new configuration instance.</returns>
public Configuration Clone() public Configuration Clone()
{ {
return new Configuration return new Configuration
{ {
MaxDegreeOfParallelism = this.MaxDegreeOfParallelism, MaxDegreeOfParallelism = this.MaxDegreeOfParallelism,
ImageFormatsManager = this.ImageFormatsManager, ImageFormatsManager = this.ImageFormatsManager,
MemoryAllocator = this.MemoryAllocator, MemoryAllocator = this.MemoryAllocator,
ImageOperationsProvider = this.ImageOperationsProvider, ImageOperationsProvider = this.ImageOperationsProvider,
ReadOrigin = this.ReadOrigin, ReadOrigin = this.ReadOrigin,
FileSystem = this.FileSystem, FileSystem = this.FileSystem,
WorkingBufferSizeHintInBytes = this.WorkingBufferSizeHintInBytes, WorkingBufferSizeHintInBytes = this.WorkingBufferSizeHintInBytes,
}; };
} }
/// <summary> /// <summary>
/// Creates the default instance with the following <see cref="IConfigurationModule"/>s preregistered: /// Creates the default instance with the following <see cref="IConfigurationModule"/>s preregistered:
/// <see cref="PngConfigurationModule"/> /// <see cref="PngConfigurationModule"/>
/// <see cref="JpegConfigurationModule"/> /// <see cref="JpegConfigurationModule"/>
/// <see cref="GifConfigurationModule"/> /// <see cref="GifConfigurationModule"/>
/// <see cref="BmpConfigurationModule"/>. /// <see cref="BmpConfigurationModule"/>.
/// </summary> /// </summary>
/// <returns>The default configuration of <see cref="Configuration"/>.</returns> /// <returns>The default configuration of <see cref="Configuration"/>.</returns>
internal static Configuration CreateDefaultInstance() internal static Configuration CreateDefaultInstance()
{ {
return new Configuration( return new Configuration(
new PngConfigurationModule(), new PngConfigurationModule(),
new JpegConfigurationModule(), new JpegConfigurationModule(),
new GifConfigurationModule(), new GifConfigurationModule(),
new BmpConfigurationModule()); new BmpConfigurationModule());
} }
} }
} }

402
src/ImageSharp/Formats/ImageFormatManager.cs

@ -1,201 +1,201 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace SixLabors.ImageSharp.Formats namespace SixLabors.ImageSharp.Formats
{ {
/// <summary> /// <summary>
/// Collection of Image Formats to be used in <see cref="Configuration" /> class. /// Collection of Image Formats to be used in <see cref="Configuration" /> class.
/// </summary> /// </summary>
public class ImageFormatManager public class ImageFormatManager
{ {
/// <summary> /// <summary>
/// Used for locking against as there is no ConcurrentSet type. /// Used for locking against as there is no ConcurrentSet type.
/// <see href="https://github.com/dotnet/corefx/issues/6318"/> /// <see href="https://github.com/dotnet/corefx/issues/6318"/>
/// </summary> /// </summary>
private static readonly object HashLock = new object(); private static readonly object HashLock = new object();
/// <summary> /// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types. /// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary> /// </summary>
private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new ConcurrentDictionary<IImageFormat, IImageEncoder>(); private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new ConcurrentDictionary<IImageFormat, IImageEncoder>();
/// <summary> /// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types. /// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary> /// </summary>
private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new ConcurrentDictionary<IImageFormat, IImageDecoder>(); private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new ConcurrentDictionary<IImageFormat, IImageDecoder>();
/// <summary> /// <summary>
/// The list of supported <see cref="IImageFormat"/>s. /// The list of supported <see cref="IImageFormat"/>s.
/// </summary> /// </summary>
private readonly HashSet<IImageFormat> imageFormats = new HashSet<IImageFormat>(); private readonly HashSet<IImageFormat> imageFormats = new HashSet<IImageFormat>();
/// <summary> /// <summary>
/// The list of supported <see cref="IImageFormatDetector"/>s. /// The list of supported <see cref="IImageFormatDetector"/>s.
/// </summary> /// </summary>
private ConcurrentBag<IImageFormatDetector> imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>(); private ConcurrentBag<IImageFormatDetector> imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageFormatManager" /> class. /// Initializes a new instance of the <see cref="ImageFormatManager" /> class.
/// </summary> /// </summary>
public ImageFormatManager() public ImageFormatManager()
{ {
} }
/// <summary> /// <summary>
/// Gets the maximum header size of all the formats. /// Gets the maximum header size of all the formats.
/// </summary> /// </summary>
internal int MaxHeaderSize { get; private set; } internal int MaxHeaderSize { get; private set; }
/// <summary> /// <summary>
/// Gets the currently registered <see cref="IImageFormat"/>s. /// Gets the currently registered <see cref="IImageFormat"/>s.
/// </summary> /// </summary>
public IEnumerable<IImageFormat> ImageFormats => this.imageFormats; public IEnumerable<IImageFormat> ImageFormats => this.imageFormats;
/// <summary> /// <summary>
/// Gets the currently registered <see cref="IImageFormatDetector"/>s. /// Gets the currently registered <see cref="IImageFormatDetector"/>s.
/// </summary> /// </summary>
internal IEnumerable<IImageFormatDetector> FormatDetectors => this.imageFormatDetectors; internal IEnumerable<IImageFormatDetector> FormatDetectors => this.imageFormatDetectors;
/// <summary> /// <summary>
/// Gets the currently registered <see cref="IImageDecoder"/>s. /// Gets the currently registered <see cref="IImageDecoder"/>s.
/// </summary> /// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageDecoder>> ImageDecoders => this.mimeTypeDecoders; internal IEnumerable<KeyValuePair<IImageFormat, IImageDecoder>> ImageDecoders => this.mimeTypeDecoders;
/// <summary> /// <summary>
/// Gets the currently registered <see cref="IImageEncoder"/>s. /// Gets the currently registered <see cref="IImageEncoder"/>s.
/// </summary> /// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageEncoder>> ImageEncoders => this.mimeTypeEncoders; internal IEnumerable<KeyValuePair<IImageFormat, IImageEncoder>> ImageEncoders => this.mimeTypeEncoders;
/// <summary> /// <summary>
/// Registers a new format provider. /// Registers a new format provider.
/// </summary> /// </summary>
/// <param name="format">The format to register as a known format.</param> /// <param name="format">The format to register as a known format.</param>
public void AddImageFormat(IImageFormat format) public void AddImageFormat(IImageFormat format)
{ {
Guard.NotNull(format, nameof(format)); Guard.NotNull(format, nameof(format));
Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes)); Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes));
Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions)); Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions));
lock (HashLock) lock (HashLock)
{ {
if (!this.imageFormats.Contains(format)) if (!this.imageFormats.Contains(format))
{ {
this.imageFormats.Add(format); this.imageFormats.Add(format);
} }
} }
} }
/// <summary> /// <summary>
/// For the specified file extensions type find the e <see cref="IImageFormat"/>. /// For the specified file extensions type find the e <see cref="IImageFormat"/>.
/// </summary> /// </summary>
/// <param name="extension">The extension to discover</param> /// <param name="extension">The extension to discover</param>
/// <returns>The <see cref="IImageFormat"/> if found otherwise null</returns> /// <returns>The <see cref="IImageFormat"/> if found otherwise null</returns>
public IImageFormat FindFormatByFileExtension(string extension) public IImageFormat FindFormatByFileExtension(string extension)
{ {
Guard.NotNullOrWhiteSpace(extension, nameof(extension)); Guard.NotNullOrWhiteSpace(extension, nameof(extension));
if (extension[0] == '.') if (extension[0] == '.')
{ {
extension = extension.Substring(1); extension = extension.Substring(1);
} }
return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase));
} }
/// <summary> /// <summary>
/// For the specified mime type find the <see cref="IImageFormat"/>. /// For the specified mime type find the <see cref="IImageFormat"/>.
/// </summary> /// </summary>
/// <param name="mimeType">The mime-type to discover</param> /// <param name="mimeType">The mime-type to discover</param>
/// <returns>The <see cref="IImageFormat"/> if found; otherwise null</returns> /// <returns>The <see cref="IImageFormat"/> if found; otherwise null</returns>
public IImageFormat FindFormatByMimeType(string mimeType) public IImageFormat FindFormatByMimeType(string mimeType)
{ {
return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase)); return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase));
} }
/// <summary> /// <summary>
/// Sets a specific image encoder as the encoder for a specific image format. /// Sets a specific image encoder as the encoder for a specific image format.
/// </summary> /// </summary>
/// <param name="imageFormat">The image format to register the encoder for.</param> /// <param name="imageFormat">The image format to register the encoder for.</param>
/// <param name="encoder">The encoder to use,</param> /// <param name="encoder">The encoder to use,</param>
public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder) public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder)
{ {
Guard.NotNull(imageFormat, nameof(imageFormat)); Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(encoder, nameof(encoder)); Guard.NotNull(encoder, nameof(encoder));
this.AddImageFormat(imageFormat); this.AddImageFormat(imageFormat);
this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder); this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder);
} }
/// <summary> /// <summary>
/// Sets a specific image decoder as the decoder for a specific image format. /// Sets a specific image decoder as the decoder for a specific image format.
/// </summary> /// </summary>
/// <param name="imageFormat">The image format to register the encoder for.</param> /// <param name="imageFormat">The image format to register the encoder for.</param>
/// <param name="decoder">The decoder to use,</param> /// <param name="decoder">The decoder to use,</param>
public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder) public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder)
{ {
Guard.NotNull(imageFormat, nameof(imageFormat)); Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(decoder, nameof(decoder)); Guard.NotNull(decoder, nameof(decoder));
this.AddImageFormat(imageFormat); this.AddImageFormat(imageFormat);
this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder); this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder);
} }
/// <summary> /// <summary>
/// Removes all the registered image format detectors. /// Removes all the registered image format detectors.
/// </summary> /// </summary>
public void ClearImageFormatDetectors() public void ClearImageFormatDetectors()
{ {
this.imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>(); this.imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>();
} }
/// <summary> /// <summary>
/// Adds a new detector for detecting mime types. /// Adds a new detector for detecting mime types.
/// </summary> /// </summary>
/// <param name="detector">The detector to add</param> /// <param name="detector">The detector to add</param>
public void AddImageFormatDetector(IImageFormatDetector detector) public void AddImageFormatDetector(IImageFormatDetector detector)
{ {
Guard.NotNull(detector, nameof(detector)); Guard.NotNull(detector, nameof(detector));
this.imageFormatDetectors.Add(detector); this.imageFormatDetectors.Add(detector);
this.SetMaxHeaderSize(); this.SetMaxHeaderSize();
} }
/// <summary> /// <summary>
/// For the specified mime type find the decoder. /// For the specified mime type find the decoder.
/// </summary> /// </summary>
/// <param name="format">The format to discover</param> /// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageDecoder"/> if found otherwise null</returns> /// <returns>The <see cref="IImageDecoder"/> if found otherwise null</returns>
public IImageDecoder FindDecoder(IImageFormat format) public IImageDecoder FindDecoder(IImageFormat format)
{ {
Guard.NotNull(format, nameof(format)); Guard.NotNull(format, nameof(format));
return this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder) return this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder)
? decoder ? decoder
: null; : null;
} }
/// <summary> /// <summary>
/// For the specified mime type find the encoder. /// For the specified mime type find the encoder.
/// </summary> /// </summary>
/// <param name="format">The format to discover</param> /// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageEncoder"/> if found otherwise null</returns> /// <returns>The <see cref="IImageEncoder"/> if found otherwise null</returns>
public IImageEncoder FindEncoder(IImageFormat format) public IImageEncoder FindEncoder(IImageFormat format)
{ {
Guard.NotNull(format, nameof(format)); Guard.NotNull(format, nameof(format));
return this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder) return this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder)
? encoder ? encoder
: null; : null;
} }
/// <summary> /// <summary>
/// Sets the max header size. /// Sets the max header size.
/// </summary> /// </summary>
private void SetMaxHeaderSize() private void SetMaxHeaderSize()
{ {
this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize); this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize);
} }
} }
} }

BIN
src/ImageSharp/Formats/Jpeg/5116.DCT_Filter.pdf

Binary file not shown.

BIN
src/ImageSharp/Formats/Jpeg/itu-t81.pdf

Binary file not shown.

352
src/ImageSharp/GraphicsOptions.cs

@ -1,177 +1,177 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
{ {
/// <summary> /// <summary>
/// Options for influencing the drawing functions. /// Options for influencing the drawing functions.
/// </summary> /// </summary>
public struct GraphicsOptions public struct GraphicsOptions
{ {
/// <summary> /// <summary>
/// Represents the default <see cref="GraphicsOptions"/>. /// Represents the default <see cref="GraphicsOptions"/>.
/// </summary> /// </summary>
public static readonly GraphicsOptions Default = new GraphicsOptions(true); public static readonly GraphicsOptions Default = new GraphicsOptions(true);
private float? blendPercentage; private float? blendPercentage;
private int? antialiasSubpixelDepth; private int? antialiasSubpixelDepth;
private bool? antialias; private bool? antialias;
private PixelColorBlendingMode colorBlendingMode; private PixelColorBlendingMode colorBlendingMode;
private PixelAlphaCompositionMode alphaCompositionMode; private PixelAlphaCompositionMode alphaCompositionMode;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct. /// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary> /// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param> /// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
public GraphicsOptions(bool enableAntialiasing) public GraphicsOptions(bool enableAntialiasing)
{ {
this.colorBlendingMode = PixelColorBlendingMode.Normal; this.colorBlendingMode = PixelColorBlendingMode.Normal;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = 1; this.blendPercentage = 1;
this.antialiasSubpixelDepth = 16; this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing; this.antialias = enableAntialiasing;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct. /// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary> /// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param> /// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
/// <param name="opacity">blending percentage to apply to the drawing operation</param> /// <param name="opacity">blending percentage to apply to the drawing operation</param>
public GraphicsOptions(bool enableAntialiasing, float opacity) public GraphicsOptions(bool enableAntialiasing, float opacity)
{ {
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = PixelColorBlendingMode.Normal; this.colorBlendingMode = PixelColorBlendingMode.Normal;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = opacity; this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16; this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing; this.antialias = enableAntialiasing;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct. /// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary> /// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param> /// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
/// <param name="opacity">blending percentage to apply to the drawing operation</param> /// <param name="opacity">blending percentage to apply to the drawing operation</param>
/// <param name="blending">color blending mode to apply to the drawing operation</param> /// <param name="blending">color blending mode to apply to the drawing operation</param>
public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, float opacity) public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, float opacity)
{ {
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = blending; this.colorBlendingMode = blending;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver; this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = opacity; this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16; this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing; this.antialias = enableAntialiasing;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct. /// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary> /// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param> /// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
/// <param name="opacity">blending percentage to apply to the drawing operation</param> /// <param name="opacity">blending percentage to apply to the drawing operation</param>
/// <param name="blending">color blending mode to apply to the drawing operation</param> /// <param name="blending">color blending mode to apply to the drawing operation</param>
/// <param name="composition">alpha composition mode to apply to the drawing operation</param> /// <param name="composition">alpha composition mode to apply to the drawing operation</param>
public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, PixelAlphaCompositionMode composition, float opacity) public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, PixelAlphaCompositionMode composition, float opacity)
{ {
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = blending; this.colorBlendingMode = blending;
this.alphaCompositionMode = composition; this.alphaCompositionMode = composition;
this.blendPercentage = opacity; this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16; this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing; this.antialias = enableAntialiasing;
} }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether antialiasing should be applied. /// Gets or sets a value indicating whether antialiasing should be applied.
/// </summary> /// </summary>
public bool Antialias public bool Antialias
{ {
get => this.antialias ?? true; get => this.antialias ?? true;
set => this.antialias = value; set => this.antialias = value;
} }
/// <summary> /// <summary>
/// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled. /// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled.
/// </summary> /// </summary>
public int AntialiasSubpixelDepth public int AntialiasSubpixelDepth
{ {
get => this.antialiasSubpixelDepth ?? 16; get => this.antialiasSubpixelDepth ?? 16;
set => this.antialiasSubpixelDepth = value; set => this.antialiasSubpixelDepth = value;
} }
/// <summary> /// <summary>
/// Gets or sets a value indicating the blending percentage to apply to the drawing operation /// Gets or sets a value indicating the blending percentage to apply to the drawing operation
/// </summary> /// </summary>
public float BlendPercentage public float BlendPercentage
{ {
get => (this.blendPercentage ?? 1).Clamp(0, 1); get => (this.blendPercentage ?? 1).Clamp(0, 1);
set => this.blendPercentage = value; set => this.blendPercentage = value;
} }
// In the future we could expose a PixelBlender<TPixel> directly on here // In the future we could expose a PixelBlender<TPixel> directly on here
// or some forms of PixelBlender factory for each pixel type. Will need // or some forms of PixelBlender factory for each pixel type. Will need
// some API thought post V1. // some API thought post V1.
/// <summary> /// <summary>
/// Gets or sets a value indicating the color blending mode to apply to the drawing operation /// Gets or sets a value indicating the color blending mode to apply to the drawing operation
/// </summary> /// </summary>
public PixelColorBlendingMode ColorBlendingMode public PixelColorBlendingMode ColorBlendingMode
{ {
get => this.colorBlendingMode; get => this.colorBlendingMode;
set => this.colorBlendingMode = value; set => this.colorBlendingMode = value;
} }
/// <summary> /// <summary>
/// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation /// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation
/// </summary> /// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode public PixelAlphaCompositionMode AlphaCompositionMode
{ {
get => this.alphaCompositionMode; get => this.alphaCompositionMode;
set => this.alphaCompositionMode = value; set => this.alphaCompositionMode = value;
} }
/// <summary> /// <summary>
/// Evaluates if a given SOURCE color can completely replace a BACKDROP color given the current blending and composition settings. /// Evaluates if a given SOURCE color can completely replace a BACKDROP color given the current blending and composition settings.
/// </summary> /// </summary>
/// <param name="color">the color</param> /// <param name="color">the color</param>
/// <returns>true if the color can be considered opaque</returns> /// <returns>true if the color can be considered opaque</returns>
/// <remarks> /// <remarks>
/// Blending and composition is an expensive operation, in some cases, like /// Blending and composition is an expensive operation, in some cases, like
/// filling with a solid color, the blending can be avoided by a plain color replacement. /// filling with a solid color, the blending can be avoided by a plain color replacement.
/// This method can be useful for such processors to select the fast path. /// This method can be useful for such processors to select the fast path.
/// </remarks> /// </remarks>
internal bool IsOpaqueColorWithoutBlending(Color color) internal bool IsOpaqueColorWithoutBlending(Color color)
{ {
if (this.ColorBlendingMode != PixelColorBlendingMode.Normal) if (this.ColorBlendingMode != PixelColorBlendingMode.Normal)
{ {
return false; return false;
} }
if (this.AlphaCompositionMode != PixelAlphaCompositionMode.SrcOver && if (this.AlphaCompositionMode != PixelAlphaCompositionMode.SrcOver &&
this.AlphaCompositionMode != PixelAlphaCompositionMode.Src) this.AlphaCompositionMode != PixelAlphaCompositionMode.Src)
{ {
return false; return false;
} }
if (this.BlendPercentage != 1f) if (this.BlendPercentage != 1f)
{ {
return false; return false;
} }
if (color.ToVector4().W != 1f) if (color.ToVector4().W != 1f)
{ {
return false; return false;
} }
return true; return true;
} }
} }
} }

6
src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs

@ -7,12 +7,12 @@ namespace SixLabors.ImageSharp.PixelFormats
/// Enumerates the various alpha composition modes. /// Enumerates the various alpha composition modes.
/// </summary> /// </summary>
public enum PixelAlphaCompositionMode public enum PixelAlphaCompositionMode
{ {
/// <summary> /// <summary>
/// returns the destination over the source. /// returns the destination over the source.
/// </summary> /// </summary>
SrcOver = 0, SrcOver = 0,
/// <summary> /// <summary>
/// returns the source colors. /// returns the source colors.
/// </summary> /// </summary>

7618
src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs

File diff suppressed because it is too large

226
src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt

@ -1,114 +1,114 @@
<# <#
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
#> #>
<#@ template debug="false" hostspecific="false" language="C#" #> <#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #> <#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #> <#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #> <#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #> <#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #> <#@ output extension=".cs" #>
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// <auto-generated /> // <auto-generated />
using System; using System;
using System.Numerics; using System.Numerics;
using System.Buffers; using System.Buffers;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.Memory; using SixLabors.Memory;
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{ {
/// <summary> /// <summary>
/// Collection of Porter Duff alpha blending functions applying different composition models. /// Collection of Porter Duff alpha blending functions applying different composition models.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// These functions are designed to be a general solution for all color cases, /// 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 /// 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 /// and source, and there's no need to alpha-premultiply neither the backdrop
/// nor the source. /// nor the source.
/// Note there are faster functions for when the backdrop color is known /// Note there are faster functions for when the backdrop color is known
/// to be opaque /// to be opaque
/// </remarks> /// </remarks>
internal static class DefaultPixelBlenders<TPixel> internal static class DefaultPixelBlenders<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
<# <#
string[] composers = new []{ string[] composers = new []{
"Src", "Src",
"SrcAtop", "SrcAtop",
"SrcOver", "SrcOver",
"SrcIn", "SrcIn",
"SrcOut", "SrcOut",
"Dest", "Dest",
"DestAtop", "DestAtop",
"DestOver", "DestOver",
"DestIn", "DestIn",
"DestOut", "DestOut",
"Clear", "Clear",
"Xor", "Xor",
}; };
string[] blenders = new []{ string[] blenders = new []{
"Normal", "Normal",
"Multiply", "Multiply",
"Add", "Add",
"Subtract", "Subtract",
"Screen", "Screen",
"Darken", "Darken",
"Lighten", "Lighten",
"Overlay", "Overlay",
"HardLight" "HardLight"
}; };
foreach(var composer in composers) { foreach(var composer in composers) {
foreach(var blender in blenders) { foreach(var blender in blenders) {
string blender_composer= $"{blender}{composer}"; string blender_composer= $"{blender}{composer}";
#> #>
internal class <#= blender_composer#> : PixelBlender<TPixel> internal class <#= blender_composer#> : PixelBlender<TPixel>
{ {
/// <summary> /// <summary>
/// Gets the static instance of this blender. /// Gets the static instance of this blender.
/// </summary> /// </summary>
public static <#=blender_composer#> Instance { get; } = new <#=blender_composer#>(); public static <#=blender_composer#> Instance { get; } = new <#=blender_composer#>();
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount) public override TPixel Blend(TPixel background, TPixel source, float amount)
{ {
TPixel dest = default; TPixel dest = default;
dest.FromScaledVector4(PorterDuffFunctions.<#=blender_composer#>(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1))); dest.FromScaledVector4(PorterDuffFunctions.<#=blender_composer#>(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1)));
return dest; return dest;
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void BlendFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, ReadOnlySpan<Vector4> source, float amount) protected override void BlendFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, ReadOnlySpan<Vector4> source, float amount)
{ {
amount = amount.Clamp(0, 1); amount = amount.Clamp(0, 1);
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount); destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount);
} }
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void BlendFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, ReadOnlySpan<Vector4> source, ReadOnlySpan<float> amount) protected override void BlendFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, ReadOnlySpan<Vector4> source, ReadOnlySpan<float> amount)
{ {
for (int i = 0; i < destination.Length; i++) for (int i = 0; i < destination.Length; i++)
{ {
destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount[i].Clamp(0, 1)); destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount[i].Clamp(0, 1));
} }
} }
} }
<# <#
} }
} }
#> #>
} }
} }

4322
src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs

File diff suppressed because it is too large

364
src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt

@ -1,183 +1,183 @@
<# <#
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
#> #>
<#@ template debug="false" hostspecific="false" language="C#" #> <#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #> <#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #> <#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #> <#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #> <#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #> <#@ output extension=".cs" #>
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// <auto-generated /> // <auto-generated />
<# <#
// Note use of MethodImplOptions.NoInlining. We have tests that are failing on certain architectures when // Note use of MethodImplOptions.NoInlining. We have tests that are failing on certain architectures when
// AggresiveInlining is used. Confirmed on Intel i7-6600U in 64bit. // AggresiveInlining is used. Confirmed on Intel i7-6600U in 64bit.
#> #>
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{ {
internal static partial class PorterDuffFunctions internal static partial class PorterDuffFunctions
{ {
<# void GeneratePixelBlenders(string blender) { #> <# void GeneratePixelBlenders(string blender) { #>
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>Src(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>Src(Vector4 backdrop, Vector4 source, float opacity)
{ {
source.W *= opacity; source.W *= opacity;
return source; return source;
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>SrcAtop(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>SrcAtop(Vector4 backdrop, Vector4 source, float opacity)
{ {
source.W *= opacity; source.W *= opacity;
return Atop(backdrop, source, <#=blender#>(backdrop, source)); return Atop(backdrop, source, <#=blender#>(backdrop, source));
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>SrcOver(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>SrcOver(Vector4 backdrop, Vector4 source, float opacity)
{ {
source.W *= opacity; source.W *= opacity;
return Over(backdrop, source, <#=blender#>(backdrop, source)); return Over(backdrop, source, <#=blender#>(backdrop, source));
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>SrcIn(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>SrcIn(Vector4 backdrop, Vector4 source, float opacity)
{ {
source.W *= opacity; source.W *= opacity;
return In(backdrop, source, <#=blender#>(backdrop, source)); return In(backdrop, source, <#=blender#>(backdrop, source));
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>SrcOut(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>SrcOut(Vector4 backdrop, Vector4 source, float opacity)
{ {
source.W *= opacity; source.W *= opacity;
return Out(backdrop, source); return Out(backdrop, source);
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>Dest(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>Dest(Vector4 backdrop, Vector4 source, float opacity)
{ {
return backdrop; return backdrop;
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>DestAtop(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>DestAtop(Vector4 backdrop, Vector4 source, float opacity)
{ {
source.W *= opacity; source.W *= opacity;
return Atop(source, backdrop, <#=blender#>(source, backdrop)); return Atop(source, backdrop, <#=blender#>(source, backdrop));
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>DestOver(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>DestOver(Vector4 backdrop, Vector4 source, float opacity)
{ {
source.W *= opacity; source.W *= opacity;
return Over(source, backdrop, <#=blender#>(source, backdrop)); return Over(source, backdrop, <#=blender#>(source, backdrop));
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>DestIn(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>DestIn(Vector4 backdrop, Vector4 source, float opacity)
{ {
source.W *= opacity; source.W *= opacity;
return In(source, backdrop, <#=blender#>(source, backdrop)); return In(source, backdrop, <#=blender#>(source, backdrop));
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>DestOut(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>DestOut(Vector4 backdrop, Vector4 source, float opacity)
{ {
source.W *= opacity; source.W *= opacity;
return Out(source, backdrop); return Out(source, backdrop);
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>Xor(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>Xor(Vector4 backdrop, Vector4 source, float opacity)
{ {
source.W *= opacity; source.W *= opacity;
return Xor(backdrop, source); return Xor(backdrop, source);
} }
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>Clear(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>Clear(Vector4 backdrop, Vector4 source, float opacity)
{ {
source.W *= opacity; source.W *= opacity;
return Clear(backdrop, source); return Clear(backdrop, source);
} }
<# } #> <# } #>
<# void GenerateGenericPixelBlender(string blender, string composer) { #> <# void GenerateGenericPixelBlender(string blender, string composer) { #>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel <#=blender#><#=composer#><TPixel>(TPixel backdrop, TPixel source, float opacity) public static TPixel <#=blender#><#=composer#><TPixel>(TPixel backdrop, TPixel source, float opacity)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
opacity = opacity.Clamp(0, 1); opacity = opacity.Clamp(0, 1);
TPixel dest = default; TPixel dest = default;
dest.FromScaledVector4(<#=blender#><#=composer#>(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity)); dest.FromScaledVector4(<#=blender#><#=composer#>(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return dest; return dest;
} }
<# } #> <# } #>
<# <#
string[] composers = new []{ string[] composers = new []{
"Src", "Src",
"SrcAtop", "SrcAtop",
"SrcOver", "SrcOver",
"SrcIn", "SrcIn",
"SrcOut", "SrcOut",
"Dest", "Dest",
"DestAtop", "DestAtop",
"DestOver", "DestOver",
"DestIn", "DestIn",
"DestOut", "DestOut",
"Clear", "Clear",
"Xor", "Xor",
}; };
string[] blenders = new []{ string[] blenders = new []{
"Normal", "Normal",
"Multiply", "Multiply",
"Add", "Add",
"Subtract", "Subtract",
"Screen", "Screen",
"Darken", "Darken",
"Lighten", "Lighten",
"Overlay", "Overlay",
"HardLight" "HardLight"
}; };
foreach(var blender in blenders) foreach(var blender in blenders)
{ {
GeneratePixelBlenders(blender); GeneratePixelBlenders(blender);
foreach(var composer in composers) foreach(var composer in composers)
{ {
GenerateGenericPixelBlender(blender,composer); GenerateGenericPixelBlender(blender,composer);
} }
} }
#> #>
} }
} }

474
src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs

@ -1,238 +1,238 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{ {
/// <summary> /// <summary>
/// Collection of Porter Duff Color Blending and Alpha Composition Functions. /// Collection of Porter Duff Color Blending and Alpha Composition Functions.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// These functions are designed to be a general solution for all color cases, /// 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 /// 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 /// and source, and there's no need to alpha-premultiply neither the backdrop
/// nor the source. /// nor the source.
/// Note there are faster functions for when the backdrop color is known /// Note there are faster functions for when the backdrop color is known
/// to be opaque /// to be opaque
/// </remarks> /// </remarks>
internal static partial class PorterDuffFunctions internal static partial class PorterDuffFunctions
{ {
/// <summary> /// <summary>
/// Source over backdrop /// Source over backdrop
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">Backdrop color</param>
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Normal(Vector4 backdrop, Vector4 source) public static Vector4 Normal(Vector4 backdrop, Vector4 source)
{ {
return source; return source;
} }
/// <summary> /// <summary>
/// Source multiplied by backdrop /// Source multiplied by backdrop
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">Backdrop color</param>
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Multiply(Vector4 backdrop, Vector4 source) public static Vector4 Multiply(Vector4 backdrop, Vector4 source)
{ {
return backdrop * source; return backdrop * source;
} }
/// <summary> /// <summary>
/// Source added to backdrop /// Source added to backdrop
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">Backdrop color</param>
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Add(Vector4 backdrop, Vector4 source) public static Vector4 Add(Vector4 backdrop, Vector4 source)
{ {
return Vector4.Min(Vector4.One, backdrop + source); return Vector4.Min(Vector4.One, backdrop + source);
} }
/// <summary> /// <summary>
/// Source subtracted from backdrop /// Source subtracted from backdrop
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">Backdrop color</param>
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Subtract(Vector4 backdrop, Vector4 source) public static Vector4 Subtract(Vector4 backdrop, Vector4 source)
{ {
return Vector4.Max(Vector4.Zero, backdrop - source); return Vector4.Max(Vector4.Zero, backdrop - source);
} }
/// <summary> /// <summary>
/// Complement of source multiplied by the complement of backdrop /// Complement of source multiplied by the complement of backdrop
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">Backdrop color</param>
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Screen(Vector4 backdrop, Vector4 source) public static Vector4 Screen(Vector4 backdrop, Vector4 source)
{ {
return Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source)); return Vector4.One - ((Vector4.One - backdrop) * (Vector4.One - source));
} }
/// <summary> /// <summary>
/// Per element, chooses the smallest value of source and backdrop /// Per element, chooses the smallest value of source and backdrop
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">Backdrop color</param>
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Darken(Vector4 backdrop, Vector4 source) public static Vector4 Darken(Vector4 backdrop, Vector4 source)
{ {
return Vector4.Min(backdrop, source); return Vector4.Min(backdrop, source);
} }
/// <summary> /// <summary>
/// Per element, chooses the largest value of source and backdrop /// Per element, chooses the largest value of source and backdrop
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">Backdrop color</param>
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Lighten(Vector4 backdrop, Vector4 source) public static Vector4 Lighten(Vector4 backdrop, Vector4 source)
{ {
return Vector4.Max(backdrop, source); return Vector4.Max(backdrop, source);
} }
/// <summary> /// <summary>
/// Overlays source over backdrop /// Overlays source over backdrop
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">Backdrop color</param>
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Overlay(Vector4 backdrop, Vector4 source) public static Vector4 Overlay(Vector4 backdrop, Vector4 source)
{ {
float cr = OverlayValueFunction(backdrop.X, source.X); float cr = OverlayValueFunction(backdrop.X, source.X);
float cg = OverlayValueFunction(backdrop.Y, source.Y); float cg = OverlayValueFunction(backdrop.Y, source.Y);
float cb = OverlayValueFunction(backdrop.Z, source.Z); float cb = OverlayValueFunction(backdrop.Z, source.Z);
return Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0)); return Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0));
} }
/// <summary> /// <summary>
/// Hard light effect /// Hard light effect
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color</param> /// <param name="backdrop">Backdrop color</param>
/// <param name="source">Source color</param> /// <param name="source">Source color</param>
/// <returns>Output color</returns> /// <returns>Output color</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 HardLight(Vector4 backdrop, Vector4 source) public static Vector4 HardLight(Vector4 backdrop, Vector4 source)
{ {
float cr = OverlayValueFunction(source.X, backdrop.X); float cr = OverlayValueFunction(source.X, backdrop.X);
float cg = OverlayValueFunction(source.Y, backdrop.Y); float cg = OverlayValueFunction(source.Y, backdrop.Y);
float cb = OverlayValueFunction(source.Z, backdrop.Z); float cb = OverlayValueFunction(source.Z, backdrop.Z);
return Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0)); return Vector4.Min(Vector4.One, new Vector4(cr, cg, cb, 0));
} }
/// <summary> /// <summary>
/// Helper function for Overlay andHardLight modes /// Helper function for Overlay andHardLight modes
/// </summary> /// </summary>
/// <param name="backdrop">Backdrop color element</param> /// <param name="backdrop">Backdrop color element</param>
/// <param name="source">Source color element</param> /// <param name="source">Source color element</param>
/// <returns>Overlay value</returns> /// <returns>Overlay value</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float OverlayValueFunction(float backdrop, float source) private static float OverlayValueFunction(float backdrop, float source)
{ {
return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop)); return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop));
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Over(Vector4 dst, Vector4 src, Vector4 blend) public static Vector4 Over(Vector4 dst, Vector4 src, Vector4 blend)
{ {
// calculate weights // calculate weights
float blendW = dst.W * src.W; float blendW = dst.W * src.W;
float dstW = dst.W - blendW; float dstW = dst.W - blendW;
float srcW = src.W - blendW; float srcW = src.W - blendW;
// calculate final alpha // calculate final alpha
float alpha = dstW + srcW + blendW; float alpha = dstW + srcW + blendW;
// calculate final color // calculate final color
Vector4 color = (dst * dstW) + (src * srcW) + (blend * blendW); Vector4 color = (dst * dstW) + (src * srcW) + (blend * blendW);
// unpremultiply // unpremultiply
color /= MathF.Max(alpha, Constants.Epsilon); color /= MathF.Max(alpha, Constants.Epsilon);
color.W = alpha; color.W = alpha;
return color; return color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Atop(Vector4 dst, Vector4 src, Vector4 blend) public static Vector4 Atop(Vector4 dst, Vector4 src, Vector4 blend)
{ {
// calculate weights // calculate weights
float blendW = dst.W * src.W; float blendW = dst.W * src.W;
float dstW = dst.W - blendW; float dstW = dst.W - blendW;
// calculate final alpha // calculate final alpha
float alpha = dstW + blendW; float alpha = dstW + blendW;
// calculate final color // calculate final color
Vector4 color = (dst * dstW) + (blend * blendW); Vector4 color = (dst * dstW) + (blend * blendW);
// unpremultiply // unpremultiply
color /= MathF.Max(alpha, Constants.Epsilon); color /= MathF.Max(alpha, Constants.Epsilon);
color.W = alpha; color.W = alpha;
return color; return color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 In(Vector4 dst, Vector4 src, Vector4 blend) public static Vector4 In(Vector4 dst, Vector4 src, Vector4 blend)
{ {
float alpha = dst.W * src.W; float alpha = dst.W * src.W;
Vector4 color = src * alpha; // premultiply Vector4 color = src * alpha; // premultiply
color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply
color.W = alpha; color.W = alpha;
return color; return color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Out(Vector4 dst, Vector4 src) public static Vector4 Out(Vector4 dst, Vector4 src)
{ {
float alpha = (1 - dst.W) * src.W; float alpha = (1 - dst.W) * src.W;
Vector4 color = src * alpha; // premultiply Vector4 color = src * alpha; // premultiply
color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply color /= MathF.Max(alpha, Constants.Epsilon); // unpremultiply
color.W = alpha; color.W = alpha;
return color; return color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Xor(Vector4 dst, Vector4 src) public static Vector4 Xor(Vector4 dst, Vector4 src)
{ {
float srcW = 1 - dst.W; float srcW = 1 - dst.W;
float dstW = 1 - src.W; float dstW = 1 - src.W;
float alpha = (src.W * srcW) + (dst.W * dstW); float alpha = (src.W * srcW) + (dst.W * dstW);
Vector4 color = (src.W * src * srcW) + (dst.W * dst * dstW); Vector4 color = (src.W * src * srcW) + (dst.W * dst * dstW);
// unpremultiply // unpremultiply
color /= MathF.Max(alpha, Constants.Epsilon); color /= MathF.Max(alpha, Constants.Epsilon);
color.W = alpha; color.W = alpha;
return color; return color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 Clear(Vector4 backdrop, Vector4 source) private static Vector4 Clear(Vector4 backdrop, Vector4 source)
{ {
return Vector4.Zero; return Vector4.Zero;
} }
} }
} }

338
src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs

@ -1,170 +1,170 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers; using System.Buffers;
using System.Numerics; using System.Numerics;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.PixelFormats namespace SixLabors.ImageSharp.PixelFormats
{ {
/// <summary> /// <summary>
/// Abstract base class for calling pixel composition functions /// Abstract base class for calling pixel composition functions
/// </summary> /// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam> /// <typeparam name="TPixel">The type of the pixel</typeparam>
internal abstract class PixelBlender<TPixel> internal abstract class PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary> /// <summary>
/// Blend 2 pixels together. /// Blend 2 pixels together.
/// </summary> /// </summary>
/// <param name="background">The background color.</param> /// <param name="background">The background color.</param>
/// <param name="source">The source color.</param> /// <param name="source">The source color.</param>
/// <param name="amount"> /// <param name="amount">
/// A value between 0 and 1 indicating the weight of the second source vector. /// A value between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param> /// </param>
/// <returns>The final pixel value after composition</returns> /// <returns>The final pixel value after composition</returns>
public abstract TPixel Blend(TPixel background, TPixel source, float amount); public abstract TPixel Blend(TPixel background, TPixel source, float amount);
/// <summary> /// <summary>
/// Blend 2 rows together. /// Blend 2 rows together.
/// </summary> /// </summary>
/// <param name="destination">destination span</param> /// <param name="destination">destination span</param>
/// <param name="background">the background span</param> /// <param name="background">the background span</param>
/// <param name="source">the source span</param> /// <param name="source">the source span</param>
/// <param name="amount"> /// <param name="amount">
/// A value between 0 and 1 indicating the weight of the second source vector. /// A value between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param> /// </param>
protected abstract void BlendFunction( protected abstract void BlendFunction(
Span<Vector4> destination, Span<Vector4> destination,
ReadOnlySpan<Vector4> background, ReadOnlySpan<Vector4> background,
ReadOnlySpan<Vector4> source, ReadOnlySpan<Vector4> source,
float amount); float amount);
/// <summary> /// <summary>
/// Blend 2 rows together. /// Blend 2 rows together.
/// </summary> /// </summary>
/// <param name="destination">destination span</param> /// <param name="destination">destination span</param>
/// <param name="background">the background span</param> /// <param name="background">the background span</param>
/// <param name="source">the source span</param> /// <param name="source">the source span</param>
/// <param name="amount"> /// <param name="amount">
/// A span with values between 0 and 1 indicating the weight of the second source vector. /// 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. /// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param> /// </param>
protected abstract void BlendFunction( protected abstract void BlendFunction(
Span<Vector4> destination, Span<Vector4> destination,
ReadOnlySpan<Vector4> background, ReadOnlySpan<Vector4> background,
ReadOnlySpan<Vector4> source, ReadOnlySpan<Vector4> source,
ReadOnlySpan<float> amount); ReadOnlySpan<float> amount);
/// <summary> /// <summary>
/// Blends 2 rows together /// Blends 2 rows together
/// </summary> /// </summary>
/// <param name="configuration"><see cref="Configuration"/> to use internally</param> /// <param name="configuration"><see cref="Configuration"/> to use internally</param>
/// <param name="destination">the destination span</param> /// <param name="destination">the destination span</param>
/// <param name="background">the background span</param> /// <param name="background">the background span</param>
/// <param name="source">the source span</param> /// <param name="source">the source span</param>
/// <param name="amount"> /// <param name="amount">
/// A span with values between 0 and 1 indicating the weight of the second source vector. /// 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. /// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param> /// </param>
public void Blend( public void Blend(
Configuration configuration, Configuration configuration,
Span<TPixel> destination, Span<TPixel> destination,
ReadOnlySpan<TPixel> background, ReadOnlySpan<TPixel> background,
ReadOnlySpan<TPixel> source, ReadOnlySpan<TPixel> source,
ReadOnlySpan<float> amount) ReadOnlySpan<float> amount)
{ {
this.Blend<TPixel>(configuration, destination, background, source, amount); this.Blend<TPixel>(configuration, destination, background, source, amount);
} }
/// <summary> /// <summary>
/// Blends 2 rows together /// Blends 2 rows together
/// </summary> /// </summary>
/// <typeparam name="TPixelSrc">the pixel format of the source span</typeparam> /// <typeparam name="TPixelSrc">the pixel format of the source span</typeparam>
/// <param name="configuration"><see cref="Configuration"/> to use internally</param> /// <param name="configuration"><see cref="Configuration"/> to use internally</param>
/// <param name="destination">the destination span</param> /// <param name="destination">the destination span</param>
/// <param name="background">the background span</param> /// <param name="background">the background span</param>
/// <param name="source">the source span</param> /// <param name="source">the source span</param>
/// <param name="amount"> /// <param name="amount">
/// A span with values between 0 and 1 indicating the weight of the second source vector. /// 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. /// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param> /// </param>
public void Blend<TPixelSrc>( public void Blend<TPixelSrc>(
Configuration configuration, Configuration configuration,
Span<TPixel> destination, Span<TPixel> destination,
ReadOnlySpan<TPixel> background, ReadOnlySpan<TPixel> background,
ReadOnlySpan<TPixelSrc> source, ReadOnlySpan<TPixelSrc> source,
ReadOnlySpan<float> amount) ReadOnlySpan<float> amount)
where TPixelSrc : struct, IPixel<TPixelSrc> where TPixelSrc : struct, IPixel<TPixelSrc>
{ {
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));
Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length)); Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length));
using (IMemoryOwner<Vector4> buffer = using (IMemoryOwner<Vector4> buffer =
configuration.MemoryAllocator.Allocate<Vector4>(destination.Length * 3)) configuration.MemoryAllocator.Allocate<Vector4>(destination.Length * 3))
{ {
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length); Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> backgroundSpan = buffer.Slice(destination.Length, destination.Length); Span<Vector4> backgroundSpan = buffer.Slice(destination.Length, destination.Length);
Span<Vector4> sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); Span<Vector4> sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
ReadOnlySpan<TPixel> sourcePixels = background.Slice(0, background.Length); ReadOnlySpan<TPixel> sourcePixels = background.Slice(0, background.Length);
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale); PixelOperations<TPixel>.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale);
ReadOnlySpan<TPixelSrc> sourcePixels1 = source.Slice(0, background.Length); ReadOnlySpan<TPixelSrc> sourcePixels1 = source.Slice(0, background.Length);
PixelOperations<TPixelSrc>.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale); PixelOperations<TPixelSrc>.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale);
this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount); this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount);
Span<Vector4> sourceVectors = destinationSpan.Slice(0, background.Length); Span<Vector4> sourceVectors = destinationSpan.Slice(0, background.Length);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale);
} }
} }
/// <summary> /// <summary>
/// Blends 2 rows together /// Blends 2 rows together
/// </summary> /// </summary>
/// <typeparam name="TPixelSrc">the pixel format of the source span</typeparam> /// <typeparam name="TPixelSrc">the pixel format of the source span</typeparam>
/// <param name="configuration"><see cref="Configuration"/> to use internally</param> /// <param name="configuration"><see cref="Configuration"/> to use internally</param>
/// <param name="destination">the destination span</param> /// <param name="destination">the destination span</param>
/// <param name="background">the background span</param> /// <param name="background">the background span</param>
/// <param name="source">the source span</param> /// <param name="source">the source span</param>
/// <param name="amount"> /// <param name="amount">
/// A value between 0 and 1 indicating the weight of the second source vector. /// A value between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param> /// </param>
public void Blend<TPixelSrc>( public void Blend<TPixelSrc>(
Configuration configuration, Configuration configuration,
Span<TPixel> destination, Span<TPixel> destination,
ReadOnlySpan<TPixel> background, ReadOnlySpan<TPixel> background,
ReadOnlySpan<TPixelSrc> source, ReadOnlySpan<TPixelSrc> source,
float amount) float amount)
where TPixelSrc : struct, IPixel<TPixelSrc> where TPixelSrc : struct, IPixel<TPixelSrc>
{ {
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length)); Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount)); Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
using (IMemoryOwner<Vector4> buffer = using (IMemoryOwner<Vector4> buffer =
configuration.MemoryAllocator.Allocate<Vector4>(destination.Length * 3)) configuration.MemoryAllocator.Allocate<Vector4>(destination.Length * 3))
{ {
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length); Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> backgroundSpan = buffer.Slice(destination.Length, destination.Length); Span<Vector4> backgroundSpan = buffer.Slice(destination.Length, destination.Length);
Span<Vector4> sourceSpan = buffer.Slice(destination.Length * 2, destination.Length); Span<Vector4> sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
ReadOnlySpan<TPixel> sourcePixels = background.Slice(0, background.Length); ReadOnlySpan<TPixel> sourcePixels = background.Slice(0, background.Length);
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale); PixelOperations<TPixel>.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale);
ReadOnlySpan<TPixelSrc> sourcePixels1 = source.Slice(0, background.Length); ReadOnlySpan<TPixelSrc> sourcePixels1 = source.Slice(0, background.Length);
PixelOperations<TPixelSrc>.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale); PixelOperations<TPixelSrc>.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale);
this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount); this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount);
Span<Vector4> sourceVectors = destinationSpan.Slice(0, background.Length); Span<Vector4> sourceVectors = destinationSpan.Slice(0, background.Length);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale); PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale);
} }
} }
} }
} }

2
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Argb32.PixelOperations.Generated.cs

@ -11,7 +11,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.PixelFormats namespace SixLabors.ImageSharp.PixelFormats
{ {
/// <content> /// <content>
@ -231,7 +230,6 @@ namespace SixLabors.ImageSharp.PixelFormats
{ {
PixelOperations<TSourcePixel>.Instance.ToArgb32(configuration, sourcePixels, destinationPixels); PixelOperations<TSourcePixel>.Instance.ToArgb32(configuration, sourcePixels, destinationPixels);
} }
} }
} }
} }

2
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Bgra32.PixelOperations.Generated.cs

@ -11,7 +11,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.PixelFormats namespace SixLabors.ImageSharp.PixelFormats
{ {
/// <content> /// <content>
@ -231,7 +230,6 @@ namespace SixLabors.ImageSharp.PixelFormats
{ {
PixelOperations<TSourcePixel>.Instance.ToBgra32(configuration, sourcePixels, destinationPixels); PixelOperations<TSourcePixel>.Instance.ToBgra32(configuration, sourcePixels, destinationPixels);
} }
} }
} }
} }

2
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray16.PixelOperations.Generated.cs

@ -11,7 +11,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.PixelFormats namespace SixLabors.ImageSharp.PixelFormats
{ {
/// <content> /// <content>
@ -194,7 +193,6 @@ namespace SixLabors.ImageSharp.PixelFormats
{ {
PixelOperations<TSourcePixel>.Instance.ToGray16(configuration, sourcePixels, destinationPixels); PixelOperations<TSourcePixel>.Instance.ToGray16(configuration, sourcePixels, destinationPixels);
} }
} }
} }
} }

2
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Gray8.PixelOperations.Generated.cs

@ -11,7 +11,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.PixelFormats namespace SixLabors.ImageSharp.PixelFormats
{ {
/// <content> /// <content>
@ -194,7 +193,6 @@ namespace SixLabors.ImageSharp.PixelFormats
{ {
PixelOperations<TSourcePixel>.Instance.ToGray8(configuration, sourcePixels, destinationPixels); PixelOperations<TSourcePixel>.Instance.ToGray8(configuration, sourcePixels, destinationPixels);
} }
} }
} }
} }

2
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb24.PixelOperations.Generated.cs

@ -11,7 +11,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.PixelFormats namespace SixLabors.ImageSharp.PixelFormats
{ {
/// <content> /// <content>
@ -205,7 +204,6 @@ namespace SixLabors.ImageSharp.PixelFormats
{ {
PixelOperations<TSourcePixel>.Instance.ToRgb24(configuration, sourcePixels, destinationPixels); PixelOperations<TSourcePixel>.Instance.ToRgb24(configuration, sourcePixels, destinationPixels);
} }
} }
} }
} }

2
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgb48.PixelOperations.Generated.cs

@ -11,7 +11,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.PixelFormats namespace SixLabors.ImageSharp.PixelFormats
{ {
/// <content> /// <content>
@ -194,7 +193,6 @@ namespace SixLabors.ImageSharp.PixelFormats
{ {
PixelOperations<TSourcePixel>.Instance.ToRgb48(configuration, sourcePixels, destinationPixels); PixelOperations<TSourcePixel>.Instance.ToRgb48(configuration, sourcePixels, destinationPixels);
} }
} }
} }
} }

2
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba32.PixelOperations.Generated.cs

@ -11,7 +11,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.PixelFormats namespace SixLabors.ImageSharp.PixelFormats
{ {
/// <content> /// <content>
@ -220,7 +219,6 @@ namespace SixLabors.ImageSharp.PixelFormats
{ {
PixelOperations<TSourcePixel>.Instance.ToRgba32(configuration, sourcePixels, destinationPixels); PixelOperations<TSourcePixel>.Instance.ToRgba32(configuration, sourcePixels, destinationPixels);
} }
} }
} }
} }

2
src/ImageSharp/PixelFormats/PixelImplementations/Generated/Rgba64.PixelOperations.Generated.cs

@ -11,7 +11,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.PixelFormats namespace SixLabors.ImageSharp.PixelFormats
{ {
/// <content> /// <content>
@ -194,7 +193,6 @@ namespace SixLabors.ImageSharp.PixelFormats
{ {
PixelOperations<TSourcePixel>.Instance.ToRgba64(configuration, sourcePixels, destinationPixels); PixelOperations<TSourcePixel>.Instance.ToRgba64(configuration, sourcePixels, destinationPixels);
} }
} }
} }
} }

432
src/ImageSharp/PixelFormats/PixelOperations{TPixel}.PixelBenders.cs

@ -1,217 +1,217 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats.PixelBlenders; using SixLabors.ImageSharp.PixelFormats.PixelBlenders;
namespace SixLabors.ImageSharp.PixelFormats namespace SixLabors.ImageSharp.PixelFormats
{ {
/// <content> /// <content>
/// Provides access to pixel blenders /// Provides access to pixel blenders
/// </content> /// </content>
public partial class PixelOperations<TPixel> public partial class PixelOperations<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
/// <summary> /// <summary>
/// Find an instance of the pixel blender. /// Find an instance of the pixel blender.
/// </summary> /// </summary>
/// <param name="options">the blending and composition to apply</param> /// <param name="options">the blending and composition to apply</param>
/// <returns>A <see cref="PixelBlender{TPixel}"/>.</returns> /// <returns>A <see cref="PixelBlender{TPixel}"/>.</returns>
internal PixelBlender<TPixel> GetPixelBlender(GraphicsOptions options) internal PixelBlender<TPixel> GetPixelBlender(GraphicsOptions options)
{ {
return this.GetPixelBlender(options.ColorBlendingMode, options.AlphaCompositionMode); return this.GetPixelBlender(options.ColorBlendingMode, options.AlphaCompositionMode);
} }
/// <summary> /// <summary>
/// Find an instance of the pixel blender. /// Find an instance of the pixel blender.
/// </summary> /// </summary>
/// <param name="colorMode">The color blending mode to apply</param> /// <param name="colorMode">The color blending mode to apply</param>
/// <param name="alphaMode">The alpha composition mode to apply</param> /// <param name="alphaMode">The alpha composition mode to apply</param>
/// <returns>A <see cref="PixelBlender{TPixel}"/>.</returns> /// <returns>A <see cref="PixelBlender{TPixel}"/>.</returns>
internal virtual PixelBlender<TPixel> GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode) internal virtual PixelBlender<TPixel> GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode)
{ {
switch (alphaMode) switch (alphaMode)
{ {
case PixelAlphaCompositionMode.Clear: case PixelAlphaCompositionMode.Clear:
switch (colorMode) switch (colorMode)
{ {
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyClear.Instance; case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyClear.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddClear.Instance; case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddClear.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractClear.Instance; case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractClear.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenClear.Instance; case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenClear.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenClear.Instance; case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenClear.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenClear.Instance; case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenClear.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayClear.Instance; case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayClear.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightClear.Instance; case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightClear.Instance;
case PixelColorBlendingMode.Normal: case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalClear.Instance; default: return DefaultPixelBlenders<TPixel>.NormalClear.Instance;
} }
case PixelAlphaCompositionMode.Xor: case PixelAlphaCompositionMode.Xor:
switch (colorMode) switch (colorMode)
{ {
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyXor.Instance; case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyXor.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddXor.Instance; case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddXor.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractXor.Instance; case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractXor.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenXor.Instance; case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenXor.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenXor.Instance; case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenXor.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenXor.Instance; case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenXor.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayXor.Instance; case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayXor.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightXor.Instance; case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightXor.Instance;
case PixelColorBlendingMode.Normal: case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalXor.Instance; default: return DefaultPixelBlenders<TPixel>.NormalXor.Instance;
} }
case PixelAlphaCompositionMode.Src: case PixelAlphaCompositionMode.Src:
switch (colorMode) switch (colorMode)
{ {
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrc.Instance; case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrc.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrc.Instance; case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrc.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrc.Instance; case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrc.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrc.Instance; case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrc.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrc.Instance; case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrc.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrc.Instance; case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrc.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrc.Instance; case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrc.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrc.Instance; case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrc.Instance;
case PixelColorBlendingMode.Normal: case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrc.Instance; default: return DefaultPixelBlenders<TPixel>.NormalSrc.Instance;
} }
case PixelAlphaCompositionMode.SrcAtop: case PixelAlphaCompositionMode.SrcAtop:
switch (colorMode) switch (colorMode)
{ {
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcAtop.Instance; case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcAtop.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcAtop.Instance; case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcAtop.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcAtop.Instance; case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcAtop.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcAtop.Instance; case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcAtop.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcAtop.Instance; case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcAtop.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcAtop.Instance; case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcAtop.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcAtop.Instance; case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcAtop.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcAtop.Instance; case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcAtop.Instance;
case PixelColorBlendingMode.Normal: case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcAtop.Instance; default: return DefaultPixelBlenders<TPixel>.NormalSrcAtop.Instance;
} }
case PixelAlphaCompositionMode.SrcIn: case PixelAlphaCompositionMode.SrcIn:
switch (colorMode) switch (colorMode)
{ {
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcIn.Instance; case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcIn.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcIn.Instance; case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcIn.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcIn.Instance; case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcIn.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcIn.Instance; case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcIn.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcIn.Instance; case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcIn.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcIn.Instance; case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcIn.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcIn.Instance; case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcIn.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcIn.Instance; case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcIn.Instance;
case PixelColorBlendingMode.Normal: case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcIn.Instance; default: return DefaultPixelBlenders<TPixel>.NormalSrcIn.Instance;
} }
case PixelAlphaCompositionMode.SrcOut: case PixelAlphaCompositionMode.SrcOut:
switch (colorMode) switch (colorMode)
{ {
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcOut.Instance; case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcOut.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcOut.Instance; case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcOut.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcOut.Instance; case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcOut.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcOut.Instance; case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcOut.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcOut.Instance; case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcOut.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcOut.Instance; case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcOut.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcOut.Instance; case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcOut.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcOut.Instance; case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcOut.Instance;
case PixelColorBlendingMode.Normal: case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcOut.Instance; default: return DefaultPixelBlenders<TPixel>.NormalSrcOut.Instance;
} }
case PixelAlphaCompositionMode.Dest: case PixelAlphaCompositionMode.Dest:
switch (colorMode) switch (colorMode)
{ {
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDest.Instance; case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDest.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDest.Instance; case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDest.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDest.Instance; case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDest.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDest.Instance; case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDest.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDest.Instance; case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDest.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDest.Instance; case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDest.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDest.Instance; case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDest.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDest.Instance; case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDest.Instance;
case PixelColorBlendingMode.Normal: case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDest.Instance; default: return DefaultPixelBlenders<TPixel>.NormalDest.Instance;
} }
case PixelAlphaCompositionMode.DestAtop: case PixelAlphaCompositionMode.DestAtop:
switch (colorMode) switch (colorMode)
{ {
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestAtop.Instance; case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestAtop.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestAtop.Instance; case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestAtop.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestAtop.Instance; case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestAtop.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestAtop.Instance; case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestAtop.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestAtop.Instance; case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestAtop.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestAtop.Instance; case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestAtop.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestAtop.Instance; case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestAtop.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestAtop.Instance; case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestAtop.Instance;
case PixelColorBlendingMode.Normal: case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestAtop.Instance; default: return DefaultPixelBlenders<TPixel>.NormalDestAtop.Instance;
} }
case PixelAlphaCompositionMode.DestIn: case PixelAlphaCompositionMode.DestIn:
switch (colorMode) switch (colorMode)
{ {
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestIn.Instance; case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestIn.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestIn.Instance; case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestIn.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestIn.Instance; case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestIn.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestIn.Instance; case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestIn.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestIn.Instance; case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestIn.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestIn.Instance; case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestIn.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestIn.Instance; case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestIn.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestIn.Instance; case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestIn.Instance;
case PixelColorBlendingMode.Normal: case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestIn.Instance; default: return DefaultPixelBlenders<TPixel>.NormalDestIn.Instance;
} }
case PixelAlphaCompositionMode.DestOut: case PixelAlphaCompositionMode.DestOut:
switch (colorMode) switch (colorMode)
{ {
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestOut.Instance; case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestOut.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestOut.Instance; case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestOut.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestOut.Instance; case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestOut.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestOut.Instance; case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestOut.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestOut.Instance; case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestOut.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestOut.Instance; case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestOut.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestOut.Instance; case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestOut.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestOut.Instance; case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestOut.Instance;
case PixelColorBlendingMode.Normal: case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestOut.Instance; default: return DefaultPixelBlenders<TPixel>.NormalDestOut.Instance;
} }
case PixelAlphaCompositionMode.DestOver: case PixelAlphaCompositionMode.DestOver:
switch (colorMode) switch (colorMode)
{ {
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestOver.Instance; case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestOver.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestOver.Instance; case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestOver.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestOver.Instance; case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestOver.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestOver.Instance; case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestOver.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestOver.Instance; case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestOver.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestOver.Instance; case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestOver.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestOver.Instance; case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestOver.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestOver.Instance; case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestOver.Instance;
case PixelColorBlendingMode.Normal: case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestOver.Instance; default: return DefaultPixelBlenders<TPixel>.NormalDestOver.Instance;
} }
case PixelAlphaCompositionMode.SrcOver: case PixelAlphaCompositionMode.SrcOver:
default: default:
switch (colorMode) switch (colorMode)
{ {
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcOver.Instance; case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcOver.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcOver.Instance; case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcOver.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcOver.Instance; case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcOver.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcOver.Instance; case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcOver.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcOver.Instance; case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcOver.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcOver.Instance; case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcOver.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcOver.Instance; case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcOver.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcOver.Instance; case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcOver.Instance;
case PixelColorBlendingMode.Normal: case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcOver.Instance; default: return DefaultPixelBlenders<TPixel>.NormalSrcOver.Instance;
} }
} }
} }
} }
} }

352
tests/ImageSharp.Tests/Drawing/SolidFillBlendedShapesTests.cs

@ -1,177 +1,177 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.Primitives; using SixLabors.Primitives;
using Xunit; using Xunit;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Drawing namespace SixLabors.ImageSharp.Tests.Drawing
{ {
[GroupOutput("Drawing")] [GroupOutput("Drawing")]
public class SolidFillBlendedShapesTests public class SolidFillBlendedShapesTests
{ {
public static IEnumerable<object[]> modes = GetAllModeCombinations(); public static IEnumerable<object[]> modes = GetAllModeCombinations();
private static IEnumerable<object[]> GetAllModeCombinations() private static IEnumerable<object[]> GetAllModeCombinations()
{ {
foreach (var composition in Enum.GetValues(typeof(PixelAlphaCompositionMode))) foreach (var composition in Enum.GetValues(typeof(PixelAlphaCompositionMode)))
{ {
foreach (var blending in Enum.GetValues(typeof(PixelColorBlendingMode))) foreach (var blending in Enum.GetValues(typeof(PixelColorBlendingMode)))
{ {
yield return new object[] { blending, composition }; yield return new object[] { blending, composition };
} }
} }
} }
[Theory] [Theory]
[WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)]
public void _1DarkBlueRect_2BlendHotPinkRect<TPixel>( public void _1DarkBlueRect_2BlendHotPinkRect<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
PixelColorBlendingMode blending, PixelColorBlendingMode blending,
PixelAlphaCompositionMode composition) PixelAlphaCompositionMode composition)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
using (Image<TPixel> img = provider.GetImage()) using (Image<TPixel> img = provider.GetImage())
{ {
int scaleX = img.Width / 100; int scaleX = img.Width / 100;
int scaleY = img.Height / 100; int scaleY = img.Height / 100;
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
Color.DarkBlue, Color.DarkBlue,
new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY) new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)
) )
.Fill(new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode=composition }, .Fill(new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode=composition },
Color.HotPink, Color.HotPink,
new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)) new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY))
); );
VerifyImage(provider, blending, composition, img); VerifyImage(provider, blending, composition, img);
} }
} }
[Theory] [Theory]
[WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)]
public void _1DarkBlueRect_2BlendHotPinkRect_3BlendTransparentEllipse<TPixel>( public void _1DarkBlueRect_2BlendHotPinkRect_3BlendTransparentEllipse<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
PixelColorBlendingMode blending, PixelColorBlendingMode blending,
PixelAlphaCompositionMode composition) PixelAlphaCompositionMode composition)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
using (Image<TPixel> img = provider.GetImage()) using (Image<TPixel> img = provider.GetImage())
{ {
int scaleX = img.Width / 100; int scaleX = img.Width / 100;
int scaleY = img.Height / 100; int scaleY = img.Height / 100;
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
Color.DarkBlue, Color.DarkBlue,
new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)));
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition },
Color.HotPink, Color.HotPink,
new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY))); new Rectangle(20 * scaleX, 0 * scaleY, 30 * scaleX, 100 * scaleY)));
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition },
Color.Transparent, Color.Transparent,
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))
); );
VerifyImage(provider, blending, composition, img); VerifyImage(provider, blending, composition, img);
} }
} }
[Theory] [Theory]
[WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)]
public void _1DarkBlueRect_2BlendHotPinkRect_3BlendSemiTransparentRedEllipse<TPixel>( public void _1DarkBlueRect_2BlendHotPinkRect_3BlendSemiTransparentRedEllipse<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
PixelColorBlendingMode blending, PixelColorBlendingMode blending,
PixelAlphaCompositionMode composition) PixelAlphaCompositionMode composition)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
using (Image<TPixel> img = provider.GetImage()) using (Image<TPixel> img = provider.GetImage())
{ {
int scaleX = (img.Width / 100); int scaleX = (img.Width / 100);
int scaleY = (img.Height / 100); int scaleY = (img.Height / 100);
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
Color.DarkBlue, Color.DarkBlue,
new Rectangle(0 * scaleX, 40, 100 * scaleX, 20 * scaleY))); new Rectangle(0 * scaleX, 40, 100 * scaleX, 20 * scaleY)));
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition },
Color.HotPink, Color.HotPink,
new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY))); new Rectangle(20 * scaleX, 0, 30 * scaleX, 100 * scaleY)));
var transparentRed = Color.Red.WithAlpha(0.5f); var transparentRed = Color.Red.WithAlpha(0.5f);
img.Mutate( img.Mutate(
x => x.Fill( x => x.Fill(
new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }, new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition },
transparentRed, transparentRed,
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)) new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))
); );
VerifyImage(provider, blending, composition, img); ; VerifyImage(provider, blending, composition, img); ;
} }
} }
[Theory] [Theory]
[WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)] [WithBlankImages(nameof(modes), 250, 250, PixelTypes.Rgba32)]
public void _1DarkBlueRect_2BlendBlackEllipse<TPixel>( public void _1DarkBlueRect_2BlendBlackEllipse<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
PixelColorBlendingMode blending, PixelColorBlendingMode blending,
PixelAlphaCompositionMode composition) PixelAlphaCompositionMode composition)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
using(Image<TPixel> dstImg = provider.GetImage(), srcImg = provider.GetImage()) using(Image<TPixel> dstImg = provider.GetImage(), srcImg = provider.GetImage())
{ {
int scaleX = (dstImg.Width / 100); int scaleX = (dstImg.Width / 100);
int scaleY = (dstImg.Height / 100); int scaleY = (dstImg.Height / 100);
dstImg.Mutate( dstImg.Mutate(
x => x.Fill( x => x.Fill(
Color.DarkBlue, Color.DarkBlue,
new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY))); new Rectangle(0 * scaleX, 40 * scaleY, 100 * scaleX, 20 * scaleY)));
srcImg.Mutate( srcImg.Mutate(
x => x.Fill( x => x.Fill(
Color.Black, Color.Black,
new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY))); new Shapes.EllipsePolygon(40 * scaleX, 50 * scaleY, 50 * scaleX, 50 * scaleY)));
dstImg.Mutate( dstImg.Mutate(
x => x.DrawImage(srcImg, new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition }) x => x.DrawImage(srcImg, new GraphicsOptions(true) { ColorBlendingMode = blending, AlphaCompositionMode = composition })
); );
VerifyImage(provider, blending, composition, dstImg); VerifyImage(provider, blending, composition, dstImg);
} }
} }
private static void VerifyImage<TPixel>( private static void VerifyImage<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
PixelColorBlendingMode blending, PixelColorBlendingMode blending,
PixelAlphaCompositionMode composition, PixelAlphaCompositionMode composition,
Image<TPixel> img) Image<TPixel> img)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
img.DebugSave( img.DebugSave(
provider, provider,
new { composition, blending }, new { composition, blending },
appendPixelTypeToFileName: false, appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false); appendSourceFileOrDescription: false);
var comparer = ImageComparer.TolerantPercentage(0.01f, 3); var comparer = ImageComparer.TolerantPercentage(0.01f, 3);
img.CompareFirstFrameToReferenceOutput(comparer, img.CompareFirstFrameToReferenceOutput(comparer,
provider, provider,
new { composition, blending }, new { composition, blending },
appendPixelTypeToFileName: false, appendPixelTypeToFileName: false,
appendSourceFileOrDescription: false); appendSourceFileOrDescription: false);
} }
} }
} }

292
tests/ImageSharp.Tests/Formats/ImageFormatManagerTests.cs

@ -1,146 +1,146 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Moq; using Moq;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using Xunit; using Xunit;
namespace SixLabors.ImageSharp.Tests namespace SixLabors.ImageSharp.Tests
{ {
public class ImageFormatManagerTests public class ImageFormatManagerTests
{ {
public ImageFormatManager FormatsManagerEmpty { get; } public ImageFormatManager FormatsManagerEmpty { get; }
public ImageFormatManager DefaultFormatsManager { get; } public ImageFormatManager DefaultFormatsManager { get; }
public ImageFormatManagerTests() public ImageFormatManagerTests()
{ {
this.DefaultFormatsManager = Configuration.CreateDefaultInstance().ImageFormatsManager; this.DefaultFormatsManager = Configuration.CreateDefaultInstance().ImageFormatsManager;
this.FormatsManagerEmpty = new ImageFormatManager(); this.FormatsManagerEmpty = new ImageFormatManager();
} }
[Fact] [Fact]
public void IfAutoloadWellKnownFormatsIsTrueAllFormatsAreLoaded() public void IfAutoloadWellKnownFormatsIsTrueAllFormatsAreLoaded()
{ {
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<PngEncoder>().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<PngEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<BmpEncoder>().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<BmpEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<JpegEncoder>().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<JpegEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<GifEncoder>().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageEncoders.Select(item => item.Value).OfType<GifEncoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<PngDecoder>().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<PngDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<BmpDecoder>().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<BmpDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<JpegDecoder>().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<JpegDecoder>().Count());
Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<BmpDecoder>().Count()); Assert.Equal(1, this.DefaultFormatsManager.ImageDecoders.Select(item => item.Value).OfType<BmpDecoder>().Count());
} }
[Fact] [Fact]
public void AddImageFormatDetectorNullthrows() public void AddImageFormatDetectorNullthrows()
{ {
Assert.Throws<ArgumentNullException>(() => Assert.Throws<ArgumentNullException>(() =>
{ {
this.DefaultFormatsManager.AddImageFormatDetector(null); this.DefaultFormatsManager.AddImageFormatDetector(null);
}); });
} }
[Fact] [Fact]
public void RegisterNullMimeTypeEncoder() public void RegisterNullMimeTypeEncoder()
{ {
Assert.Throws<ArgumentNullException>(() => Assert.Throws<ArgumentNullException>(() =>
{ {
this.DefaultFormatsManager.SetEncoder(null, new Mock<IImageEncoder>().Object); this.DefaultFormatsManager.SetEncoder(null, new Mock<IImageEncoder>().Object);
}); });
Assert.Throws<ArgumentNullException>(() => Assert.Throws<ArgumentNullException>(() =>
{ {
this.DefaultFormatsManager.SetEncoder(BmpFormat.Instance, null); this.DefaultFormatsManager.SetEncoder(BmpFormat.Instance, null);
}); });
Assert.Throws<ArgumentNullException>(() => Assert.Throws<ArgumentNullException>(() =>
{ {
this.DefaultFormatsManager.SetEncoder(null, null); this.DefaultFormatsManager.SetEncoder(null, null);
}); });
} }
[Fact] [Fact]
public void RegisterNullSetDecoder() public void RegisterNullSetDecoder()
{ {
Assert.Throws<ArgumentNullException>(() => Assert.Throws<ArgumentNullException>(() =>
{ {
this.DefaultFormatsManager.SetDecoder(null, new Mock<IImageDecoder>().Object); this.DefaultFormatsManager.SetDecoder(null, new Mock<IImageDecoder>().Object);
}); });
Assert.Throws<ArgumentNullException>(() => Assert.Throws<ArgumentNullException>(() =>
{ {
this.DefaultFormatsManager.SetDecoder(BmpFormat.Instance, null); this.DefaultFormatsManager.SetDecoder(BmpFormat.Instance, null);
}); });
Assert.Throws<ArgumentNullException>(() => Assert.Throws<ArgumentNullException>(() =>
{ {
this.DefaultFormatsManager.SetDecoder(null, null); this.DefaultFormatsManager.SetDecoder(null, null);
}); });
} }
[Fact] [Fact]
public void RegisterMimeTypeEncoderReplacesLast() public void RegisterMimeTypeEncoderReplacesLast()
{ {
IImageEncoder encoder1 = new Mock<IImageEncoder>().Object; IImageEncoder encoder1 = new Mock<IImageEncoder>().Object;
this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1); this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder1);
IImageEncoder found = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); IImageEncoder found = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat);
Assert.Equal(encoder1, found); Assert.Equal(encoder1, found);
IImageEncoder encoder2 = new Mock<IImageEncoder>().Object; IImageEncoder encoder2 = new Mock<IImageEncoder>().Object;
this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2); this.FormatsManagerEmpty.SetEncoder(TestFormat.GlobalTestFormat, encoder2);
IImageEncoder found2 = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat); IImageEncoder found2 = this.FormatsManagerEmpty.FindEncoder(TestFormat.GlobalTestFormat);
Assert.Equal(encoder2, found2); Assert.Equal(encoder2, found2);
Assert.NotEqual(found, found2); Assert.NotEqual(found, found2);
} }
[Fact] [Fact]
public void RegisterMimeTypeDecoderReplacesLast() public void RegisterMimeTypeDecoderReplacesLast()
{ {
IImageDecoder decoder1 = new Mock<IImageDecoder>().Object; IImageDecoder decoder1 = new Mock<IImageDecoder>().Object;
this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1); this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder1);
IImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); IImageDecoder found = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat);
Assert.Equal(decoder1, found); Assert.Equal(decoder1, found);
IImageDecoder decoder2 = new Mock<IImageDecoder>().Object; IImageDecoder decoder2 = new Mock<IImageDecoder>().Object;
this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2); this.FormatsManagerEmpty.SetDecoder(TestFormat.GlobalTestFormat, decoder2);
IImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat); IImageDecoder found2 = this.FormatsManagerEmpty.FindDecoder(TestFormat.GlobalTestFormat);
Assert.Equal(decoder2, found2); Assert.Equal(decoder2, found2);
Assert.NotEqual(found, found2); Assert.NotEqual(found, found2);
} }
[Fact] [Fact]
public void AddFormatCallsConfig() public void AddFormatCallsConfig()
{ {
var provider = new Mock<IConfigurationModule>(); var provider = new Mock<IConfigurationModule>();
var config = new Configuration(); var config = new Configuration();
config.Configure(provider.Object); config.Configure(provider.Object);
provider.Verify(x => x.Configure(config)); provider.Verify(x => x.Configure(config));
} }
[Fact] [Fact]
public void DetectFormatAllocatesCleanBuffer() public void DetectFormatAllocatesCleanBuffer()
{ {
byte[] jpegImage; byte[] jpegImage;
using (var buffer = new MemoryStream()) using (var buffer = new MemoryStream())
{ {
using (var image = new Image<Rgba32>(100, 100)) using (var image = new Image<Rgba32>(100, 100))
{ {
image.SaveAsJpeg(buffer); image.SaveAsJpeg(buffer);
jpegImage = buffer.ToArray(); jpegImage = buffer.ToArray();
} }
} }
byte[] invalidImage = { 1, 2, 3 }; byte[] invalidImage = { 1, 2, 3 };
Assert.Equal(Image.DetectFormat(jpegImage), JpegFormat.Instance); Assert.Equal(Image.DetectFormat(jpegImage), JpegFormat.Instance);
Assert.True(Image.DetectFormat(invalidImage) is null); Assert.True(Image.DetectFormat(invalidImage) is null);
} }
} }
} }

110
tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs

@ -1,56 +1,56 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
{ {
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using Xunit; using Xunit;
public class PorterDuffCompositorTests public class PorterDuffCompositorTests
{ {
// TODO: Add other modes to compare. // TODO: Add other modes to compare.
public static readonly TheoryData<PixelAlphaCompositionMode> CompositingOperators = public static readonly TheoryData<PixelAlphaCompositionMode> CompositingOperators =
new TheoryData<PixelAlphaCompositionMode> new TheoryData<PixelAlphaCompositionMode>
{ {
PixelAlphaCompositionMode.Src, PixelAlphaCompositionMode.Src,
PixelAlphaCompositionMode.SrcAtop, PixelAlphaCompositionMode.SrcAtop,
PixelAlphaCompositionMode.SrcOver, PixelAlphaCompositionMode.SrcOver,
PixelAlphaCompositionMode.SrcIn, PixelAlphaCompositionMode.SrcIn,
PixelAlphaCompositionMode.SrcOut, PixelAlphaCompositionMode.SrcOut,
PixelAlphaCompositionMode.Dest, PixelAlphaCompositionMode.Dest,
PixelAlphaCompositionMode.DestAtop, PixelAlphaCompositionMode.DestAtop,
PixelAlphaCompositionMode.DestOver, PixelAlphaCompositionMode.DestOver,
PixelAlphaCompositionMode.DestIn, PixelAlphaCompositionMode.DestIn,
PixelAlphaCompositionMode.DestOut, PixelAlphaCompositionMode.DestOut,
PixelAlphaCompositionMode.Clear, PixelAlphaCompositionMode.Clear,
PixelAlphaCompositionMode.Xor PixelAlphaCompositionMode.Xor
}; };
[Theory] [Theory]
[WithFile(TestImages.Png.PDDest, nameof(CompositingOperators), PixelTypes.Rgba32)] [WithFile(TestImages.Png.PDDest, nameof(CompositingOperators), PixelTypes.Rgba32)]
public void PorterDuffOutputIsCorrect(TestImageProvider<Rgba32> provider, PixelAlphaCompositionMode mode) public void PorterDuffOutputIsCorrect(TestImageProvider<Rgba32> provider, PixelAlphaCompositionMode mode)
{ {
var srcFile = TestFile.Create(TestImages.Png.PDSrc); var srcFile = TestFile.Create(TestImages.Png.PDSrc);
using (Image<Rgba32> src = srcFile.CreateRgba32Image()) using (Image<Rgba32> src = srcFile.CreateRgba32Image())
using (Image<Rgba32> dest = provider.GetImage()) using (Image<Rgba32> dest = provider.GetImage())
{ {
GraphicsOptions options = new GraphicsOptions GraphicsOptions options = new GraphicsOptions
{ {
AlphaCompositionMode = mode AlphaCompositionMode = mode
}; };
using (Image<Rgba32> res = dest.Clone(x => x.DrawImage(src, options))) using (Image<Rgba32> res = dest.Clone(x => x.DrawImage(src, options)))
{ {
string combinedMode = mode.ToString(); string combinedMode = mode.ToString();
if (combinedMode != "Src" && combinedMode.StartsWith("Src")) combinedMode = combinedMode.Substring(3); if (combinedMode != "Src" && combinedMode.StartsWith("Src")) combinedMode = combinedMode.Substring(3);
res.DebugSave(provider, combinedMode); res.DebugSave(provider, combinedMode);
res.CompareToReferenceOutput(provider, combinedMode); res.CompareToReferenceOutput(provider, combinedMode);
} }
} }
} }
} }
} }

110
tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs

@ -12,7 +12,7 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.PixelFormats namespace SixLabors.ImageSharp.Tests.PixelFormats
{ {
public class PixelBlenderTests public class PixelBlenderTests
{ {
public static TheoryData<object, Type, PixelColorBlendingMode> BlenderMappings = new TheoryData<object, Type, PixelColorBlendingMode>() public static TheoryData<object, Type, PixelColorBlendingMode> BlenderMappings = new TheoryData<object, Type, PixelColorBlendingMode>()
{ {
{ new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalSrcOver), PixelColorBlendingMode.Normal }, { new TestPixel<Rgba32>(), typeof(DefaultPixelBlenders<Rgba32>.NormalSrcOver), PixelColorBlendingMode.Normal },
@ -43,62 +43,62 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
{ {
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(mode, PixelAlphaCompositionMode.SrcOver); PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(mode, PixelAlphaCompositionMode.SrcOver);
Assert.IsType(type, blender); Assert.IsType(type, blender);
} }
public static TheoryData<Rgba32, Rgba32, float, PixelColorBlendingMode, Rgba32> ColorBlendingExpectedResults = new TheoryData<Rgba32, Rgba32, float, PixelColorBlendingMode, Rgba32>() public static TheoryData<Rgba32, Rgba32, float, PixelColorBlendingMode, Rgba32> ColorBlendingExpectedResults = new TheoryData<Rgba32, Rgba32, float, PixelColorBlendingMode, Rgba32>()
{ {
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Normal, Rgba32.MidnightBlue }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Normal, Rgba32.MidnightBlue },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Screen, new Rgba32(0xFFEEE7FF) }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Screen, new Rgba32(0xFFEEE7FF) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.HardLight, new Rgba32(0xFFC62D32) }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.HardLight, new Rgba32(0xFFC62D32) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Overlay, new Rgba32(0xFFDDCEFF) }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Overlay, new Rgba32(0xFFDDCEFF) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Darken, new Rgba32(0xFF701919) }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Darken, new Rgba32(0xFF701919) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Lighten, new Rgba32(0xFFE1E4FF) }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Lighten, new Rgba32(0xFFE1E4FF) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Add, new Rgba32(0xFFFFFDFF) }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Add, new Rgba32(0xFFFFFDFF) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Subtract, new Rgba32(0xFF71CBE6) }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Subtract, new Rgba32(0xFF71CBE6) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Multiply, new Rgba32(0xFF631619) }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Multiply, new Rgba32(0xFF631619) },
}; };
[Theory] [Theory]
[MemberData(nameof(ColorBlendingExpectedResults))] [MemberData(nameof(ColorBlendingExpectedResults))]
public void TestColorBlendingModes(Rgba32 backdrop, Rgba32 source, float opacity, PixelColorBlendingMode mode, Rgba32 expectedResult) public void TestColorBlendingModes(Rgba32 backdrop, Rgba32 source, float opacity, PixelColorBlendingMode mode, Rgba32 expectedResult)
{ {
PixelBlender<Rgba32> blender = PixelOperations<Rgba32>.Instance.GetPixelBlender(mode, PixelAlphaCompositionMode.SrcOver); PixelBlender<Rgba32> blender = PixelOperations<Rgba32>.Instance.GetPixelBlender(mode, PixelAlphaCompositionMode.SrcOver);
Rgba32 actualResult = blender.Blend(backdrop, source, opacity); Rgba32 actualResult = blender.Blend(backdrop, source, opacity);
// var str = actualResult.Rgba.ToString("X8"); // used to extract expectedResults // var str = actualResult.Rgba.ToString("X8"); // used to extract expectedResults
Assert.Equal(actualResult.ToVector4(), expectedResult.ToVector4()); Assert.Equal(actualResult.ToVector4(), expectedResult.ToVector4());
} }
public static TheoryData<Rgba32, Rgba32, float, PixelAlphaCompositionMode, Rgba32> AlphaCompositionExpectedResults = new TheoryData<Rgba32, Rgba32, float, PixelAlphaCompositionMode, Rgba32>() public static TheoryData<Rgba32, Rgba32, float, PixelAlphaCompositionMode, Rgba32> AlphaCompositionExpectedResults = new TheoryData<Rgba32, Rgba32, float, PixelAlphaCompositionMode, Rgba32>()
{ {
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Clear, new Rgba32(0) }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Clear, new Rgba32(0) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Xor, new Rgba32(0) }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Xor, new Rgba32(0) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Dest, Rgba32.MistyRose }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Dest, Rgba32.MistyRose },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestAtop, Rgba32.MistyRose }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestAtop, Rgba32.MistyRose },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestIn, Rgba32.MistyRose }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestIn, Rgba32.MistyRose },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestOut, new Rgba32(0) }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestOut, new Rgba32(0) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestOver, Rgba32.MistyRose }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestOver, Rgba32.MistyRose },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Src, Rgba32.MidnightBlue }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Src, Rgba32.MidnightBlue },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcAtop, Rgba32.MidnightBlue }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcAtop, Rgba32.MidnightBlue },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcIn, Rgba32.MidnightBlue }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcIn, Rgba32.MidnightBlue },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcOut, new Rgba32(0) }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcOut, new Rgba32(0) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcOver, Rgba32.MidnightBlue }, { Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcOver, Rgba32.MidnightBlue },
}; };
[Theory] [Theory]
[MemberData(nameof(AlphaCompositionExpectedResults))] [MemberData(nameof(AlphaCompositionExpectedResults))]
public void TestAlphaCompositionModes(Rgba32 backdrop, Rgba32 source, float opacity, PixelAlphaCompositionMode mode, Rgba32 expectedResult) public void TestAlphaCompositionModes(Rgba32 backdrop, Rgba32 source, float opacity, PixelAlphaCompositionMode mode, Rgba32 expectedResult)
{ {
PixelBlender<Rgba32> blender = PixelOperations<Rgba32>.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, mode); PixelBlender<Rgba32> blender = PixelOperations<Rgba32>.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, mode);
Rgba32 actualResult = blender.Blend(backdrop, source, opacity); Rgba32 actualResult = blender.Blend(backdrop, source, opacity);
// var str = actualResult.Rgba.ToString("X8"); // used to extract expectedResults // var str = actualResult.Rgba.ToString("X8"); // used to extract expectedResults
Assert.Equal(actualResult.ToVector4(), expectedResult.ToVector4()); Assert.Equal(actualResult.ToVector4(), expectedResult.ToVector4());
} }
} }
} }

532
tests/Images/Input/Jpg/baseline/JpegSnoopReports/Floorplan.jpg.txt

@ -1,266 +1,266 @@
JPEGsnoop 1.8.0 by Calvin Hass JPEGsnoop 1.8.0 by Calvin Hass
http://www.impulseadventure.com/photo/ http://www.impulseadventure.com/photo/
------------------------------------- -------------------------------------
Filename: [.\Floorplan.jpg] Filename: [.\Floorplan.jpg]
Filesize: [161577] Bytes Filesize: [161577] Bytes
Start Offset: 0x00000000 Start Offset: 0x00000000
*** Marker: SOI (xFFD8) *** *** Marker: SOI (xFFD8) ***
OFFSET: 0x00000000 OFFSET: 0x00000000
*** Marker: APP0 (xFFE0) *** *** Marker: APP0 (xFFE0) ***
OFFSET: 0x00000002 OFFSET: 0x00000002
Length = 16 Length = 16
Identifier = [JFIF] Identifier = [JFIF]
version = [1.1] version = [1.1]
density = 300 x 300 DPI (dots per inch) density = 300 x 300 DPI (dots per inch)
thumbnail = 0 x 0 thumbnail = 0 x 0
*** Marker: APP1 (xFFE1) *** *** Marker: APP1 (xFFE1) ***
OFFSET: 0x00000014 OFFSET: 0x00000014
Length = 13464 Length = 13464
Identifier = [Exif] Identifier = [Exif]
Identifier TIFF = 0x[4D4D002A 00000008] Identifier TIFF = 0x[4D4D002A 00000008]
Endian = Motorola (big) Endian = Motorola (big)
TAG Mark x002A = 0x002A TAG Mark x002A = 0x002A
EXIF IFD0 @ Absolute 0x00000026 EXIF IFD0 @ Absolute 0x00000026
Dir Length = 0x000A Dir Length = 0x000A
[Model ] = "Photosmart Plus B209a-m" [Model ] = "Photosmart Plus B209a-m"
[Orientation ] = 1 = Row 0: top, Col 0: left [Orientation ] = 1 = Row 0: top, Col 0: left
[XResolution ] = 300/1 [XResolution ] = 300/1
[YResolution ] = 300/1 [YResolution ] = 300/1
[ResolutionUnit ] = Inch [ResolutionUnit ] = Inch
[Software ] = "Windows Photo Editor 10.0.10011.16384" [Software ] = "Windows Photo Editor 10.0.10011.16384"
[DateTime ] = "2016:01:02 20:17:37" [DateTime ] = "2016:01:02 20:17:37"
[ExifOffset ] = @ 0x091A [ExifOffset ] = @ 0x091A
Offset to Next IFD = 0x000011B6 Offset to Next IFD = 0x000011B6
EXIF IFD1 @ Absolute 0x000011D4 EXIF IFD1 @ Absolute 0x000011D4
Dir Length = 0x0006 Dir Length = 0x0006
[Compression ] = JPEG [Compression ] = JPEG
[XResolution ] = 96/1 [XResolution ] = 96/1
[YResolution ] = 96/1 [YResolution ] = 96/1
[ResolutionUnit ] = Inch [ResolutionUnit ] = Inch
[JpegIFOffset ] = @ +0x1214 = @ 0x1232 [JpegIFOffset ] = @ +0x1214 = @ 0x1232
[JpegIFByteCount ] = 0x[0000227C] / 8828 [JpegIFByteCount ] = 0x[0000227C] / 8828
Offset to Next IFD = 0x00000000 Offset to Next IFD = 0x00000000
EXIF SubIFD @ Absolute 0x00000938 EXIF SubIFD @ Absolute 0x00000938
Dir Length = 0x0008 Dir Length = 0x0008
[DateTimeOriginal ] = "2016:01:02 19:22:28" [DateTimeOriginal ] = "2016:01:02 19:22:28"
[DateTimeDigitized ] = "2016:01:02 19:22:28" [DateTimeDigitized ] = "2016:01:02 19:22:28"
[SubSecTimeOriginal ] = "00" [SubSecTimeOriginal ] = "00"
[SubSecTimeDigitized ] = "00" [SubSecTimeDigitized ] = "00"
[ColorSpace ] = sRGB [ColorSpace ] = sRGB
[ExifImageWidth ] = 0x[00000922] / 2338 [ExifImageWidth ] = 0x[00000922] / 2338
[ExifImageHeight ] = 0x[000008C9] / 2249 [ExifImageHeight ] = 0x[000008C9] / 2249
*** Marker: APP1 (xFFE1) *** *** Marker: APP1 (xFFE1) ***
OFFSET: 0x000034AE OFFSET: 0x000034AE
Length = 12772 Length = 12772
Identifier = [http://ns.adobe.com/xap/1.0/] Identifier = [http://ns.adobe.com/xap/1.0/]
XMP = XMP =
|<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?> |<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>
|<x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:xmp="http://ns.adobe.com/xap/1.0/"><xmp:CreatorTool>Windows Photo Editor 10.0.10011.16384</xmp:CreatorTool><xmp:CreateDate>2016-01-02T19:22:28</xmp:CreateDate></rdf:Description></rdf:RDF></x:xmpmeta> |<x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:xmp="http://ns.adobe.com/xap/1.0/"><xmp:CreatorTool>Windows Photo Editor 10.0.10011.16384</xmp:CreatorTool><xmp:CreateDate>2016-01-02T19:22:28</xmp:CreateDate></rdf:Description></rdf:RDF></x:xmpmeta>
*** Marker: DQT (xFFDB) *** *** Marker: DQT (xFFDB) ***
Define a Quantization Table. Define a Quantization Table.
OFFSET: 0x00006694 OFFSET: 0x00006694
Table length = 67 Table length = 67
---- ----
Precision=8 bits Precision=8 bits
Destination ID=0 (Luminance) Destination ID=0 (Luminance)
DQT, Row #0: 3 2 2 3 5 8 10 12 DQT, Row #0: 3 2 2 3 5 8 10 12
DQT, Row #1: 2 2 3 4 5 12 12 11 DQT, Row #1: 2 2 3 4 5 12 12 11
DQT, Row #2: 3 3 3 5 8 11 14 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 #3: 3 3 4 6 10 17 16 12
DQT, Row #4: 4 4 7 11 14 22 21 15 DQT, Row #4: 4 4 7 11 14 22 21 15
DQT, Row #5: 5 7 11 13 16 21 23 18 DQT, Row #5: 5 7 11 13 16 21 23 18
DQT, Row #6: 10 13 16 17 21 24 24 20 DQT, Row #6: 10 13 16 17 21 24 24 20
DQT, Row #7: 14 18 19 20 22 20 21 20 DQT, Row #7: 14 18 19 20 22 20 21 20
Approx quality factor = 90.06 (scaling=19.88 variance=1.14) Approx quality factor = 90.06 (scaling=19.88 variance=1.14)
*** Marker: SOF0 (Baseline DCT) (xFFC0) *** *** Marker: SOF0 (Baseline DCT) (xFFC0) ***
OFFSET: 0x000066D9 OFFSET: 0x000066D9
Frame header length = 11 Frame header length = 11
Precision = 8 Precision = 8
Number of Lines = 645 Number of Lines = 645
Samples per Line = 976 Samples per Line = 976
Image Size = 976 x 645 Image Size = 976 x 645
Raw Image Orientation = Landscape Raw Image Orientation = Landscape
Number of Img components = 1 Number of Img components = 1
Component[1]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) Component[1]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y)
*** Marker: DHT (Define Huffman Table) (xFFC4) *** *** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x000066E6 OFFSET: 0x000066E6
Huffman table length = 31 Huffman table length = 31
---- ----
Destination ID = 0 Destination ID = 0
Class = 0 (DC / Lossless Table) Class = 0 (DC / Lossless Table)
Codes of length 01 bits (000 total): Codes of length 01 bits (000 total):
Codes of length 02 bits (001 total): 00 Codes of length 02 bits (001 total): 00
Codes of length 03 bits (005 total): 01 02 03 04 05 Codes of length 03 bits (005 total): 01 02 03 04 05
Codes of length 04 bits (001 total): 06 Codes of length 04 bits (001 total): 06
Codes of length 05 bits (001 total): 07 Codes of length 05 bits (001 total): 07
Codes of length 06 bits (001 total): 08 Codes of length 06 bits (001 total): 08
Codes of length 07 bits (001 total): 09 Codes of length 07 bits (001 total): 09
Codes of length 08 bits (001 total): 0A Codes of length 08 bits (001 total): 0A
Codes of length 09 bits (001 total): 0B Codes of length 09 bits (001 total): 0B
Codes of length 10 bits (000 total): Codes of length 10 bits (000 total):
Codes of length 11 bits (000 total): Codes of length 11 bits (000 total):
Codes of length 12 bits (000 total): Codes of length 12 bits (000 total):
Codes of length 13 bits (000 total): Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total): Codes of length 14 bits (000 total):
Codes of length 15 bits (000 total): Codes of length 15 bits (000 total):
Codes of length 16 bits (000 total): Codes of length 16 bits (000 total):
Total number of codes: 012 Total number of codes: 012
*** Marker: DHT (Define Huffman Table) (xFFC4) *** *** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x00006707 OFFSET: 0x00006707
Huffman table length = 181 Huffman table length = 181
---- ----
Destination ID = 0 Destination ID = 0
Class = 1 (AC Table) Class = 1 (AC Table)
Codes of length 01 bits (000 total): Codes of length 01 bits (000 total):
Codes of length 02 bits (002 total): 01 02 Codes of length 02 bits (002 total): 01 02
Codes of length 03 bits (001 total): 03 Codes of length 03 bits (001 total): 03
Codes of length 04 bits (003 total): 00 04 11 Codes of length 04 bits (003 total): 00 04 11
Codes of length 05 bits (003 total): 05 12 21 Codes of length 05 bits (003 total): 05 12 21
Codes of length 06 bits (002 total): 31 41 Codes of length 06 bits (002 total): 31 41
Codes of length 07 bits (004 total): 06 13 51 61 Codes of length 07 bits (004 total): 06 13 51 61
Codes of length 08 bits (003 total): 07 22 71 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 09 bits (005 total): 14 32 81 91 A1
Codes of length 10 bits (005 total): 08 23 42 B1 C1 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 11 bits (004 total): 15 52 D1 F0
Codes of length 12 bits (004 total): 24 33 62 72 Codes of length 12 bits (004 total): 24 33 62 72
Codes of length 13 bits (000 total): Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total): Codes of length 14 bits (000 total):
Codes of length 15 bits (001 total): 82 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 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 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 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 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 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 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 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 E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA
Total number of codes: 162 Total number of codes: 162
*** Marker: SOS (Start of Scan) (xFFDA) *** *** Marker: SOS (Start of Scan) (xFFDA) ***
OFFSET: 0x000067BE OFFSET: 0x000067BE
Scan header length = 8 Scan header length = 8
Number of img components = 1 Number of img components = 1
Component[1]: selector=0x01, table=0(DC),0(AC) Component[1]: selector=0x01, table=0(DC),0(AC)
Spectral selection = 0 .. 63 Spectral selection = 0 .. 63
Successive approximation = 0x00 Successive approximation = 0x00
*** Decoding SCAN Data *** *** Decoding SCAN Data ***
OFFSET: 0x000067C8 OFFSET: 0x000067C8
Scan Decode Mode: No IDCT (DC only) Scan Decode Mode: No IDCT (DC only)
NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT]
Scan Data encountered marker 0xFFD9 @ 0x00027727.0 Scan Data encountered marker 0xFFD9 @ 0x00027727.0
Compression stats: Compression stats:
Compression Ratio: 4.66:1 Compression Ratio: 4.66:1
Bits per pixel: 1.72:1 Bits per pixel: 1.72:1
Huffman code histogram stats: Huffman code histogram stats:
Huffman Table: (Dest ID: 0, Class: DC) Huffman Table: (Dest ID: 0, Class: DC)
# codes of length 01 bits: 0 ( 0%) # codes of length 01 bits: 0 ( 0%)
# codes of length 02 bits: 3571 ( 36%) # codes of length 02 bits: 3571 ( 36%)
# codes of length 03 bits: 4320 ( 44%) # codes of length 03 bits: 4320 ( 44%)
# codes of length 04 bits: 925 ( 9%) # codes of length 04 bits: 925 ( 9%)
# codes of length 05 bits: 456 ( 5%) # codes of length 05 bits: 456 ( 5%)
# codes of length 06 bits: 313 ( 3%) # codes of length 06 bits: 313 ( 3%)
# codes of length 07 bits: 291 ( 3%) # codes of length 07 bits: 291 ( 3%)
# codes of length 08 bits: 6 ( 0%) # codes of length 08 bits: 6 ( 0%)
# codes of length 09 bits: 0 ( 0%) # codes of length 09 bits: 0 ( 0%)
# codes of length 10 bits: 0 ( 0%) # codes of length 10 bits: 0 ( 0%)
# codes of length 11 bits: 0 ( 0%) # codes of length 11 bits: 0 ( 0%)
# codes of length 12 bits: 0 ( 0%) # codes of length 12 bits: 0 ( 0%)
# codes of length 13 bits: 0 ( 0%) # codes of length 13 bits: 0 ( 0%)
# codes of length 14 bits: 0 ( 0%) # codes of length 14 bits: 0 ( 0%)
# codes of length 15 bits: 0 ( 0%) # codes of length 15 bits: 0 ( 0%)
# codes of length 16 bits: 0 ( 0%) # codes of length 16 bits: 0 ( 0%)
Huffman Table: (Dest ID: 0, Class: AC) Huffman Table: (Dest ID: 0, Class: AC)
# codes of length 01 bits: 0 ( 0%) # codes of length 01 bits: 0 ( 0%)
# codes of length 02 bits: 78118 ( 44%) # codes of length 02 bits: 78118 ( 44%)
# codes of length 03 bits: 22349 ( 13%) # codes of length 03 bits: 22349 ( 13%)
# codes of length 04 bits: 35264 ( 20%) # codes of length 04 bits: 35264 ( 20%)
# codes of length 05 bits: 18811 ( 11%) # codes of length 05 bits: 18811 ( 11%)
# codes of length 06 bits: 4312 ( 2%) # codes of length 06 bits: 4312 ( 2%)
# codes of length 07 bits: 8245 ( 5%) # codes of length 07 bits: 8245 ( 5%)
# codes of length 08 bits: 4682 ( 3%) # codes of length 08 bits: 4682 ( 3%)
# codes of length 09 bits: 1584 ( 1%) # codes of length 09 bits: 1584 ( 1%)
# codes of length 10 bits: 1900 ( 1%) # codes of length 10 bits: 1900 ( 1%)
# codes of length 11 bits: 324 ( 0%) # codes of length 11 bits: 324 ( 0%)
# codes of length 12 bits: 116 ( 0%) # codes of length 12 bits: 116 ( 0%)
# codes of length 13 bits: 0 ( 0%) # codes of length 13 bits: 0 ( 0%)
# codes of length 14 bits: 0 ( 0%) # codes of length 14 bits: 0 ( 0%)
# codes of length 15 bits: 6 ( 0%) # codes of length 15 bits: 6 ( 0%)
# codes of length 16 bits: 639 ( 0%) # codes of length 16 bits: 639 ( 0%)
YCC clipping in DC: YCC clipping in DC:
Y component: [<0= 0] [>255= 0] Y component: [<0= 0] [>255= 0]
Cb component: [<0= 0] [>255= 0] Cb component: [<0= 0] [>255= 0]
Cr component: [<0= 0] [>255= 0] Cr component: [<0= 0] [>255= 0]
RGB clipping in DC: RGB clipping in DC:
R component: [<0= 0] [>255= 0] R component: [<0= 0] [>255= 0]
G component: [<0= 0] [>255= 0] G component: [<0= 0] [>255= 0]
B component: [<0= 0] [>255= 0] B component: [<0= 0] [>255= 0]
Average Pixel Luminance (Y): Average Pixel Luminance (Y):
Y=[231] (range: 0..255) Y=[231] (range: 0..255)
Brightest Pixel Search: Brightest Pixel Search:
YCC=[ 1017, 0, 0] RGB=[255,255,255] @ MCU[ 7, 0] YCC=[ 1017, 0, 0] RGB=[255,255,255] @ MCU[ 7, 0]
Finished Decoding SCAN Data Finished Decoding SCAN Data
Number of RESTART markers decoded: 0 Number of RESTART markers decoded: 0
Next position in scan buffer: Offset 0x00027726.4 Next position in scan buffer: Offset 0x00027726.4
*** Marker: EOI (End of Image) (xFFD9) *** *** Marker: EOI (End of Image) (xFFD9) ***
OFFSET: 0x00027727 OFFSET: 0x00027727
*** Searching Compression Signatures *** *** Searching Compression Signatures ***
Signature: 015C645021E37D3469A6B652789383DB Signature: 015C645021E37D3469A6B652789383DB
Signature (Rotated): 01D400C125EB43B05762A66347B271F7 Signature (Rotated): 01D400C125EB43B05762A66347B271F7
File Offset: 0 bytes File Offset: 0 bytes
Chroma subsampling: Gray Chroma subsampling: Gray
EXIF Make/Model: OK [???] [Photosmart Plus B209a-m] EXIF Make/Model: OK [???] [Photosmart Plus B209a-m]
EXIF Makernotes: NONE EXIF Makernotes: NONE
EXIF Software: OK [Windows Photo Editor 10.0.10011.16384] EXIF Software: OK [Windows Photo Editor 10.0.10011.16384]
Searching Compression Signatures: (3347 built-in, 0 user(*) ) Searching Compression Signatures: (3347 built-in, 0 user(*) )
EXIF.Make / Software EXIF.Model Quality Subsamp Match? EXIF.Make / Software EXIF.Model Quality Subsamp Match?
------------------------- ----------------------------------- ---------------- -------------- ------------------------- ----------------------------------- ---------------- --------------
SW :[IJG Library ] [090 Gray ] SW :[IJG Library ] [090 Gray ]
The following IJG-based editors also match this signature: The following IJG-based editors also match this signature:
SW :[GIMP ] [090 Gray ] SW :[GIMP ] [090 Gray ]
SW :[IrfanView ] [090 Gray ] SW :[IrfanView ] [090 Gray ]
SW :[idImager ] [090 Gray ] SW :[idImager ] [090 Gray ]
SW :[FastStone Image Viewer ] [090 Gray ] SW :[FastStone Image Viewer ] [090 Gray ]
SW :[NeatImage ] [090 Gray ] SW :[NeatImage ] [090 Gray ]
SW :[Paint.NET ] [090 Gray ] SW :[Paint.NET ] [090 Gray ]
SW :[Photomatix ] [090 Gray ] SW :[Photomatix ] [090 Gray ]
SW :[XnView ] [090 Gray ] SW :[XnView ] [090 Gray ]
Based on the analysis of compression characteristics and EXIF metadata: Based on the analysis of compression characteristics and EXIF metadata:
ASSESSMENT: Class 2 - Image has high probability of being processed/edited ASSESSMENT: Class 2 - Image has high probability of being processed/edited

868
tests/Images/Input/Jpg/baseline/JpegSnoopReports/badrst.jpg.txt

@ -1,434 +1,434 @@
JPEGsnoop 1.8.0 by Calvin Hass JPEGsnoop 1.8.0 by Calvin Hass
http://www.impulseadventure.com/photo/ http://www.impulseadventure.com/photo/
------------------------------------- -------------------------------------
Filename: [.\badrst.jpg] Filename: [.\badrst.jpg]
Filesize: [74497] Bytes Filesize: [74497] Bytes
Start Offset: 0x00000000 Start Offset: 0x00000000
*** Marker: SOI (xFFD8) *** *** Marker: SOI (xFFD8) ***
OFFSET: 0x00000000 OFFSET: 0x00000000
*** Marker: APP0 (xFFE0) *** *** Marker: APP0 (xFFE0) ***
OFFSET: 0x00000002 OFFSET: 0x00000002
Length = 16 Length = 16
Identifier = [JFIF] Identifier = [JFIF]
version = [1.1] version = [1.1]
density = 96 x 96 DPI (dots per inch) density = 96 x 96 DPI (dots per inch)
thumbnail = 0 x 0 thumbnail = 0 x 0
*** Marker: APP1 (xFFE1) *** *** Marker: APP1 (xFFE1) ***
OFFSET: 0x00000014 OFFSET: 0x00000014
Length = 8628 Length = 8628
Identifier = [Exif] Identifier = [Exif]
Identifier TIFF = 0x[4D4D002A 00000008] Identifier TIFF = 0x[4D4D002A 00000008]
Endian = Motorola (big) Endian = Motorola (big)
TAG Mark x002A = 0x002A TAG Mark x002A = 0x002A
EXIF IFD0 @ Absolute 0x00000026 EXIF IFD0 @ Absolute 0x00000026
Dir Length = 0x0003 Dir Length = 0x0003
[Orientation ] = 1 = Row 0: top, Col 0: left [Orientation ] = 1 = Row 0: top, Col 0: left
[ExifOffset ] = @ 0x083E [ExifOffset ] = @ 0x083E
Offset to Next IFD = 0x000010B6 Offset to Next IFD = 0x000010B6
EXIF IFD1 @ Absolute 0x000010D4 EXIF IFD1 @ Absolute 0x000010D4
Dir Length = 0x0006 Dir Length = 0x0006
[Compression ] = JPEG [Compression ] = JPEG
[XResolution ] = 96/1 [XResolution ] = 96/1
[YResolution ] = 96/1 [YResolution ] = 96/1
[ResolutionUnit ] = Inch [ResolutionUnit ] = Inch
[JpegIFOffset ] = @ +0x1114 = @ 0x1132 [JpegIFOffset ] = @ +0x1114 = @ 0x1132
[JpegIFByteCount ] = 0x[00001097] / 4247 [JpegIFByteCount ] = 0x[00001097] / 4247
Offset to Next IFD = 0x00000000 Offset to Next IFD = 0x00000000
EXIF SubIFD @ Absolute 0x0000085C EXIF SubIFD @ Absolute 0x0000085C
Dir Length = 0x0005 Dir Length = 0x0005
[DateTimeOriginal ] = "2016:02:28 11:17:08" [DateTimeOriginal ] = "2016:02:28 11:17:08"
[DateTimeDigitized ] = "2016:02:28 11:17:08" [DateTimeDigitized ] = "2016:02:28 11:17:08"
[SubSecTimeOriginal ] = "06" [SubSecTimeOriginal ] = "06"
[SubSecTimeDigitized ] = "06" [SubSecTimeDigitized ] = "06"
*** Marker: APP1 (xFFE1) *** *** Marker: APP1 (xFFE1) ***
OFFSET: 0x000021CA OFFSET: 0x000021CA
Length = 2464 Length = 2464
Identifier = [http://ns.adobe.com/xap/1.0/] Identifier = [http://ns.adobe.com/xap/1.0/]
XMP = XMP =
|<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?> |<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>
|<x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:xmp="http://ns.adobe.com/xap/1.0/"><xmp:CreateDate>2016-02-28T11:17:08.057</xmp:CreateDate></rdf:Description></rdf:RDF></x:xmpmeta> |<x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:xmp="http://ns.adobe.com/xap/1.0/"><xmp:CreateDate>2016-02-28T11:17:08.057</xmp:CreateDate></rdf:Description></rdf:RDF></x:xmpmeta>
*** Marker: DQT (xFFDB) *** *** Marker: DQT (xFFDB) ***
Define a Quantization Table. Define a Quantization Table.
OFFSET: 0x00002B6C OFFSET: 0x00002B6C
Table length = 67 Table length = 67
---- ----
Precision=8 bits Precision=8 bits
Destination ID=0 (Luminance) Destination ID=0 (Luminance)
DQT, Row #0: 3 2 2 3 5 8 10 12 DQT, Row #0: 3 2 2 3 5 8 10 12
DQT, Row #1: 2 2 3 4 5 12 12 11 DQT, Row #1: 2 2 3 4 5 12 12 11
DQT, Row #2: 3 3 3 5 8 11 14 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 #3: 3 3 4 6 10 17 16 12
DQT, Row #4: 4 4 7 11 14 22 21 15 DQT, Row #4: 4 4 7 11 14 22 21 15
DQT, Row #5: 5 7 11 13 16 21 23 18 DQT, Row #5: 5 7 11 13 16 21 23 18
DQT, Row #6: 10 13 16 17 21 24 24 20 DQT, Row #6: 10 13 16 17 21 24 24 20
DQT, Row #7: 14 18 19 20 22 20 21 20 DQT, Row #7: 14 18 19 20 22 20 21 20
Approx quality factor = 90.06 (scaling=19.88 variance=1.14) Approx quality factor = 90.06 (scaling=19.88 variance=1.14)
*** Marker: DQT (xFFDB) *** *** Marker: DQT (xFFDB) ***
Define a Quantization Table. Define a Quantization Table.
OFFSET: 0x00002BB1 OFFSET: 0x00002BB1
Table length = 67 Table length = 67
---- ----
Precision=8 bits Precision=8 bits
Destination ID=1 (Chrominance) Destination ID=1 (Chrominance)
DQT, Row #0: 3 4 5 9 20 20 20 20 DQT, Row #0: 3 4 5 9 20 20 20 20
DQT, Row #1: 4 4 5 13 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 #2: 5 5 11 20 20 20 20 20
DQT, Row #3: 9 13 20 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 #4: 20 20 20 20 20 20 20 20
DQT, Row #5: 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 #6: 20 20 20 20 20 20 20 20
DQT, Row #7: 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) Approx quality factor = 89.93 (scaling=20.14 variance=0.34)
*** Marker: SOF0 (Baseline DCT) (xFFC0) *** *** Marker: SOF0 (Baseline DCT) (xFFC0) ***
OFFSET: 0x00002BF6 OFFSET: 0x00002BF6
Frame header length = 17 Frame header length = 17
Precision = 8 Precision = 8
Number of Lines = 480 Number of Lines = 480
Samples per Line = 640 Samples per Line = 640
Image Size = 640 x 480 Image Size = 640 x 480
Raw Image Orientation = Landscape Raw Image Orientation = Landscape
Number of Img components = 3 Number of Img components = 3
Component[1]: ID=0x01, Samp Fac=0x22 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y) 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[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) Component[3]: ID=0x03, Samp Fac=0x11 (Subsamp 2 x 2), Quant Tbl Sel=0x01 (Chrom: Cr)
*** Marker: DHT (Define Huffman Table) (xFFC4) *** *** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x00002C09 OFFSET: 0x00002C09
Huffman table length = 31 Huffman table length = 31
---- ----
Destination ID = 0 Destination ID = 0
Class = 0 (DC / Lossless Table) Class = 0 (DC / Lossless Table)
Codes of length 01 bits (000 total): Codes of length 01 bits (000 total):
Codes of length 02 bits (001 total): 00 Codes of length 02 bits (001 total): 00
Codes of length 03 bits (005 total): 01 02 03 04 05 Codes of length 03 bits (005 total): 01 02 03 04 05
Codes of length 04 bits (001 total): 06 Codes of length 04 bits (001 total): 06
Codes of length 05 bits (001 total): 07 Codes of length 05 bits (001 total): 07
Codes of length 06 bits (001 total): 08 Codes of length 06 bits (001 total): 08
Codes of length 07 bits (001 total): 09 Codes of length 07 bits (001 total): 09
Codes of length 08 bits (001 total): 0A Codes of length 08 bits (001 total): 0A
Codes of length 09 bits (001 total): 0B Codes of length 09 bits (001 total): 0B
Codes of length 10 bits (000 total): Codes of length 10 bits (000 total):
Codes of length 11 bits (000 total): Codes of length 11 bits (000 total):
Codes of length 12 bits (000 total): Codes of length 12 bits (000 total):
Codes of length 13 bits (000 total): Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total): Codes of length 14 bits (000 total):
Codes of length 15 bits (000 total): Codes of length 15 bits (000 total):
Codes of length 16 bits (000 total): Codes of length 16 bits (000 total):
Total number of codes: 012 Total number of codes: 012
*** Marker: DHT (Define Huffman Table) (xFFC4) *** *** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x00002C2A OFFSET: 0x00002C2A
Huffman table length = 181 Huffman table length = 181
---- ----
Destination ID = 0 Destination ID = 0
Class = 1 (AC Table) Class = 1 (AC Table)
Codes of length 01 bits (000 total): Codes of length 01 bits (000 total):
Codes of length 02 bits (002 total): 01 02 Codes of length 02 bits (002 total): 01 02
Codes of length 03 bits (001 total): 03 Codes of length 03 bits (001 total): 03
Codes of length 04 bits (003 total): 00 04 11 Codes of length 04 bits (003 total): 00 04 11
Codes of length 05 bits (003 total): 05 12 21 Codes of length 05 bits (003 total): 05 12 21
Codes of length 06 bits (002 total): 31 41 Codes of length 06 bits (002 total): 31 41
Codes of length 07 bits (004 total): 06 13 51 61 Codes of length 07 bits (004 total): 06 13 51 61
Codes of length 08 bits (003 total): 07 22 71 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 09 bits (005 total): 14 32 81 91 A1
Codes of length 10 bits (005 total): 08 23 42 B1 C1 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 11 bits (004 total): 15 52 D1 F0
Codes of length 12 bits (004 total): 24 33 62 72 Codes of length 12 bits (004 total): 24 33 62 72
Codes of length 13 bits (000 total): Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total): Codes of length 14 bits (000 total):
Codes of length 15 bits (001 total): 82 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 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 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 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 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 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 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 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 E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA
Total number of codes: 162 Total number of codes: 162
*** Marker: DHT (Define Huffman Table) (xFFC4) *** *** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x00002CE1 OFFSET: 0x00002CE1
Huffman table length = 31 Huffman table length = 31
---- ----
Destination ID = 1 Destination ID = 1
Class = 0 (DC / Lossless Table) Class = 0 (DC / Lossless Table)
Codes of length 01 bits (000 total): Codes of length 01 bits (000 total):
Codes of length 02 bits (003 total): 00 01 02 Codes of length 02 bits (003 total): 00 01 02
Codes of length 03 bits (001 total): 03 Codes of length 03 bits (001 total): 03
Codes of length 04 bits (001 total): 04 Codes of length 04 bits (001 total): 04
Codes of length 05 bits (001 total): 05 Codes of length 05 bits (001 total): 05
Codes of length 06 bits (001 total): 06 Codes of length 06 bits (001 total): 06
Codes of length 07 bits (001 total): 07 Codes of length 07 bits (001 total): 07
Codes of length 08 bits (001 total): 08 Codes of length 08 bits (001 total): 08
Codes of length 09 bits (001 total): 09 Codes of length 09 bits (001 total): 09
Codes of length 10 bits (001 total): 0A Codes of length 10 bits (001 total): 0A
Codes of length 11 bits (001 total): 0B Codes of length 11 bits (001 total): 0B
Codes of length 12 bits (000 total): Codes of length 12 bits (000 total):
Codes of length 13 bits (000 total): Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total): Codes of length 14 bits (000 total):
Codes of length 15 bits (000 total): Codes of length 15 bits (000 total):
Codes of length 16 bits (000 total): Codes of length 16 bits (000 total):
Total number of codes: 012 Total number of codes: 012
*** Marker: DHT (Define Huffman Table) (xFFC4) *** *** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x00002D02 OFFSET: 0x00002D02
Huffman table length = 181 Huffman table length = 181
---- ----
Destination ID = 1 Destination ID = 1
Class = 1 (AC Table) Class = 1 (AC Table)
Codes of length 01 bits (000 total): Codes of length 01 bits (000 total):
Codes of length 02 bits (002 total): 00 01 Codes of length 02 bits (002 total): 00 01
Codes of length 03 bits (001 total): 02 Codes of length 03 bits (001 total): 02
Codes of length 04 bits (002 total): 03 11 Codes of length 04 bits (002 total): 03 11
Codes of length 05 bits (004 total): 04 05 21 31 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 06 bits (004 total): 06 12 41 51
Codes of length 07 bits (003 total): 07 61 71 Codes of length 07 bits (003 total): 07 61 71
Codes of length 08 bits (004 total): 13 22 32 81 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 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 10 bits (005 total): 09 23 33 52 F0
Codes of length 11 bits (004 total): 15 62 72 D1 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 12 bits (004 total): 0A 16 24 34
Codes of length 13 bits (000 total): Codes of length 13 bits (000 total):
Codes of length 14 bits (001 total): E1 Codes of length 14 bits (001 total): E1
Codes of length 15 bits (002 total): 25 F1 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 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 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 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 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 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 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 D6 D7 D8 D9 DA E2 E3 E4 E5 E6 E7 E8 E9 EA F2 F3
F4 F5 F6 F7 F8 F9 FA F4 F5 F6 F7 F8 F9 FA
Total number of codes: 162 Total number of codes: 162
*** Marker: DRI (Restart Interval) (xFFDD) *** *** Marker: DRI (Restart Interval) (xFFDD) ***
OFFSET: 0x00002DB9 OFFSET: 0x00002DB9
Length = 4 Length = 4
interval = 600 interval = 600
*** Marker: SOS (Start of Scan) (xFFDA) *** *** Marker: SOS (Start of Scan) (xFFDA) ***
OFFSET: 0x00002DBF OFFSET: 0x00002DBF
Scan header length = 12 Scan header length = 12
Number of img components = 3 Number of img components = 3
Component[1]: selector=0x01, table=0(DC),0(AC) Component[1]: selector=0x01, table=0(DC),0(AC)
Component[2]: selector=0x02, table=1(DC),1(AC) Component[2]: selector=0x02, table=1(DC),1(AC)
Component[3]: selector=0x03, table=1(DC),1(AC) Component[3]: selector=0x03, table=1(DC),1(AC)
Spectral selection = 0 .. 63 Spectral selection = 0 .. 63
Successive approximation = 0x00 Successive approximation = 0x00
*** Decoding SCAN Data *** *** Decoding SCAN Data ***
OFFSET: 0x00002DCD OFFSET: 0x00002DCD
Scan Decode Mode: No IDCT (DC only) Scan Decode Mode: No IDCT (DC only)
NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT] NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT]
Expect Restart interval elapsed @ 0x00008802.4 Expect Restart interval elapsed @ 0x00008802.4
ERROR: Restart marker not detected ERROR: Restart marker not detected
*** ERROR: Can't find huffman bitstring @ 0x00008802.5, table 0, value [0xffffffe0] *** ERROR: Can't find huffman bitstring @ 0x00008802.5, table 0, value [0xffffffe0]
*** ERROR: Bad huffman code @ 0x00008802.4 *** ERROR: Bad huffman code @ 0x00008802.4
*** ERROR: Bad scan data in MCU(0,15): Lum CSS(0,0) @ Offset 0x00008802.5 *** ERROR: Bad scan data in MCU(0,15): Lum CSS(0,0) @ Offset 0x00008802.5
MCU located at pixel=(0,240) MCU located at pixel=(0,240)
*** ERROR: Can't find huffman bitstring @ 0x00008802.6, table 0, value [0xffffffc0] *** ERROR: Can't find huffman bitstring @ 0x00008802.6, table 0, value [0xffffffc0]
*** ERROR: Bad huffman code @ 0x00008802.5 *** ERROR: Bad huffman code @ 0x00008802.5
*** ERROR: Bad scan data in MCU(0,15): Lum CSS(1,0) @ Offset 0x00008802.6 *** ERROR: Bad scan data in MCU(0,15): Lum CSS(1,0) @ Offset 0x00008802.6
MCU located at pixel=(8,240) MCU located at pixel=(8,240)
*** ERROR: Can't find huffman bitstring @ 0x00008802.7, table 0, value [0xffffff80] *** ERROR: Can't find huffman bitstring @ 0x00008802.7, table 0, value [0xffffff80]
*** ERROR: Bad huffman code @ 0x00008802.6 *** ERROR: Bad huffman code @ 0x00008802.6
*** ERROR: Bad scan data in MCU(0,15): Lum CSS(0,1) @ Offset 0x00008802.7 *** ERROR: Bad scan data in MCU(0,15): Lum CSS(0,1) @ Offset 0x00008802.7
MCU located at pixel=(0,248) MCU located at pixel=(0,248)
*** ERROR: Can't find huffman bitstring @ 0x00008803.0, table 0, value [0xffffffff] *** ERROR: Can't find huffman bitstring @ 0x00008803.0, table 0, value [0xffffffff]
*** ERROR: Bad huffman code @ 0x00008802.7 *** ERROR: Bad huffman code @ 0x00008802.7
*** ERROR: Bad scan data in MCU(0,15): Lum CSS(1,1) @ Offset 0x00008803.0 *** ERROR: Bad scan data in MCU(0,15): Lum CSS(1,1) @ Offset 0x00008803.0
MCU located at pixel=(8,248) MCU located at pixel=(8,248)
*** ERROR: Can't find huffman bitstring @ 0x00008803.1, table 1, value [0xfffffffe] *** ERROR: Can't find huffman bitstring @ 0x00008803.1, table 1, value [0xfffffffe]
*** ERROR: Bad huffman code @ 0x00008803.0 *** ERROR: Bad huffman code @ 0x00008803.0
*** ERROR: Bad scan data in MCU(0,15): Chr(Cb) CSS(0,0) @ Offset 0x00008803.1 *** ERROR: Bad scan data in MCU(0,15): Chr(Cb) CSS(0,0) @ Offset 0x00008803.1
MCU located at pixel=(0,240) MCU located at pixel=(0,240)
*** ERROR: Can't find huffman bitstring @ 0x00008803.2, table 1, value [0xfffffffc] *** ERROR: Can't find huffman bitstring @ 0x00008803.2, table 1, value [0xfffffffc]
*** ERROR: Bad huffman code @ 0x00008803.1 *** ERROR: Bad huffman code @ 0x00008803.1
*** ERROR: Bad scan data in MCU(0,15): Chr(Cr) CSS(0,0) @ Offset 0x00008803.2 *** ERROR: Bad scan data in MCU(0,15): Chr(Cr) CSS(0,0) @ Offset 0x00008803.2
MCU located at pixel=(0,240) MCU located at pixel=(0,240)
*** ERROR: Can't find huffman bitstring @ 0x00008803.3, table 0, value [0xfffffff8] *** ERROR: Can't find huffman bitstring @ 0x00008803.3, table 0, value [0xfffffff8]
*** ERROR: Bad huffman code @ 0x00008803.2 *** ERROR: Bad huffman code @ 0x00008803.2
Only reported first 20 instances of this message... Only reported first 20 instances of this message...
Compression stats: Compression stats:
Compression Ratio: 14.80:1 Compression Ratio: 14.80:1
Bits per pixel: 1.62:1 Bits per pixel: 1.62:1
Huffman code histogram stats: Huffman code histogram stats:
Huffman Table: (Dest ID: 0, Class: DC) Huffman Table: (Dest ID: 0, Class: DC)
# codes of length 01 bits: 40 ( 1%) # codes of length 01 bits: 40 ( 1%)
# codes of length 02 bits: 202 ( 4%) # codes of length 02 bits: 202 ( 4%)
# codes of length 03 bits: 3515 ( 73%) # codes of length 03 bits: 3515 ( 73%)
# codes of length 04 bits: 423 ( 9%) # codes of length 04 bits: 423 ( 9%)
# codes of length 05 bits: 338 ( 7%) # codes of length 05 bits: 338 ( 7%)
# codes of length 06 bits: 228 ( 5%) # codes of length 06 bits: 228 ( 5%)
# codes of length 07 bits: 54 ( 1%) # codes of length 07 bits: 54 ( 1%)
# codes of length 08 bits: 0 ( 0%) # codes of length 08 bits: 0 ( 0%)
# codes of length 09 bits: 0 ( 0%) # codes of length 09 bits: 0 ( 0%)
# codes of length 10 bits: 0 ( 0%) # codes of length 10 bits: 0 ( 0%)
# codes of length 11 bits: 0 ( 0%) # codes of length 11 bits: 0 ( 0%)
# codes of length 12 bits: 0 ( 0%) # codes of length 12 bits: 0 ( 0%)
# codes of length 13 bits: 0 ( 0%) # codes of length 13 bits: 0 ( 0%)
# codes of length 14 bits: 0 ( 0%) # codes of length 14 bits: 0 ( 0%)
# codes of length 15 bits: 0 ( 0%) # codes of length 15 bits: 0 ( 0%)
# codes of length 16 bits: 0 ( 0%) # codes of length 16 bits: 0 ( 0%)
Huffman Table: (Dest ID: 1, Class: DC) Huffman Table: (Dest ID: 1, Class: DC)
# codes of length 01 bits: 20 ( 1%) # codes of length 01 bits: 20 ( 1%)
# codes of length 02 bits: 1657 ( 69%) # codes of length 02 bits: 1657 ( 69%)
# codes of length 03 bits: 311 ( 13%) # codes of length 03 bits: 311 ( 13%)
# codes of length 04 bits: 232 ( 10%) # codes of length 04 bits: 232 ( 10%)
# codes of length 05 bits: 123 ( 5%) # codes of length 05 bits: 123 ( 5%)
# codes of length 06 bits: 49 ( 2%) # codes of length 06 bits: 49 ( 2%)
# codes of length 07 bits: 8 ( 0%) # codes of length 07 bits: 8 ( 0%)
# codes of length 08 bits: 0 ( 0%) # codes of length 08 bits: 0 ( 0%)
# codes of length 09 bits: 0 ( 0%) # codes of length 09 bits: 0 ( 0%)
# codes of length 10 bits: 0 ( 0%) # codes of length 10 bits: 0 ( 0%)
# codes of length 11 bits: 0 ( 0%) # codes of length 11 bits: 0 ( 0%)
# codes of length 12 bits: 0 ( 0%) # codes of length 12 bits: 0 ( 0%)
# codes of length 13 bits: 0 ( 0%) # codes of length 13 bits: 0 ( 0%)
# codes of length 14 bits: 0 ( 0%) # codes of length 14 bits: 0 ( 0%)
# codes of length 15 bits: 0 ( 0%) # codes of length 15 bits: 0 ( 0%)
# codes of length 16 bits: 0 ( 0%) # codes of length 16 bits: 0 ( 0%)
Huffman Table: (Dest ID: 0, Class: AC) Huffman Table: (Dest ID: 0, Class: AC)
# codes of length 01 bits: 0 ( 0%) # codes of length 01 bits: 0 ( 0%)
# codes of length 02 bits: 32135 ( 43%) # codes of length 02 bits: 32135 ( 43%)
# codes of length 03 bits: 8668 ( 12%) # codes of length 03 bits: 8668 ( 12%)
# codes of length 04 bits: 15771 ( 21%) # codes of length 04 bits: 15771 ( 21%)
# codes of length 05 bits: 7559 ( 10%) # codes of length 05 bits: 7559 ( 10%)
# codes of length 06 bits: 2518 ( 3%) # codes of length 06 bits: 2518 ( 3%)
# codes of length 07 bits: 3834 ( 5%) # codes of length 07 bits: 3834 ( 5%)
# codes of length 08 bits: 1387 ( 2%) # codes of length 08 bits: 1387 ( 2%)
# codes of length 09 bits: 1122 ( 2%) # codes of length 09 bits: 1122 ( 2%)
# codes of length 10 bits: 562 ( 1%) # codes of length 10 bits: 562 ( 1%)
# codes of length 11 bits: 234 ( 0%) # codes of length 11 bits: 234 ( 0%)
# codes of length 12 bits: 131 ( 0%) # codes of length 12 bits: 131 ( 0%)
# codes of length 13 bits: 0 ( 0%) # codes of length 13 bits: 0 ( 0%)
# codes of length 14 bits: 0 ( 0%) # codes of length 14 bits: 0 ( 0%)
# codes of length 15 bits: 57 ( 0%) # codes of length 15 bits: 57 ( 0%)
# codes of length 16 bits: 286 ( 0%) # codes of length 16 bits: 286 ( 0%)
Huffman Table: (Dest ID: 1, Class: AC) Huffman Table: (Dest ID: 1, Class: AC)
# codes of length 01 bits: 0 ( 0%) # codes of length 01 bits: 0 ( 0%)
# codes of length 02 bits: 4525 ( 57%) # codes of length 02 bits: 4525 ( 57%)
# codes of length 03 bits: 1153 ( 14%) # codes of length 03 bits: 1153 ( 14%)
# codes of length 04 bits: 1341 ( 17%) # codes of length 04 bits: 1341 ( 17%)
# codes of length 05 bits: 543 ( 7%) # codes of length 05 bits: 543 ( 7%)
# codes of length 06 bits: 281 ( 4%) # codes of length 06 bits: 281 ( 4%)
# codes of length 07 bits: 14 ( 0%) # codes of length 07 bits: 14 ( 0%)
# codes of length 08 bits: 93 ( 1%) # codes of length 08 bits: 93 ( 1%)
# codes of length 09 bits: 23 ( 0%) # codes of length 09 bits: 23 ( 0%)
# codes of length 10 bits: 3 ( 0%) # codes of length 10 bits: 3 ( 0%)
# codes of length 11 bits: 3 ( 0%) # codes of length 11 bits: 3 ( 0%)
# codes of length 12 bits: 0 ( 0%) # codes of length 12 bits: 0 ( 0%)
# codes of length 13 bits: 0 ( 0%) # codes of length 13 bits: 0 ( 0%)
# codes of length 14 bits: 2 ( 0%) # codes of length 14 bits: 2 ( 0%)
# codes of length 15 bits: 0 ( 0%) # codes of length 15 bits: 0 ( 0%)
# codes of length 16 bits: 0 ( 0%) # codes of length 16 bits: 0 ( 0%)
YCC clipping in DC: YCC clipping in DC:
Y component: [<0= 0] [>255= 0] Y component: [<0= 0] [>255= 0]
Cb component: [<0= 0] [>255= 0] Cb component: [<0= 0] [>255= 0]
Cr component: [<0= 0] [>255= 0] Cr component: [<0= 0] [>255= 0]
RGB clipping in DC: RGB clipping in DC:
R component: [<0= 0] [>255= 0] R component: [<0= 0] [>255= 0]
G component: [<0= 0] [>255= 0] G component: [<0= 0] [>255= 0]
B component: [<0= 0] [>255= 0] B component: [<0= 0] [>255= 0]
Average Pixel Luminance (Y): Average Pixel Luminance (Y):
Y=[103] (range: 0..255) Y=[103] (range: 0..255)
Brightest Pixel Search: Brightest Pixel Search:
YCC=[ 1014, -3, -27] RGB=[248,255,252] @ MCU[ 0, 13] YCC=[ 1014, -3, -27] RGB=[248,255,252] @ MCU[ 0, 13]
Finished Decoding SCAN Data Finished Decoding SCAN Data
Number of RESTART markers decoded: 1 Number of RESTART markers decoded: 1
Next position in scan buffer: Offset 0x0001210E.0 Next position in scan buffer: Offset 0x0001210E.0
*** Skipped 10 marker pad bytes *** *** Skipped 10 marker pad bytes ***
*** Marker: RST# *** *** Marker: RST# ***
OFFSET: 0x0000880D OFFSET: 0x0000880D
WARNING: Restart marker [0xFFD0] detected outside scan WARNING: Restart marker [0xFFD0] detected outside scan
Stopping decode Stopping decode
Use [Img Search Fwd/Rev] to locate other valid embedded JPEGs Use [Img Search Fwd/Rev] to locate other valid embedded JPEGs
*** Searching Compression Signatures *** *** Searching Compression Signatures ***
Signature: 013BA18D5561625796E986FDBC09F846 Signature: 013BA18D5561625796E986FDBC09F846
Signature (Rotated): 01AC57E12793DFA7C46C704625C5AF0F Signature (Rotated): 01AC57E12793DFA7C46C704625C5AF0F
File Offset: 0 bytes File Offset: 0 bytes
Chroma subsampling: 2x2 Chroma subsampling: 2x2
EXIF Make/Model: NONE EXIF Make/Model: NONE
EXIF Makernotes: NONE EXIF Makernotes: NONE
EXIF Software: NONE EXIF Software: NONE
Searching Compression Signatures: (3347 built-in, 0 user(*) ) Searching Compression Signatures: (3347 built-in, 0 user(*) )
EXIF.Make / Software EXIF.Model Quality Subsamp Match? EXIF.Make / Software EXIF.Model Quality Subsamp Match?
------------------------- ----------------------------------- ---------------- -------------- ------------------------- ----------------------------------- ---------------- --------------
CAM:[??? ] [Treo 680 ] [ ] Yes CAM:[??? ] [Treo 680 ] [ ] Yes
CAM:[Canon ] [Canon PowerShot Pro1 ] [fine ] No CAM:[Canon ] [Canon PowerShot Pro1 ] [fine ] No
CAM:[NIKON ] [E2500 ] [FINE ] No CAM:[NIKON ] [E2500 ] [FINE ] No
CAM:[NIKON ] [E3100 ] [FINE ] No CAM:[NIKON ] [E3100 ] [FINE ] No
CAM:[NIKON ] [E4500 ] [FINE ] No CAM:[NIKON ] [E4500 ] [FINE ] No
CAM:[NIKON ] [E5000 ] [FINE ] No CAM:[NIKON ] [E5000 ] [FINE ] No
CAM:[NIKON ] [E5700 ] [FINE ] No CAM:[NIKON ] [E5700 ] [FINE ] No
CAM:[NIKON ] [E775 ] [FINE ] No CAM:[NIKON ] [E775 ] [FINE ] No
CAM:[NIKON ] [E885 ] [FINE ] No CAM:[NIKON ] [E885 ] [FINE ] No
CAM:[OLYMPUS OPTICAL CO.,LTD ] [C3040Z ] [ ] No CAM:[OLYMPUS OPTICAL CO.,LTD ] [C3040Z ] [ ] No
CAM:[PENTAX ] [PENTAX Optio 550 ] [ ] No CAM:[PENTAX ] [PENTAX Optio 550 ] [ ] No
CAM:[Research In Motion ] [BlackBerry 9530 ] [Superfine ] Yes CAM:[Research In Motion ] [BlackBerry 9530 ] [Superfine ] Yes
CAM:[SEIKO EPSON CORP. ] [PhotoPC 3000Z ] [ ] No CAM:[SEIKO EPSON CORP. ] [PhotoPC 3000Z ] [ ] No
CAM:[SONY ] [DSC-H7 ] [ ] No CAM:[SONY ] [DSC-H7 ] [ ] No
CAM:[SONY ] [DSC-H9 ] [ ] No CAM:[SONY ] [DSC-H9 ] [ ] No
CAM:[SONY ] [DSC-S90 ] [ ] No CAM:[SONY ] [DSC-S90 ] [ ] No
CAM:[SONY ] [DSC-W1 ] [ ] No CAM:[SONY ] [DSC-W1 ] [ ] No
CAM:[SONY ] [SONY ] [ ] No CAM:[SONY ] [SONY ] [ ] No
SW :[ACDSee ] [ ] SW :[ACDSee ] [ ]
SW :[FixFoto ] [fine ] SW :[FixFoto ] [fine ]
SW :[IJG Library ] [090 ] SW :[IJG Library ] [090 ]
SW :[ZoomBrowser EX ] [high ] SW :[ZoomBrowser EX ] [high ]
The following IJG-based editors also match this signature: The following IJG-based editors also match this signature:
SW :[GIMP ] [090 ] SW :[GIMP ] [090 ]
SW :[IrfanView ] [090 ] SW :[IrfanView ] [090 ]
SW :[idImager ] [090 ] SW :[idImager ] [090 ]
SW :[FastStone Image Viewer ] [090 ] SW :[FastStone Image Viewer ] [090 ]
SW :[NeatImage ] [090 ] SW :[NeatImage ] [090 ]
SW :[Paint.NET ] [090 ] SW :[Paint.NET ] [090 ]
SW :[Photomatix ] [090 ] SW :[Photomatix ] [090 ]
SW :[XnView ] [090 ] SW :[XnView ] [090 ]
Based on the analysis of compression characteristics and EXIF metadata: Based on the analysis of compression characteristics and EXIF metadata:
ASSESSMENT: Class 1 - Image is processed/edited ASSESSMENT: Class 1 - Image is processed/edited
This may be a new software editor for the database. This may be a new software editor for the database.
If this file is processed, and editor doesn't appear in list above, If this file is processed, and editor doesn't appear in list above,
PLEASE ADD TO DATABASE with [Tools->Add Camera to DB] PLEASE ADD TO DATABASE with [Tools->Add Camera to DB]
*** Additional Info *** *** Additional Info ***
NOTE: Data exists after EOF, range: 0x00000000-0x00012301 (74497 bytes) NOTE: Data exists after EOF, range: 0x00000000-0x00012301 (74497 bytes)

Loading…
Cancel
Save