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
*.svg text eol=lf
*.targets text eol=lf
*.tt text eol=lf
*.tt text eol=crlf
*.ttinclude text eol=crlf
*.txt text eol=lf
*.vb text eol=lf

9
.gitignore

@ -204,8 +204,6 @@ FakesAssemblies/
**/node_modules
**/node_modules/*
**/Images/ActualOutput
**/Images/ReferenceOutput
# ASP.NET 5
project.lock.json
@ -218,7 +216,8 @@ artifacts/
*.csproj.bak
#CodeCoverage
**/CodeCoverage/*
docs/
/samples/AvatarWithRoundedCorner/output
/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)
- [.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
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
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.
// Licensed under the Apache License, Version 2.0.
using System.Buffers;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Combines two images together by blending the pixels.
/// </summary>
public class DrawImageProcessor : IImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor"/> class.
/// </summary>
/// <param name="image">The image to blend.</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="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
/// <param name="opacity">The opacity of the image to blend.</param>
public DrawImageProcessor(
Image image,
Point location,
PixelColorBlendingMode colorBlendingMode,
PixelAlphaCompositionMode alphaCompositionMode,
float opacity)
{
this.Image = image;
this.Location = location;
this.ColorBlendingMode = colorBlendingMode;
this.AlphaCompositionMode = alphaCompositionMode;
this.Opacity = opacity;
}
/// <summary>
/// Gets the image to blend.
/// </summary>
public Image Image { get; }
/// <summary>
/// Gets the location to draw the blended image.
/// </summary>
public Point Location { get; }
/// <summary>
/// Gets the blending mode to use when drawing the image.
/// </summary>
public PixelColorBlendingMode ColorBlendingMode { get; }
/// <summary>
/// Gets the Alpha blending mode to use when drawing the image.
/// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode { get; }
/// <summary>
/// Gets the opacity of the image to blend.
/// </summary>
public float Opacity { get; }
/// <inheritdoc />
public IImageProcessor<TPixelBg> CreatePixelSpecificProcessor<TPixelBg>()
where TPixelBg : struct, IPixel<TPixelBg>
{
var visitor = new ProcessorFactoryVisitor<TPixelBg>(this);
this.Image.AcceptVisitor(visitor);
return visitor.Result;
}
private class ProcessorFactoryVisitor<TPixelBg> : IImageVisitor
where TPixelBg : struct, IPixel<TPixelBg>
{
private readonly DrawImageProcessor definition;
public ProcessorFactoryVisitor(DrawImageProcessor definition)
{
this.definition = definition;
}
public IImageProcessor<TPixelBg> Result { get; private set; }
public void Visit<TPixelFg>(Image<TPixelFg> image)
where TPixelFg : struct, IPixel<TPixelFg>
{
this.Result = new DrawImageProcessor<TPixelBg, TPixelFg>(
image,
this.definition.Location,
this.definition.ColorBlendingMode,
this.definition.AlphaCompositionMode,
this.definition.Opacity);
}
}
}
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Buffers;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
/// <summary>
/// Combines two images together by blending the pixels.
/// </summary>
public class DrawImageProcessor : IImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor"/> class.
/// </summary>
/// <param name="image">The image to blend.</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="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
/// <param name="opacity">The opacity of the image to blend.</param>
public DrawImageProcessor(
Image image,
Point location,
PixelColorBlendingMode colorBlendingMode,
PixelAlphaCompositionMode alphaCompositionMode,
float opacity)
{
this.Image = image;
this.Location = location;
this.ColorBlendingMode = colorBlendingMode;
this.AlphaCompositionMode = alphaCompositionMode;
this.Opacity = opacity;
}
/// <summary>
/// Gets the image to blend.
/// </summary>
public Image Image { get; }
/// <summary>
/// Gets the location to draw the blended image.
/// </summary>
public Point Location { get; }
/// <summary>
/// Gets the blending mode to use when drawing the image.
/// </summary>
public PixelColorBlendingMode ColorBlendingMode { get; }
/// <summary>
/// Gets the Alpha blending mode to use when drawing the image.
/// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode { get; }
/// <summary>
/// Gets the opacity of the image to blend.
/// </summary>
public float Opacity { get; }
/// <inheritdoc />
public IImageProcessor<TPixelBg> CreatePixelSpecificProcessor<TPixelBg>()
where TPixelBg : struct, IPixel<TPixelBg>
{
var visitor = new ProcessorFactoryVisitor<TPixelBg>(this);
this.Image.AcceptVisitor(visitor);
return visitor.Result;
}
private class ProcessorFactoryVisitor<TPixelBg> : IImageVisitor
where TPixelBg : struct, IPixel<TPixelBg>
{
private readonly DrawImageProcessor definition;
public ProcessorFactoryVisitor(DrawImageProcessor definition)
{
this.definition = definition;
}
public IImageProcessor<TPixelBg> Result { get; private set; }
public void Visit<TPixelFg>(Image<TPixelFg> image)
where TPixelFg : struct, IPixel<TPixelFg>
{
this.Result = new DrawImageProcessor<TPixelBg, TPixelFg>(
image,
this.definition.Location,
this.definition.ColorBlendingMode,
this.definition.AlphaCompositionMode,
this.definition.Opacity);
}
}
}
}

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

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

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

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

326
src/ImageSharp/Configuration.cs

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

402
src/ImageSharp/Formats/ImageFormatManager.cs

@ -1,201 +1,201 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// Collection of Image Formats to be used in <see cref="Configuration" /> class.
/// </summary>
public class ImageFormatManager
{
/// <summary>
/// Used for locking against as there is no ConcurrentSet type.
/// <see href="https://github.com/dotnet/corefx/issues/6318"/>
/// </summary>
private static readonly object HashLock = new object();
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary>
private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new ConcurrentDictionary<IImageFormat, IImageEncoder>();
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary>
private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new ConcurrentDictionary<IImageFormat, IImageDecoder>();
/// <summary>
/// The list of supported <see cref="IImageFormat"/>s.
/// </summary>
private readonly HashSet<IImageFormat> imageFormats = new HashSet<IImageFormat>();
/// <summary>
/// The list of supported <see cref="IImageFormatDetector"/>s.
/// </summary>
private ConcurrentBag<IImageFormatDetector> imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>();
/// <summary>
/// Initializes a new instance of the <see cref="ImageFormatManager" /> class.
/// </summary>
public ImageFormatManager()
{
}
/// <summary>
/// Gets the maximum header size of all the formats.
/// </summary>
internal int MaxHeaderSize { get; private set; }
/// <summary>
/// Gets the currently registered <see cref="IImageFormat"/>s.
/// </summary>
public IEnumerable<IImageFormat> ImageFormats => this.imageFormats;
/// <summary>
/// Gets the currently registered <see cref="IImageFormatDetector"/>s.
/// </summary>
internal IEnumerable<IImageFormatDetector> FormatDetectors => this.imageFormatDetectors;
/// <summary>
/// Gets the currently registered <see cref="IImageDecoder"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageDecoder>> ImageDecoders => this.mimeTypeDecoders;
/// <summary>
/// Gets the currently registered <see cref="IImageEncoder"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageEncoder>> ImageEncoders => this.mimeTypeEncoders;
/// <summary>
/// Registers a new format provider.
/// </summary>
/// <param name="format">The format to register as a known format.</param>
public void AddImageFormat(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes));
Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions));
lock (HashLock)
{
if (!this.imageFormats.Contains(format))
{
this.imageFormats.Add(format);
}
}
}
/// <summary>
/// For the specified file extensions type find the e <see cref="IImageFormat"/>.
/// </summary>
/// <param name="extension">The extension to discover</param>
/// <returns>The <see cref="IImageFormat"/> if found otherwise null</returns>
public IImageFormat FindFormatByFileExtension(string extension)
{
Guard.NotNullOrWhiteSpace(extension, nameof(extension));
if (extension[0] == '.')
{
extension = extension.Substring(1);
}
return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase));
}
/// <summary>
/// For the specified mime type find the <see cref="IImageFormat"/>.
/// </summary>
/// <param name="mimeType">The mime-type to discover</param>
/// <returns>The <see cref="IImageFormat"/> if found; otherwise null</returns>
public IImageFormat FindFormatByMimeType(string mimeType)
{
return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase));
}
/// <summary>
/// Sets a specific image encoder as the encoder for a specific image format.
/// </summary>
/// <param name="imageFormat">The image format to register the encoder for.</param>
/// <param name="encoder">The encoder to use,</param>
public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder)
{
Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(encoder, nameof(encoder));
this.AddImageFormat(imageFormat);
this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder);
}
/// <summary>
/// Sets a specific image decoder as the decoder for a specific image format.
/// </summary>
/// <param name="imageFormat">The image format to register the encoder for.</param>
/// <param name="decoder">The decoder to use,</param>
public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder)
{
Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(decoder, nameof(decoder));
this.AddImageFormat(imageFormat);
this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder);
}
/// <summary>
/// Removes all the registered image format detectors.
/// </summary>
public void ClearImageFormatDetectors()
{
this.imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>();
}
/// <summary>
/// Adds a new detector for detecting mime types.
/// </summary>
/// <param name="detector">The detector to add</param>
public void AddImageFormatDetector(IImageFormatDetector detector)
{
Guard.NotNull(detector, nameof(detector));
this.imageFormatDetectors.Add(detector);
this.SetMaxHeaderSize();
}
/// <summary>
/// For the specified mime type find the decoder.
/// </summary>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageDecoder"/> if found otherwise null</returns>
public IImageDecoder FindDecoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
return this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder)
? decoder
: null;
}
/// <summary>
/// For the specified mime type find the encoder.
/// </summary>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageEncoder"/> if found otherwise null</returns>
public IImageEncoder FindEncoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
return this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder)
? encoder
: null;
}
/// <summary>
/// Sets the max header size.
/// </summary>
private void SetMaxHeaderSize()
{
this.MaxHeaderSize = this.imageFormatDetectors.Max(x => x.HeaderSize);
}
}
}
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace SixLabors.ImageSharp.Formats
{
/// <summary>
/// Collection of Image Formats to be used in <see cref="Configuration" /> class.
/// </summary>
public class ImageFormatManager
{
/// <summary>
/// Used for locking against as there is no ConcurrentSet type.
/// <see href="https://github.com/dotnet/corefx/issues/6318"/>
/// </summary>
private static readonly object HashLock = new object();
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary>
private readonly ConcurrentDictionary<IImageFormat, IImageEncoder> mimeTypeEncoders = new ConcurrentDictionary<IImageFormat, IImageEncoder>();
/// <summary>
/// The list of supported <see cref="IImageEncoder"/> keyed to mime types.
/// </summary>
private readonly ConcurrentDictionary<IImageFormat, IImageDecoder> mimeTypeDecoders = new ConcurrentDictionary<IImageFormat, IImageDecoder>();
/// <summary>
/// The list of supported <see cref="IImageFormat"/>s.
/// </summary>
private readonly HashSet<IImageFormat> imageFormats = new HashSet<IImageFormat>();
/// <summary>
/// The list of supported <see cref="IImageFormatDetector"/>s.
/// </summary>
private ConcurrentBag<IImageFormatDetector> imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>();
/// <summary>
/// Initializes a new instance of the <see cref="ImageFormatManager" /> class.
/// </summary>
public ImageFormatManager()
{
}
/// <summary>
/// Gets the maximum header size of all the formats.
/// </summary>
internal int MaxHeaderSize { get; private set; }
/// <summary>
/// Gets the currently registered <see cref="IImageFormat"/>s.
/// </summary>
public IEnumerable<IImageFormat> ImageFormats => this.imageFormats;
/// <summary>
/// Gets the currently registered <see cref="IImageFormatDetector"/>s.
/// </summary>
internal IEnumerable<IImageFormatDetector> FormatDetectors => this.imageFormatDetectors;
/// <summary>
/// Gets the currently registered <see cref="IImageDecoder"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageDecoder>> ImageDecoders => this.mimeTypeDecoders;
/// <summary>
/// Gets the currently registered <see cref="IImageEncoder"/>s.
/// </summary>
internal IEnumerable<KeyValuePair<IImageFormat, IImageEncoder>> ImageEncoders => this.mimeTypeEncoders;
/// <summary>
/// Registers a new format provider.
/// </summary>
/// <param name="format">The format to register as a known format.</param>
public void AddImageFormat(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
Guard.NotNull(format.MimeTypes, nameof(format.MimeTypes));
Guard.NotNull(format.FileExtensions, nameof(format.FileExtensions));
lock (HashLock)
{
if (!this.imageFormats.Contains(format))
{
this.imageFormats.Add(format);
}
}
}
/// <summary>
/// For the specified file extensions type find the e <see cref="IImageFormat"/>.
/// </summary>
/// <param name="extension">The extension to discover</param>
/// <returns>The <see cref="IImageFormat"/> if found otherwise null</returns>
public IImageFormat FindFormatByFileExtension(string extension)
{
Guard.NotNullOrWhiteSpace(extension, nameof(extension));
if (extension[0] == '.')
{
extension = extension.Substring(1);
}
return this.imageFormats.FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase));
}
/// <summary>
/// For the specified mime type find the <see cref="IImageFormat"/>.
/// </summary>
/// <param name="mimeType">The mime-type to discover</param>
/// <returns>The <see cref="IImageFormat"/> if found; otherwise null</returns>
public IImageFormat FindFormatByMimeType(string mimeType)
{
return this.imageFormats.FirstOrDefault(x => x.MimeTypes.Contains(mimeType, StringComparer.OrdinalIgnoreCase));
}
/// <summary>
/// Sets a specific image encoder as the encoder for a specific image format.
/// </summary>
/// <param name="imageFormat">The image format to register the encoder for.</param>
/// <param name="encoder">The encoder to use,</param>
public void SetEncoder(IImageFormat imageFormat, IImageEncoder encoder)
{
Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(encoder, nameof(encoder));
this.AddImageFormat(imageFormat);
this.mimeTypeEncoders.AddOrUpdate(imageFormat, encoder, (s, e) => encoder);
}
/// <summary>
/// Sets a specific image decoder as the decoder for a specific image format.
/// </summary>
/// <param name="imageFormat">The image format to register the encoder for.</param>
/// <param name="decoder">The decoder to use,</param>
public void SetDecoder(IImageFormat imageFormat, IImageDecoder decoder)
{
Guard.NotNull(imageFormat, nameof(imageFormat));
Guard.NotNull(decoder, nameof(decoder));
this.AddImageFormat(imageFormat);
this.mimeTypeDecoders.AddOrUpdate(imageFormat, decoder, (s, e) => decoder);
}
/// <summary>
/// Removes all the registered image format detectors.
/// </summary>
public void ClearImageFormatDetectors()
{
this.imageFormatDetectors = new ConcurrentBag<IImageFormatDetector>();
}
/// <summary>
/// Adds a new detector for detecting mime types.
/// </summary>
/// <param name="detector">The detector to add</param>
public void AddImageFormatDetector(IImageFormatDetector detector)
{
Guard.NotNull(detector, nameof(detector));
this.imageFormatDetectors.Add(detector);
this.SetMaxHeaderSize();
}
/// <summary>
/// For the specified mime type find the decoder.
/// </summary>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageDecoder"/> if found otherwise null</returns>
public IImageDecoder FindDecoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
return this.mimeTypeDecoders.TryGetValue(format, out IImageDecoder decoder)
? decoder
: null;
}
/// <summary>
/// For the specified mime type find the encoder.
/// </summary>
/// <param name="format">The format to discover</param>
/// <returns>The <see cref="IImageEncoder"/> if found otherwise null</returns>
public IImageEncoder FindEncoder(IImageFormat format)
{
Guard.NotNull(format, nameof(format));
return this.mimeTypeEncoders.TryGetValue(format, out IImageEncoder encoder)
? encoder
: null;
}
/// <summary>
/// Sets the max header size.
/// </summary>
private void SetMaxHeaderSize()
{
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.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Options for influencing the drawing functions.
/// </summary>
public struct GraphicsOptions
{
/// <summary>
/// Represents the default <see cref="GraphicsOptions"/>.
/// </summary>
public static readonly GraphicsOptions Default = new GraphicsOptions(true);
private float? blendPercentage;
private int? antialiasSubpixelDepth;
private bool? antialias;
private PixelColorBlendingMode colorBlendingMode;
private PixelAlphaCompositionMode alphaCompositionMode;
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
public GraphicsOptions(bool enableAntialiasing)
{
this.colorBlendingMode = PixelColorBlendingMode.Normal;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = 1;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
/// <param name="opacity">blending percentage to apply to the drawing operation</param>
public GraphicsOptions(bool enableAntialiasing, float opacity)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = PixelColorBlendingMode.Normal;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary>
/// <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="blending">color blending mode to apply to the drawing operation</param>
public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, float opacity)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = blending;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary>
/// <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="blending">color blending 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)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = blending;
this.alphaCompositionMode = composition;
this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary>
/// Gets or sets a value indicating whether antialiasing should be applied.
/// </summary>
public bool Antialias
{
get => this.antialias ?? true;
set => this.antialias = value;
}
/// <summary>
/// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled.
/// </summary>
public int AntialiasSubpixelDepth
{
get => this.antialiasSubpixelDepth ?? 16;
set => this.antialiasSubpixelDepth = value;
}
/// <summary>
/// Gets or sets a value indicating the blending percentage to apply to the drawing operation
/// </summary>
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
// or some forms of PixelBlender factory for each pixel type. Will need
// some API thought post V1.
/// <summary>
/// Gets or sets a value indicating the color blending mode to apply to the drawing operation
/// </summary>
public PixelColorBlendingMode ColorBlendingMode
{
get => this.colorBlendingMode;
set => this.colorBlendingMode = value;
}
/// <summary>
/// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation
/// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode
{
get => this.alphaCompositionMode;
set => this.alphaCompositionMode = value;
}
/// <summary>
/// Evaluates if a given SOURCE color can completely replace a BACKDROP color given the current blending and composition settings.
/// </summary>
/// <param name="color">the color</param>
/// <returns>true if the color can be considered opaque</returns>
/// <remarks>
/// 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.
/// This method can be useful for such processors to select the fast path.
/// </remarks>
internal bool IsOpaqueColorWithoutBlending(Color color)
{
if (this.ColorBlendingMode != PixelColorBlendingMode.Normal)
{
return false;
}
if (this.AlphaCompositionMode != PixelAlphaCompositionMode.SrcOver &&
this.AlphaCompositionMode != PixelAlphaCompositionMode.Src)
{
return false;
}
if (this.BlendPercentage != 1f)
{
return false;
}
if (color.ToVector4().W != 1f)
{
return false;
}
return true;
}
}
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Options for influencing the drawing functions.
/// </summary>
public struct GraphicsOptions
{
/// <summary>
/// Represents the default <see cref="GraphicsOptions"/>.
/// </summary>
public static readonly GraphicsOptions Default = new GraphicsOptions(true);
private float? blendPercentage;
private int? antialiasSubpixelDepth;
private bool? antialias;
private PixelColorBlendingMode colorBlendingMode;
private PixelAlphaCompositionMode alphaCompositionMode;
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
public GraphicsOptions(bool enableAntialiasing)
{
this.colorBlendingMode = PixelColorBlendingMode.Normal;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = 1;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary>
/// <param name="enableAntialiasing">If set to <c>true</c> [enable antialiasing].</param>
/// <param name="opacity">blending percentage to apply to the drawing operation</param>
public GraphicsOptions(bool enableAntialiasing, float opacity)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = PixelColorBlendingMode.Normal;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary>
/// <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="blending">color blending mode to apply to the drawing operation</param>
public GraphicsOptions(bool enableAntialiasing, PixelColorBlendingMode blending, float opacity)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = blending;
this.alphaCompositionMode = PixelAlphaCompositionMode.SrcOver;
this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsOptions"/> struct.
/// </summary>
/// <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="blending">color blending 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)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.colorBlendingMode = blending;
this.alphaCompositionMode = composition;
this.blendPercentage = opacity;
this.antialiasSubpixelDepth = 16;
this.antialias = enableAntialiasing;
}
/// <summary>
/// Gets or sets a value indicating whether antialiasing should be applied.
/// </summary>
public bool Antialias
{
get => this.antialias ?? true;
set => this.antialias = value;
}
/// <summary>
/// Gets or sets a value indicating the number of subpixels to use while rendering with antialiasing enabled.
/// </summary>
public int AntialiasSubpixelDepth
{
get => this.antialiasSubpixelDepth ?? 16;
set => this.antialiasSubpixelDepth = value;
}
/// <summary>
/// Gets or sets a value indicating the blending percentage to apply to the drawing operation
/// </summary>
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
// or some forms of PixelBlender factory for each pixel type. Will need
// some API thought post V1.
/// <summary>
/// Gets or sets a value indicating the color blending mode to apply to the drawing operation
/// </summary>
public PixelColorBlendingMode ColorBlendingMode
{
get => this.colorBlendingMode;
set => this.colorBlendingMode = value;
}
/// <summary>
/// Gets or sets a value indicating the alpha composition mode to apply to the drawing operation
/// </summary>
public PixelAlphaCompositionMode AlphaCompositionMode
{
get => this.alphaCompositionMode;
set => this.alphaCompositionMode = value;
}
/// <summary>
/// Evaluates if a given SOURCE color can completely replace a BACKDROP color given the current blending and composition settings.
/// </summary>
/// <param name="color">the color</param>
/// <returns>true if the color can be considered opaque</returns>
/// <remarks>
/// 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.
/// This method can be useful for such processors to select the fast path.
/// </remarks>
internal bool IsOpaqueColorWithoutBlending(Color color)
{
if (this.ColorBlendingMode != PixelColorBlendingMode.Normal)
{
return false;
}
if (this.AlphaCompositionMode != PixelAlphaCompositionMode.SrcOver &&
this.AlphaCompositionMode != PixelAlphaCompositionMode.Src)
{
return false;
}
if (this.BlendPercentage != 1f)
{
return false;
}
if (color.ToVector4().W != 1f)
{
return false;
}
return true;
}
}
}

6
src/ImageSharp/PixelFormats/PixelAlphaCompositionMode.cs

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

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

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

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

@ -1,170 +1,170 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <summary>
/// Abstract base class for calling pixel composition functions
/// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam>
internal abstract class PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Blend 2 pixels together.
/// </summary>
/// <param name="background">The background color.</param>
/// <param name="source">The source color.</param>
/// <param name="amount">
/// 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.
/// </param>
/// <returns>The final pixel value after composition</returns>
public abstract TPixel Blend(TPixel background, TPixel source, float amount);
/// <summary>
/// Blend 2 rows together.
/// </summary>
/// <param name="destination">destination span</param>
/// <param name="background">the background span</param>
/// <param name="source">the source span</param>
/// <param name="amount">
/// 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.
/// </param>
protected abstract void BlendFunction(
Span<Vector4> destination,
ReadOnlySpan<Vector4> background,
ReadOnlySpan<Vector4> source,
float amount);
/// <summary>
/// Blend 2 rows together.
/// </summary>
/// <param name="destination">destination span</param>
/// <param name="background">the background span</param>
/// <param name="source">the source span</param>
/// <param name="amount">
/// 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.
/// </param>
protected abstract void BlendFunction(
Span<Vector4> destination,
ReadOnlySpan<Vector4> background,
ReadOnlySpan<Vector4> source,
ReadOnlySpan<float> amount);
/// <summary>
/// Blends 2 rows together
/// </summary>
/// <param name="configuration"><see cref="Configuration"/> to use internally</param>
/// <param name="destination">the destination span</param>
/// <param name="background">the background span</param>
/// <param name="source">the source span</param>
/// <param name="amount">
/// 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.
/// </param>
public void Blend(
Configuration configuration,
Span<TPixel> destination,
ReadOnlySpan<TPixel> background,
ReadOnlySpan<TPixel> source,
ReadOnlySpan<float> amount)
{
this.Blend<TPixel>(configuration, destination, background, source, amount);
}
/// <summary>
/// Blends 2 rows together
/// </summary>
/// <typeparam name="TPixelSrc">the pixel format of the source span</typeparam>
/// <param name="configuration"><see cref="Configuration"/> to use internally</param>
/// <param name="destination">the destination span</param>
/// <param name="background">the background span</param>
/// <param name="source">the source span</param>
/// <param name="amount">
/// 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.
/// </param>
public void Blend<TPixelSrc>(
Configuration configuration,
Span<TPixel> destination,
ReadOnlySpan<TPixel> background,
ReadOnlySpan<TPixelSrc> source,
ReadOnlySpan<float> amount)
where TPixelSrc : struct, IPixel<TPixelSrc>
{
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));
Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length));
using (IMemoryOwner<Vector4> buffer =
configuration.MemoryAllocator.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> backgroundSpan = buffer.Slice(destination.Length, destination.Length);
Span<Vector4> sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
ReadOnlySpan<TPixel> sourcePixels = background.Slice(0, background.Length);
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale);
ReadOnlySpan<TPixelSrc> sourcePixels1 = source.Slice(0, background.Length);
PixelOperations<TPixelSrc>.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale);
this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount);
Span<Vector4> sourceVectors = destinationSpan.Slice(0, background.Length);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale);
}
}
/// <summary>
/// Blends 2 rows together
/// </summary>
/// <typeparam name="TPixelSrc">the pixel format of the source span</typeparam>
/// <param name="configuration"><see cref="Configuration"/> to use internally</param>
/// <param name="destination">the destination span</param>
/// <param name="background">the background span</param>
/// <param name="source">the source span</param>
/// <param name="amount">
/// 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.
/// </param>
public void Blend<TPixelSrc>(
Configuration configuration,
Span<TPixel> destination,
ReadOnlySpan<TPixel> background,
ReadOnlySpan<TPixelSrc> source,
float amount)
where TPixelSrc : struct, IPixel<TPixelSrc>
{
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
using (IMemoryOwner<Vector4> buffer =
configuration.MemoryAllocator.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> backgroundSpan = buffer.Slice(destination.Length, destination.Length);
Span<Vector4> sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
ReadOnlySpan<TPixel> sourcePixels = background.Slice(0, background.Length);
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale);
ReadOnlySpan<TPixelSrc> sourcePixels1 = source.Slice(0, background.Length);
PixelOperations<TPixelSrc>.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale);
this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount);
Span<Vector4> sourceVectors = destinationSpan.Slice(0, background.Length);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale);
}
}
}
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <summary>
/// Abstract base class for calling pixel composition functions
/// </summary>
/// <typeparam name="TPixel">The type of the pixel</typeparam>
internal abstract class PixelBlender<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Blend 2 pixels together.
/// </summary>
/// <param name="background">The background color.</param>
/// <param name="source">The source color.</param>
/// <param name="amount">
/// 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.
/// </param>
/// <returns>The final pixel value after composition</returns>
public abstract TPixel Blend(TPixel background, TPixel source, float amount);
/// <summary>
/// Blend 2 rows together.
/// </summary>
/// <param name="destination">destination span</param>
/// <param name="background">the background span</param>
/// <param name="source">the source span</param>
/// <param name="amount">
/// 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.
/// </param>
protected abstract void BlendFunction(
Span<Vector4> destination,
ReadOnlySpan<Vector4> background,
ReadOnlySpan<Vector4> source,
float amount);
/// <summary>
/// Blend 2 rows together.
/// </summary>
/// <param name="destination">destination span</param>
/// <param name="background">the background span</param>
/// <param name="source">the source span</param>
/// <param name="amount">
/// 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.
/// </param>
protected abstract void BlendFunction(
Span<Vector4> destination,
ReadOnlySpan<Vector4> background,
ReadOnlySpan<Vector4> source,
ReadOnlySpan<float> amount);
/// <summary>
/// Blends 2 rows together
/// </summary>
/// <param name="configuration"><see cref="Configuration"/> to use internally</param>
/// <param name="destination">the destination span</param>
/// <param name="background">the background span</param>
/// <param name="source">the source span</param>
/// <param name="amount">
/// 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.
/// </param>
public void Blend(
Configuration configuration,
Span<TPixel> destination,
ReadOnlySpan<TPixel> background,
ReadOnlySpan<TPixel> source,
ReadOnlySpan<float> amount)
{
this.Blend<TPixel>(configuration, destination, background, source, amount);
}
/// <summary>
/// Blends 2 rows together
/// </summary>
/// <typeparam name="TPixelSrc">the pixel format of the source span</typeparam>
/// <param name="configuration"><see cref="Configuration"/> to use internally</param>
/// <param name="destination">the destination span</param>
/// <param name="background">the background span</param>
/// <param name="source">the source span</param>
/// <param name="amount">
/// 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.
/// </param>
public void Blend<TPixelSrc>(
Configuration configuration,
Span<TPixel> destination,
ReadOnlySpan<TPixel> background,
ReadOnlySpan<TPixelSrc> source,
ReadOnlySpan<float> amount)
where TPixelSrc : struct, IPixel<TPixelSrc>
{
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));
Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length));
using (IMemoryOwner<Vector4> buffer =
configuration.MemoryAllocator.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> backgroundSpan = buffer.Slice(destination.Length, destination.Length);
Span<Vector4> sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
ReadOnlySpan<TPixel> sourcePixels = background.Slice(0, background.Length);
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale);
ReadOnlySpan<TPixelSrc> sourcePixels1 = source.Slice(0, background.Length);
PixelOperations<TPixelSrc>.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale);
this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount);
Span<Vector4> sourceVectors = destinationSpan.Slice(0, background.Length);
PixelOperations<TPixel>.Instance.FromVector4Destructive(configuration, sourceVectors, destination, PixelConversionModifiers.Scale);
}
}
/// <summary>
/// Blends 2 rows together
/// </summary>
/// <typeparam name="TPixelSrc">the pixel format of the source span</typeparam>
/// <param name="configuration"><see cref="Configuration"/> to use internally</param>
/// <param name="destination">the destination span</param>
/// <param name="background">the background span</param>
/// <param name="source">the source span</param>
/// <param name="amount">
/// 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.
/// </param>
public void Blend<TPixelSrc>(
Configuration configuration,
Span<TPixel> destination,
ReadOnlySpan<TPixel> background,
ReadOnlySpan<TPixelSrc> source,
float amount)
where TPixelSrc : struct, IPixel<TPixelSrc>
{
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));
Guard.MustBeBetweenOrEqualTo(amount, 0, 1, nameof(amount));
using (IMemoryOwner<Vector4> buffer =
configuration.MemoryAllocator.Allocate<Vector4>(destination.Length * 3))
{
Span<Vector4> destinationSpan = buffer.Slice(0, destination.Length);
Span<Vector4> backgroundSpan = buffer.Slice(destination.Length, destination.Length);
Span<Vector4> sourceSpan = buffer.Slice(destination.Length * 2, destination.Length);
ReadOnlySpan<TPixel> sourcePixels = background.Slice(0, background.Length);
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourcePixels, backgroundSpan, PixelConversionModifiers.Scale);
ReadOnlySpan<TPixelSrc> sourcePixels1 = source.Slice(0, background.Length);
PixelOperations<TPixelSrc>.Instance.ToVector4(configuration, sourcePixels1, sourceSpan, PixelConversionModifiers.Scale);
this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount);
Span<Vector4> sourceVectors = destinationSpan.Slice(0, background.Length);
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;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <content>
@ -231,7 +230,6 @@ namespace SixLabors.ImageSharp.PixelFormats
{
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;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <content>
@ -231,7 +230,6 @@ namespace SixLabors.ImageSharp.PixelFormats
{
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;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <content>
@ -194,7 +193,6 @@ namespace SixLabors.ImageSharp.PixelFormats
{
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;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <content>
@ -194,7 +193,6 @@ namespace SixLabors.ImageSharp.PixelFormats
{
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;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <content>
@ -205,7 +204,6 @@ namespace SixLabors.ImageSharp.PixelFormats
{
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;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <content>
@ -194,7 +193,6 @@ namespace SixLabors.ImageSharp.PixelFormats
{
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;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <content>
@ -220,7 +219,6 @@ namespace SixLabors.ImageSharp.PixelFormats
{
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;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <content>
@ -194,7 +193,6 @@ namespace SixLabors.ImageSharp.PixelFormats
{
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.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats.PixelBlenders;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <content>
/// Provides access to pixel blenders
/// </content>
public partial class PixelOperations<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Find an instance of the pixel blender.
/// </summary>
/// <param name="options">the blending and composition to apply</param>
/// <returns>A <see cref="PixelBlender{TPixel}"/>.</returns>
internal PixelBlender<TPixel> GetPixelBlender(GraphicsOptions options)
{
return this.GetPixelBlender(options.ColorBlendingMode, options.AlphaCompositionMode);
}
/// <summary>
/// Find an instance of the pixel blender.
/// </summary>
/// <param name="colorMode">The color blending mode to apply</param>
/// <param name="alphaMode">The alpha composition mode to apply</param>
/// <returns>A <see cref="PixelBlender{TPixel}"/>.</returns>
internal virtual PixelBlender<TPixel> GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode)
{
switch (alphaMode)
{
case PixelAlphaCompositionMode.Clear:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyClear.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddClear.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractClear.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenClear.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenClear.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenClear.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayClear.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightClear.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalClear.Instance;
}
case PixelAlphaCompositionMode.Xor:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyXor.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddXor.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractXor.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenXor.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenXor.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenXor.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayXor.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightXor.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalXor.Instance;
}
case PixelAlphaCompositionMode.Src:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrc.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrc.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrc.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrc.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrc.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrc.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrc.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrc.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrc.Instance;
}
case PixelAlphaCompositionMode.SrcAtop:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcAtop.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcAtop.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcAtop.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcAtop.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcAtop.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcAtop.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcAtop.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcAtop.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcAtop.Instance;
}
case PixelAlphaCompositionMode.SrcIn:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcIn.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcIn.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcIn.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcIn.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcIn.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcIn.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcIn.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcIn.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcIn.Instance;
}
case PixelAlphaCompositionMode.SrcOut:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcOut.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcOut.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcOut.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcOut.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcOut.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcOut.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcOut.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcOut.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcOut.Instance;
}
case PixelAlphaCompositionMode.Dest:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDest.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDest.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDest.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDest.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDest.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDest.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDest.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDest.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDest.Instance;
}
case PixelAlphaCompositionMode.DestAtop:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestAtop.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestAtop.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestAtop.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestAtop.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestAtop.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestAtop.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestAtop.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestAtop.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestAtop.Instance;
}
case PixelAlphaCompositionMode.DestIn:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestIn.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestIn.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestIn.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestIn.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestIn.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestIn.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestIn.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestIn.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestIn.Instance;
}
case PixelAlphaCompositionMode.DestOut:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestOut.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestOut.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestOut.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestOut.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestOut.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestOut.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestOut.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestOut.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestOut.Instance;
}
case PixelAlphaCompositionMode.DestOver:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestOver.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestOver.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestOver.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestOver.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestOver.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestOver.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestOver.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestOver.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestOver.Instance;
}
case PixelAlphaCompositionMode.SrcOver:
default:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcOver.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcOver.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcOver.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcOver.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcOver.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcOver.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcOver.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcOver.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcOver.Instance;
}
}
}
}
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats.PixelBlenders;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <content>
/// Provides access to pixel blenders
/// </content>
public partial class PixelOperations<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Find an instance of the pixel blender.
/// </summary>
/// <param name="options">the blending and composition to apply</param>
/// <returns>A <see cref="PixelBlender{TPixel}"/>.</returns>
internal PixelBlender<TPixel> GetPixelBlender(GraphicsOptions options)
{
return this.GetPixelBlender(options.ColorBlendingMode, options.AlphaCompositionMode);
}
/// <summary>
/// Find an instance of the pixel blender.
/// </summary>
/// <param name="colorMode">The color blending mode to apply</param>
/// <param name="alphaMode">The alpha composition mode to apply</param>
/// <returns>A <see cref="PixelBlender{TPixel}"/>.</returns>
internal virtual PixelBlender<TPixel> GetPixelBlender(PixelColorBlendingMode colorMode, PixelAlphaCompositionMode alphaMode)
{
switch (alphaMode)
{
case PixelAlphaCompositionMode.Clear:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyClear.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddClear.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractClear.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenClear.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenClear.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenClear.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayClear.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightClear.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalClear.Instance;
}
case PixelAlphaCompositionMode.Xor:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyXor.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddXor.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractXor.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenXor.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenXor.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenXor.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayXor.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightXor.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalXor.Instance;
}
case PixelAlphaCompositionMode.Src:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrc.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrc.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrc.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrc.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrc.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrc.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrc.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrc.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrc.Instance;
}
case PixelAlphaCompositionMode.SrcAtop:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcAtop.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcAtop.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcAtop.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcAtop.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcAtop.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcAtop.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcAtop.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcAtop.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcAtop.Instance;
}
case PixelAlphaCompositionMode.SrcIn:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcIn.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcIn.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcIn.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcIn.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcIn.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcIn.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcIn.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcIn.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcIn.Instance;
}
case PixelAlphaCompositionMode.SrcOut:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcOut.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcOut.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcOut.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcOut.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcOut.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcOut.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcOut.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcOut.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcOut.Instance;
}
case PixelAlphaCompositionMode.Dest:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDest.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDest.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDest.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDest.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDest.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDest.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDest.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDest.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDest.Instance;
}
case PixelAlphaCompositionMode.DestAtop:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestAtop.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestAtop.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestAtop.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestAtop.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestAtop.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestAtop.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestAtop.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestAtop.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestAtop.Instance;
}
case PixelAlphaCompositionMode.DestIn:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestIn.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestIn.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestIn.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestIn.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestIn.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestIn.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestIn.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestIn.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestIn.Instance;
}
case PixelAlphaCompositionMode.DestOut:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestOut.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestOut.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestOut.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestOut.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestOut.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestOut.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestOut.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestOut.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestOut.Instance;
}
case PixelAlphaCompositionMode.DestOver:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplyDestOver.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddDestOver.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractDestOver.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenDestOver.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenDestOver.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenDestOver.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlayDestOver.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightDestOver.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalDestOver.Instance;
}
case PixelAlphaCompositionMode.SrcOver:
default:
switch (colorMode)
{
case PixelColorBlendingMode.Multiply: return DefaultPixelBlenders<TPixel>.MultiplySrcOver.Instance;
case PixelColorBlendingMode.Add: return DefaultPixelBlenders<TPixel>.AddSrcOver.Instance;
case PixelColorBlendingMode.Subtract: return DefaultPixelBlenders<TPixel>.SubtractSrcOver.Instance;
case PixelColorBlendingMode.Screen: return DefaultPixelBlenders<TPixel>.ScreenSrcOver.Instance;
case PixelColorBlendingMode.Darken: return DefaultPixelBlenders<TPixel>.DarkenSrcOver.Instance;
case PixelColorBlendingMode.Lighten: return DefaultPixelBlenders<TPixel>.LightenSrcOver.Instance;
case PixelColorBlendingMode.Overlay: return DefaultPixelBlenders<TPixel>.OverlaySrcOver.Instance;
case PixelColorBlendingMode.HardLight: return DefaultPixelBlenders<TPixel>.HardLightSrcOver.Instance;
case PixelColorBlendingMode.Normal:
default: return DefaultPixelBlenders<TPixel>.NormalSrcOver.Instance;
}
}
}
}
}

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

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

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

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

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

@ -1,56 +1,56 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
{
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Xunit;
public class PorterDuffCompositorTests
{
// TODO: Add other modes to compare.
public static readonly TheoryData<PixelAlphaCompositionMode> CompositingOperators =
new TheoryData<PixelAlphaCompositionMode>
{
PixelAlphaCompositionMode.Src,
PixelAlphaCompositionMode.SrcAtop,
PixelAlphaCompositionMode.SrcOver,
PixelAlphaCompositionMode.SrcIn,
PixelAlphaCompositionMode.SrcOut,
PixelAlphaCompositionMode.Dest,
PixelAlphaCompositionMode.DestAtop,
PixelAlphaCompositionMode.DestOver,
PixelAlphaCompositionMode.DestIn,
PixelAlphaCompositionMode.DestOut,
PixelAlphaCompositionMode.Clear,
PixelAlphaCompositionMode.Xor
};
[Theory]
[WithFile(TestImages.Png.PDDest, nameof(CompositingOperators), PixelTypes.Rgba32)]
public void PorterDuffOutputIsCorrect(TestImageProvider<Rgba32> provider, PixelAlphaCompositionMode mode)
{
var srcFile = TestFile.Create(TestImages.Png.PDSrc);
using (Image<Rgba32> src = srcFile.CreateRgba32Image())
using (Image<Rgba32> dest = provider.GetImage())
{
GraphicsOptions options = new GraphicsOptions
{
AlphaCompositionMode = mode
};
using (Image<Rgba32> res = dest.Clone(x => x.DrawImage(src, options)))
{
string combinedMode = mode.ToString();
if (combinedMode != "Src" && combinedMode.StartsWith("Src")) combinedMode = combinedMode.Substring(3);
res.DebugSave(provider, combinedMode);
res.CompareToReferenceOutput(provider, combinedMode);
}
}
}
}
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders
{
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Xunit;
public class PorterDuffCompositorTests
{
// TODO: Add other modes to compare.
public static readonly TheoryData<PixelAlphaCompositionMode> CompositingOperators =
new TheoryData<PixelAlphaCompositionMode>
{
PixelAlphaCompositionMode.Src,
PixelAlphaCompositionMode.SrcAtop,
PixelAlphaCompositionMode.SrcOver,
PixelAlphaCompositionMode.SrcIn,
PixelAlphaCompositionMode.SrcOut,
PixelAlphaCompositionMode.Dest,
PixelAlphaCompositionMode.DestAtop,
PixelAlphaCompositionMode.DestOver,
PixelAlphaCompositionMode.DestIn,
PixelAlphaCompositionMode.DestOut,
PixelAlphaCompositionMode.Clear,
PixelAlphaCompositionMode.Xor
};
[Theory]
[WithFile(TestImages.Png.PDDest, nameof(CompositingOperators), PixelTypes.Rgba32)]
public void PorterDuffOutputIsCorrect(TestImageProvider<Rgba32> provider, PixelAlphaCompositionMode mode)
{
var srcFile = TestFile.Create(TestImages.Png.PDSrc);
using (Image<Rgba32> src = srcFile.CreateRgba32Image())
using (Image<Rgba32> dest = provider.GetImage())
{
GraphicsOptions options = new GraphicsOptions
{
AlphaCompositionMode = mode
};
using (Image<Rgba32> res = dest.Clone(x => x.DrawImage(src, options)))
{
string combinedMode = mode.ToString();
if (combinedMode != "Src" && combinedMode.StartsWith("Src")) combinedMode = combinedMode.Substring(3);
res.DebugSave(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
{
public class PixelBlenderTests
{
{
public static TheoryData<object, Type, PixelColorBlendingMode> BlenderMappings = new TheoryData<object, Type, PixelColorBlendingMode>()
{
{ 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);
Assert.IsType(type, blender);
}
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.Screen, new Rgba32(0xFFEEE7FF) },
{ 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.Darken, new Rgba32(0xFF701919) },
{ 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.Subtract, new Rgba32(0xFF71CBE6) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Multiply, new Rgba32(0xFF631619) },
};
}
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.Screen, new Rgba32(0xFFEEE7FF) },
{ 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.Darken, new Rgba32(0xFF701919) },
{ 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.Subtract, new Rgba32(0xFF71CBE6) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelColorBlendingMode.Multiply, new Rgba32(0xFF631619) },
};
[Theory]
[MemberData(nameof(ColorBlendingExpectedResults))]
public void TestColorBlendingModes(Rgba32 backdrop, Rgba32 source, float opacity, PixelColorBlendingMode mode, Rgba32 expectedResult)
{
PixelBlender<Rgba32> blender = PixelOperations<Rgba32>.Instance.GetPixelBlender(mode, PixelAlphaCompositionMode.SrcOver);
Rgba32 actualResult = blender.Blend(backdrop, source, opacity);
// var str = actualResult.Rgba.ToString("X8"); // used to extract expectedResults
Assert.Equal(actualResult.ToVector4(), expectedResult.ToVector4());
}
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.Xor, new Rgba32(0) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Dest, 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.DestOut, new Rgba32(0) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestOver, Rgba32.MistyRose },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Src, 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.SrcOut, new Rgba32(0) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcOver, Rgba32.MidnightBlue },
};
[MemberData(nameof(ColorBlendingExpectedResults))]
public void TestColorBlendingModes(Rgba32 backdrop, Rgba32 source, float opacity, PixelColorBlendingMode mode, Rgba32 expectedResult)
{
PixelBlender<Rgba32> blender = PixelOperations<Rgba32>.Instance.GetPixelBlender(mode, PixelAlphaCompositionMode.SrcOver);
Rgba32 actualResult = blender.Blend(backdrop, source, opacity);
// var str = actualResult.Rgba.ToString("X8"); // used to extract expectedResults
Assert.Equal(actualResult.ToVector4(), expectedResult.ToVector4());
}
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.Xor, new Rgba32(0) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Dest, 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.DestOut, new Rgba32(0) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.DestOver, Rgba32.MistyRose },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.Src, 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.SrcOut, new Rgba32(0) },
{ Rgba32.MistyRose, Rgba32.MidnightBlue, 1, PixelAlphaCompositionMode.SrcOver, Rgba32.MidnightBlue },
};
[Theory]
[MemberData(nameof(AlphaCompositionExpectedResults))]
public void TestAlphaCompositionModes(Rgba32 backdrop, Rgba32 source, float opacity, PixelAlphaCompositionMode mode, Rgba32 expectedResult)
{
PixelBlender<Rgba32> blender = PixelOperations<Rgba32>.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, mode);
Rgba32 actualResult = blender.Blend(backdrop, source, opacity);
// var str = actualResult.Rgba.ToString("X8"); // used to extract expectedResults
Assert.Equal(actualResult.ToVector4(), expectedResult.ToVector4());
[MemberData(nameof(AlphaCompositionExpectedResults))]
public void TestAlphaCompositionModes(Rgba32 backdrop, Rgba32 source, float opacity, PixelAlphaCompositionMode mode, Rgba32 expectedResult)
{
PixelBlender<Rgba32> blender = PixelOperations<Rgba32>.Instance.GetPixelBlender(PixelColorBlendingMode.Normal, mode);
Rgba32 actualResult = blender.Blend(backdrop, source, opacity);
// var str = actualResult.Rgba.ToString("X8"); // used to extract expectedResults
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
http://www.impulseadventure.com/photo/
-------------------------------------
Filename: [.\Floorplan.jpg]
Filesize: [161577] Bytes
Start Offset: 0x00000000
*** Marker: SOI (xFFD8) ***
OFFSET: 0x00000000
*** Marker: APP0 (xFFE0) ***
OFFSET: 0x00000002
Length = 16
Identifier = [JFIF]
version = [1.1]
density = 300 x 300 DPI (dots per inch)
thumbnail = 0 x 0
*** Marker: APP1 (xFFE1) ***
OFFSET: 0x00000014
Length = 13464
Identifier = [Exif]
Identifier TIFF = 0x[4D4D002A 00000008]
Endian = Motorola (big)
TAG Mark x002A = 0x002A
EXIF IFD0 @ Absolute 0x00000026
Dir Length = 0x000A
[Model ] = "Photosmart Plus B209a-m"
[Orientation ] = 1 = Row 0: top, Col 0: left
[XResolution ] = 300/1
[YResolution ] = 300/1
[ResolutionUnit ] = Inch
[Software ] = "Windows Photo Editor 10.0.10011.16384"
[DateTime ] = "2016:01:02 20:17:37"
[ExifOffset ] = @ 0x091A
Offset to Next IFD = 0x000011B6
EXIF IFD1 @ Absolute 0x000011D4
Dir Length = 0x0006
[Compression ] = JPEG
[XResolution ] = 96/1
[YResolution ] = 96/1
[ResolutionUnit ] = Inch
[JpegIFOffset ] = @ +0x1214 = @ 0x1232
[JpegIFByteCount ] = 0x[0000227C] / 8828
Offset to Next IFD = 0x00000000
EXIF SubIFD @ Absolute 0x00000938
Dir Length = 0x0008
[DateTimeOriginal ] = "2016:01:02 19:22:28"
[DateTimeDigitized ] = "2016:01:02 19:22:28"
[SubSecTimeOriginal ] = "00"
[SubSecTimeDigitized ] = "00"
[ColorSpace ] = sRGB
[ExifImageWidth ] = 0x[00000922] / 2338
[ExifImageHeight ] = 0x[000008C9] / 2249
*** Marker: APP1 (xFFE1) ***
OFFSET: 0x000034AE
Length = 12772
Identifier = [http://ns.adobe.com/xap/1.0/]
XMP =
|<?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>
*** Marker: DQT (xFFDB) ***
Define a Quantization Table.
OFFSET: 0x00006694
Table length = 67
----
Precision=8 bits
Destination ID=0 (Luminance)
DQT, Row #0: 3 2 2 3 5 8 10 12
DQT, Row #1: 2 2 3 4 5 12 12 11
DQT, Row #2: 3 3 3 5 8 11 14 11
DQT, Row #3: 3 3 4 6 10 17 16 12
DQT, Row #4: 4 4 7 11 14 22 21 15
DQT, Row #5: 5 7 11 13 16 21 23 18
DQT, Row #6: 10 13 16 17 21 24 24 20
DQT, Row #7: 14 18 19 20 22 20 21 20
Approx quality factor = 90.06 (scaling=19.88 variance=1.14)
*** Marker: SOF0 (Baseline DCT) (xFFC0) ***
OFFSET: 0x000066D9
Frame header length = 11
Precision = 8
Number of Lines = 645
Samples per Line = 976
Image Size = 976 x 645
Raw Image Orientation = Landscape
Number of Img components = 1
Component[1]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y)
*** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x000066E6
Huffman table length = 31
----
Destination ID = 0
Class = 0 (DC / Lossless Table)
Codes of length 01 bits (000 total):
Codes of length 02 bits (001 total): 00
Codes of length 03 bits (005 total): 01 02 03 04 05
Codes of length 04 bits (001 total): 06
Codes of length 05 bits (001 total): 07
Codes of length 06 bits (001 total): 08
Codes of length 07 bits (001 total): 09
Codes of length 08 bits (001 total): 0A
Codes of length 09 bits (001 total): 0B
Codes of length 10 bits (000 total):
Codes of length 11 bits (000 total):
Codes of length 12 bits (000 total):
Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total):
Codes of length 15 bits (000 total):
Codes of length 16 bits (000 total):
Total number of codes: 012
*** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x00006707
Huffman table length = 181
----
Destination ID = 0
Class = 1 (AC Table)
Codes of length 01 bits (000 total):
Codes of length 02 bits (002 total): 01 02
Codes of length 03 bits (001 total): 03
Codes of length 04 bits (003 total): 00 04 11
Codes of length 05 bits (003 total): 05 12 21
Codes of length 06 bits (002 total): 31 41
Codes of length 07 bits (004 total): 06 13 51 61
Codes of length 08 bits (003 total): 07 22 71
Codes of length 09 bits (005 total): 14 32 81 91 A1
Codes of length 10 bits (005 total): 08 23 42 B1 C1
Codes of length 11 bits (004 total): 15 52 D1 F0
Codes of length 12 bits (004 total): 24 33 62 72
Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total):
Codes of length 15 bits (001 total): 82
Codes of length 16 bits (125 total): 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36
37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56
57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76
77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95
96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3
B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA
D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7
E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA
Total number of codes: 162
*** Marker: SOS (Start of Scan) (xFFDA) ***
OFFSET: 0x000067BE
Scan header length = 8
Number of img components = 1
Component[1]: selector=0x01, table=0(DC),0(AC)
Spectral selection = 0 .. 63
Successive approximation = 0x00
*** Decoding SCAN Data ***
OFFSET: 0x000067C8
Scan Decode Mode: No IDCT (DC only)
NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT]
Scan Data encountered marker 0xFFD9 @ 0x00027727.0
Compression stats:
Compression Ratio: 4.66:1
Bits per pixel: 1.72:1
Huffman code histogram stats:
Huffman Table: (Dest ID: 0, Class: DC)
# codes of length 01 bits: 0 ( 0%)
# codes of length 02 bits: 3571 ( 36%)
# codes of length 03 bits: 4320 ( 44%)
# codes of length 04 bits: 925 ( 9%)
# codes of length 05 bits: 456 ( 5%)
# codes of length 06 bits: 313 ( 3%)
# codes of length 07 bits: 291 ( 3%)
# codes of length 08 bits: 6 ( 0%)
# codes of length 09 bits: 0 ( 0%)
# codes of length 10 bits: 0 ( 0%)
# codes of length 11 bits: 0 ( 0%)
# codes of length 12 bits: 0 ( 0%)
# codes of length 13 bits: 0 ( 0%)
# codes of length 14 bits: 0 ( 0%)
# codes of length 15 bits: 0 ( 0%)
# codes of length 16 bits: 0 ( 0%)
Huffman Table: (Dest ID: 0, Class: AC)
# codes of length 01 bits: 0 ( 0%)
# codes of length 02 bits: 78118 ( 44%)
# codes of length 03 bits: 22349 ( 13%)
# codes of length 04 bits: 35264 ( 20%)
# codes of length 05 bits: 18811 ( 11%)
# codes of length 06 bits: 4312 ( 2%)
# codes of length 07 bits: 8245 ( 5%)
# codes of length 08 bits: 4682 ( 3%)
# codes of length 09 bits: 1584 ( 1%)
# codes of length 10 bits: 1900 ( 1%)
# codes of length 11 bits: 324 ( 0%)
# codes of length 12 bits: 116 ( 0%)
# codes of length 13 bits: 0 ( 0%)
# codes of length 14 bits: 0 ( 0%)
# codes of length 15 bits: 6 ( 0%)
# codes of length 16 bits: 639 ( 0%)
YCC clipping in DC:
Y component: [<0= 0] [>255= 0]
Cb component: [<0= 0] [>255= 0]
Cr component: [<0= 0] [>255= 0]
RGB clipping in DC:
R component: [<0= 0] [>255= 0]
G component: [<0= 0] [>255= 0]
B component: [<0= 0] [>255= 0]
Average Pixel Luminance (Y):
Y=[231] (range: 0..255)
Brightest Pixel Search:
YCC=[ 1017, 0, 0] RGB=[255,255,255] @ MCU[ 7, 0]
Finished Decoding SCAN Data
Number of RESTART markers decoded: 0
Next position in scan buffer: Offset 0x00027726.4
*** Marker: EOI (End of Image) (xFFD9) ***
OFFSET: 0x00027727
*** Searching Compression Signatures ***
Signature: 015C645021E37D3469A6B652789383DB
Signature (Rotated): 01D400C125EB43B05762A66347B271F7
File Offset: 0 bytes
Chroma subsampling: Gray
EXIF Make/Model: OK [???] [Photosmart Plus B209a-m]
EXIF Makernotes: NONE
EXIF Software: OK [Windows Photo Editor 10.0.10011.16384]
Searching Compression Signatures: (3347 built-in, 0 user(*) )
EXIF.Make / Software EXIF.Model Quality Subsamp Match?
------------------------- ----------------------------------- ---------------- --------------
SW :[IJG Library ] [090 Gray ]
The following IJG-based editors also match this signature:
SW :[GIMP ] [090 Gray ]
SW :[IrfanView ] [090 Gray ]
SW :[idImager ] [090 Gray ]
SW :[FastStone Image Viewer ] [090 Gray ]
SW :[NeatImage ] [090 Gray ]
SW :[Paint.NET ] [090 Gray ]
SW :[Photomatix ] [090 Gray ]
SW :[XnView ] [090 Gray ]
Based on the analysis of compression characteristics and EXIF metadata:
ASSESSMENT: Class 2 - Image has high probability of being processed/edited
JPEGsnoop 1.8.0 by Calvin Hass
http://www.impulseadventure.com/photo/
-------------------------------------
Filename: [.\Floorplan.jpg]
Filesize: [161577] Bytes
Start Offset: 0x00000000
*** Marker: SOI (xFFD8) ***
OFFSET: 0x00000000
*** Marker: APP0 (xFFE0) ***
OFFSET: 0x00000002
Length = 16
Identifier = [JFIF]
version = [1.1]
density = 300 x 300 DPI (dots per inch)
thumbnail = 0 x 0
*** Marker: APP1 (xFFE1) ***
OFFSET: 0x00000014
Length = 13464
Identifier = [Exif]
Identifier TIFF = 0x[4D4D002A 00000008]
Endian = Motorola (big)
TAG Mark x002A = 0x002A
EXIF IFD0 @ Absolute 0x00000026
Dir Length = 0x000A
[Model ] = "Photosmart Plus B209a-m"
[Orientation ] = 1 = Row 0: top, Col 0: left
[XResolution ] = 300/1
[YResolution ] = 300/1
[ResolutionUnit ] = Inch
[Software ] = "Windows Photo Editor 10.0.10011.16384"
[DateTime ] = "2016:01:02 20:17:37"
[ExifOffset ] = @ 0x091A
Offset to Next IFD = 0x000011B6
EXIF IFD1 @ Absolute 0x000011D4
Dir Length = 0x0006
[Compression ] = JPEG
[XResolution ] = 96/1
[YResolution ] = 96/1
[ResolutionUnit ] = Inch
[JpegIFOffset ] = @ +0x1214 = @ 0x1232
[JpegIFByteCount ] = 0x[0000227C] / 8828
Offset to Next IFD = 0x00000000
EXIF SubIFD @ Absolute 0x00000938
Dir Length = 0x0008
[DateTimeOriginal ] = "2016:01:02 19:22:28"
[DateTimeDigitized ] = "2016:01:02 19:22:28"
[SubSecTimeOriginal ] = "00"
[SubSecTimeDigitized ] = "00"
[ColorSpace ] = sRGB
[ExifImageWidth ] = 0x[00000922] / 2338
[ExifImageHeight ] = 0x[000008C9] / 2249
*** Marker: APP1 (xFFE1) ***
OFFSET: 0x000034AE
Length = 12772
Identifier = [http://ns.adobe.com/xap/1.0/]
XMP =
|<?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>
*** Marker: DQT (xFFDB) ***
Define a Quantization Table.
OFFSET: 0x00006694
Table length = 67
----
Precision=8 bits
Destination ID=0 (Luminance)
DQT, Row #0: 3 2 2 3 5 8 10 12
DQT, Row #1: 2 2 3 4 5 12 12 11
DQT, Row #2: 3 3 3 5 8 11 14 11
DQT, Row #3: 3 3 4 6 10 17 16 12
DQT, Row #4: 4 4 7 11 14 22 21 15
DQT, Row #5: 5 7 11 13 16 21 23 18
DQT, Row #6: 10 13 16 17 21 24 24 20
DQT, Row #7: 14 18 19 20 22 20 21 20
Approx quality factor = 90.06 (scaling=19.88 variance=1.14)
*** Marker: SOF0 (Baseline DCT) (xFFC0) ***
OFFSET: 0x000066D9
Frame header length = 11
Precision = 8
Number of Lines = 645
Samples per Line = 976
Image Size = 976 x 645
Raw Image Orientation = Landscape
Number of Img components = 1
Component[1]: ID=0x01, Samp Fac=0x11 (Subsamp 1 x 1), Quant Tbl Sel=0x00 (Lum: Y)
*** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x000066E6
Huffman table length = 31
----
Destination ID = 0
Class = 0 (DC / Lossless Table)
Codes of length 01 bits (000 total):
Codes of length 02 bits (001 total): 00
Codes of length 03 bits (005 total): 01 02 03 04 05
Codes of length 04 bits (001 total): 06
Codes of length 05 bits (001 total): 07
Codes of length 06 bits (001 total): 08
Codes of length 07 bits (001 total): 09
Codes of length 08 bits (001 total): 0A
Codes of length 09 bits (001 total): 0B
Codes of length 10 bits (000 total):
Codes of length 11 bits (000 total):
Codes of length 12 bits (000 total):
Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total):
Codes of length 15 bits (000 total):
Codes of length 16 bits (000 total):
Total number of codes: 012
*** Marker: DHT (Define Huffman Table) (xFFC4) ***
OFFSET: 0x00006707
Huffman table length = 181
----
Destination ID = 0
Class = 1 (AC Table)
Codes of length 01 bits (000 total):
Codes of length 02 bits (002 total): 01 02
Codes of length 03 bits (001 total): 03
Codes of length 04 bits (003 total): 00 04 11
Codes of length 05 bits (003 total): 05 12 21
Codes of length 06 bits (002 total): 31 41
Codes of length 07 bits (004 total): 06 13 51 61
Codes of length 08 bits (003 total): 07 22 71
Codes of length 09 bits (005 total): 14 32 81 91 A1
Codes of length 10 bits (005 total): 08 23 42 B1 C1
Codes of length 11 bits (004 total): 15 52 D1 F0
Codes of length 12 bits (004 total): 24 33 62 72
Codes of length 13 bits (000 total):
Codes of length 14 bits (000 total):
Codes of length 15 bits (001 total): 82
Codes of length 16 bits (125 total): 09 0A 16 17 18 19 1A 25 26 27 28 29 2A 34 35 36
37 38 39 3A 43 44 45 46 47 48 49 4A 53 54 55 56
57 58 59 5A 63 64 65 66 67 68 69 6A 73 74 75 76
77 78 79 7A 83 84 85 86 87 88 89 8A 92 93 94 95
96 97 98 99 9A A2 A3 A4 A5 A6 A7 A8 A9 AA B2 B3
B4 B5 B6 B7 B8 B9 BA C2 C3 C4 C5 C6 C7 C8 C9 CA
D2 D3 D4 D5 D6 D7 D8 D9 DA E1 E2 E3 E4 E5 E6 E7
E8 E9 EA F1 F2 F3 F4 F5 F6 F7 F8 F9 FA
Total number of codes: 162
*** Marker: SOS (Start of Scan) (xFFDA) ***
OFFSET: 0x000067BE
Scan header length = 8
Number of img components = 1
Component[1]: selector=0x01, table=0(DC),0(AC)
Spectral selection = 0 .. 63
Successive approximation = 0x00
*** Decoding SCAN Data ***
OFFSET: 0x000067C8
Scan Decode Mode: No IDCT (DC only)
NOTE: Low-resolution DC component shown. Can decode full-res with [Options->Scan Segment->Full IDCT]
Scan Data encountered marker 0xFFD9 @ 0x00027727.0
Compression stats:
Compression Ratio: 4.66:1
Bits per pixel: 1.72:1
Huffman code histogram stats:
Huffman Table: (Dest ID: 0, Class: DC)
# codes of length 01 bits: 0 ( 0%)
# codes of length 02 bits: 3571 ( 36%)
# codes of length 03 bits: 4320 ( 44%)
# codes of length 04 bits: 925 ( 9%)
# codes of length 05 bits: 456 ( 5%)
# codes of length 06 bits: 313 ( 3%)
# codes of length 07 bits: 291 ( 3%)
# codes of length 08 bits: 6 ( 0%)
# codes of length 09 bits: 0 ( 0%)
# codes of length 10 bits: 0 ( 0%)
# codes of length 11 bits: 0 ( 0%)
# codes of length 12 bits: 0 ( 0%)
# codes of length 13 bits: 0 ( 0%)
# codes of length 14 bits: 0 ( 0%)
# codes of length 15 bits: 0 ( 0%)
# codes of length 16 bits: 0 ( 0%)
Huffman Table: (Dest ID: 0, Class: AC)
# codes of length 01 bits: 0 ( 0%)
# codes of length 02 bits: 78118 ( 44%)
# codes of length 03 bits: 22349 ( 13%)
# codes of length 04 bits: 35264 ( 20%)
# codes of length 05 bits: 18811 ( 11%)
# codes of length 06 bits: 4312 ( 2%)
# codes of length 07 bits: 8245 ( 5%)
# codes of length 08 bits: 4682 ( 3%)
# codes of length 09 bits: 1584 ( 1%)
# codes of length 10 bits: 1900 ( 1%)
# codes of length 11 bits: 324 ( 0%)
# codes of length 12 bits: 116 ( 0%)
# codes of length 13 bits: 0 ( 0%)
# codes of length 14 bits: 0 ( 0%)
# codes of length 15 bits: 6 ( 0%)
# codes of length 16 bits: 639 ( 0%)
YCC clipping in DC:
Y component: [<0= 0] [>255= 0]
Cb component: [<0= 0] [>255= 0]
Cr component: [<0= 0] [>255= 0]
RGB clipping in DC:
R component: [<0= 0] [>255= 0]
G component: [<0= 0] [>255= 0]
B component: [<0= 0] [>255= 0]
Average Pixel Luminance (Y):
Y=[231] (range: 0..255)
Brightest Pixel Search:
YCC=[ 1017, 0, 0] RGB=[255,255,255] @ MCU[ 7, 0]
Finished Decoding SCAN Data
Number of RESTART markers decoded: 0
Next position in scan buffer: Offset 0x00027726.4
*** Marker: EOI (End of Image) (xFFD9) ***
OFFSET: 0x00027727
*** Searching Compression Signatures ***
Signature: 015C645021E37D3469A6B652789383DB
Signature (Rotated): 01D400C125EB43B05762A66347B271F7
File Offset: 0 bytes
Chroma subsampling: Gray
EXIF Make/Model: OK [???] [Photosmart Plus B209a-m]
EXIF Makernotes: NONE
EXIF Software: OK [Windows Photo Editor 10.0.10011.16384]
Searching Compression Signatures: (3347 built-in, 0 user(*) )
EXIF.Make / Software EXIF.Model Quality Subsamp Match?
------------------------- ----------------------------------- ---------------- --------------
SW :[IJG Library ] [090 Gray ]
The following IJG-based editors also match this signature:
SW :[GIMP ] [090 Gray ]
SW :[IrfanView ] [090 Gray ]
SW :[idImager ] [090 Gray ]
SW :[FastStone Image Viewer ] [090 Gray ]
SW :[NeatImage ] [090 Gray ]
SW :[Paint.NET ] [090 Gray ]
SW :[Photomatix ] [090 Gray ]
SW :[XnView ] [090 Gray ]
Based on the analysis of compression characteristics and EXIF metadata:
ASSESSMENT: Class 2 - Image has high probability of being processed/edited

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

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

Loading…
Cancel
Save