Browse Source

Merge branch 'master' into colorspace-transforms

af/merge-core
James Jackson-South 8 years ago
committed by GitHub
parent
commit
7cbfe7ccba
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      .github/ISSUE_TEMPLATE/ask-question.md
  2. 6
      .github/ISSUE_TEMPLATE/bug-report.md
  3. 13
      .github/ISSUE_TEMPLATE/feature-request.md
  4. 4
      ImageSharp.sln
  5. 2
      appveyor.yml
  6. 2
      build.ps1
  7. 210
      src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs
  8. 53
      src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
  9. 7
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs
  10. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs
  11. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs
  12. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs
  13. 4
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs
  14. 4
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs
  15. 4
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs
  16. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs
  17. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs
  18. 8
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs
  19. 38
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  20. 24
      src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
  21. 2
      src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs
  22. 8166
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.cs
  23. 44
      src/ImageSharp/PixelFormats/PixelBlenders/DefaultPixelBlenders.Generated.tt
  24. 838
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.cs
  25. 67
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.Generated.tt
  26. 31
      src/ImageSharp/PixelFormats/PixelBlenders/PorterDuffFunctions.cs
  27. 120
      src/ImageSharp/PixelFormats/PixelBlender{TPixel}.cs
  28. 2
      src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs
  29. 6
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
  30. 2
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  31. 58
      tests/ImageSharp.Tests/PixelFormats/PixelOperationsTests.Blender.cs
  32. 3
      tests/ImageSharp.Tests/TestImages.cs
  33. 2
      tests/Images/External
  34. 3
      tests/Images/Input/Jpg/issues/Issue694-Decode-Exif-OutOfRange.jpg
  35. 3
      tests/Images/Input/Jpg/issues/Issue695-Invalid-EOI.jpg
  36. 3
      tests/Images/Input/Jpg/issues/Issue696-Resize-Exif-OutOfRange.jpg

13
.github/ISSUE_TEMPLATE/ask-question.md

@ -0,0 +1,13 @@
---
name: Ask question
about: Ask a question about this project.
---
You should not create an issue but use Gitter instead: https://gitter.im/ImageSharp/General
You should not create an issue but use Gitter instead: https://gitter.im/ImageSharp/General
You should not create an issue but use Gitter instead: https://gitter.im/ImageSharp/General
You should not create an issue but use Gitter instead: https://gitter.im/ImageSharp/General

6
.github/ISSUE_TEMPLATE.md → .github/ISSUE_TEMPLATE/bug-report.md

@ -1,3 +1,9 @@
---
name: Bug report
about: Create a report to help us improve
---
### Prerequisites
- [ ] I have written a descriptive issue title

13
.github/ISSUE_TEMPLATE/feature-request.md

@ -0,0 +1,13 @@
---
name: Feature request
about: Suggest an idea for this project
---
You should first discuss the feature on Gitter: https://gitter.im/ImageSharp/General
You should first discuss the feature on Gitter: https://gitter.im/ImageSharp/General
You should first discuss the feature on Gitter: https://gitter.im/ImageSharp/General
You should first discuss the feature on Gitter: https://gitter.im/ImageSharp/General

4
ImageSharp.sln

@ -8,13 +8,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt
.editorconfig = .editorconfig
.travis.yml = .travis.yml
appveyor.yml = appveyor.yml
.github\ISSUE_TEMPLATE\ask-question.md = .github\ISSUE_TEMPLATE\ask-question.md
.github\ISSUE_TEMPLATE\bug-report.md = .github\ISSUE_TEMPLATE\bug-report.md
codecov.yml = codecov.yml
CodeCoverage.runsettings = CodeCoverage.runsettings
.github\CONTRIBUTING.md = .github\CONTRIBUTING.md
.github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md
features.md = features.md
ImageSharp.ruleset = ImageSharp.ruleset
ImageSharp.sln.DotSettings = ImageSharp.sln.DotSettings
.github\ISSUE_TEMPLATE.md = .github\ISSUE_TEMPLATE.md
NuGet.config = NuGet.config
.github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md
README.md = README.md

2
appveyor.yml

@ -1,5 +1,5 @@
version: 1.0.0.{build}
image: Previous Visual Studio 2017
image: Visual Studio 2017
# prevent the double build when a branch has an active PR
skip_branch_with_pr: true

2
build.ps1

@ -94,7 +94,7 @@ if("$env:APPVEYOR_API_URL" -ne ""){
}
Write-Host "Building version '${version}'"
dotnet restore /p:packageversion=$version
dotnet restore /p:packageversion=$version /p:DisableImplicitNuGetFallbackFolder=true
Write-Host "Building projects"
dotnet build -c Release /p:packageversion=$version

210
src/ImageSharp.Drawing/Processing/DrawImageExtensions.cs

@ -1,121 +1,137 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Drawing;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of images to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class DrawImageExtensions
{
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, float opacity)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, Point.Empty, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="colorBlending">The blending mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, PixelColorBlendingMode colorBlending, float opacity)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, Point.Empty, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity));
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Drawing;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Adds extensions that allow the drawing of images to the <see cref="Image{TPixel}"/> type.
/// </summary>
public static class DrawImageExtensions
{
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, float opacity)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, Point.Empty, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="colorBlending">The blending mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, PixelColorBlendingMode colorBlending, float opacity)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, Point.Empty, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="colorBlending">The color blending mode.</param>
/// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, Point.Empty, colorBlending, alphaComposition, opacity));
/// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, Point.Empty, colorBlending, alphaComposition, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="options">The options, including the blending type and blending amount.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, Point.Empty, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage));
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, GraphicsOptions options)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, Point.Empty, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, float opacity)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, Point location, float opacity)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, location, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlending">The color blending to apply.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlending">The color blending to apply.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, PixelColorBlendingMode colorBlending, float opacity)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, Point location, PixelColorBlendingMode colorBlending, float opacity)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, location, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlending">The color blending to apply.</param>
/// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="alphaComposition">The alpha composition mode.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, colorBlending, alphaComposition, opacity));
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, Point location, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, location, colorBlending, alphaComposition, opacity));
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// <summary>
/// Draws the given image together with the current one by blending their pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format of the source image.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="options">The options containing the blend mode and opacity.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage));
}
/// <param name="options">The options containing the blend mode and opacity.</param>
/// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, Point location, GraphicsOptions options)
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, location, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage));
}
}

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

@ -15,32 +15,34 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
/// <summary>
/// Combines two images together by blending the pixels.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class DrawImageProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
/// <typeparam name="TPixelDst">The pixel format of destination image.</typeparam>
/// <typeparam name="TPixelSrc">The pixel format os source image.</typeparam>
internal class DrawImageProcessor<TPixelDst, TPixelSrc> : ImageProcessor<TPixelDst>
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
{
/// <summary>
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixel}"/> class.
/// Initializes a new instance of the <see cref="DrawImageProcessor{TPixelDst, TPixelSrc}"/> class.
/// </summary>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param>
/// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
public DrawImageProcessor(Image<TPixel> image, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity)
public DrawImageProcessor(Image<TPixelSrc> image, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.Image = image;
this.Opacity = opacity;
this.Blender = PixelOperations<TPixel>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode);
this.Blender = PixelOperations<TPixelDst>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode);
this.Location = location;
}
/// <summary>
/// Gets the image to blend
/// </summary>
public Image<TPixel> Image { get; }
public Image<TPixelSrc> Image { get; }
/// <summary>
/// Gets the opacity of the image to blend
@ -50,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
/// <summary>
/// Gets the pixel blender
/// </summary>
public PixelBlender<TPixel> Blender { get; }
public PixelBlender<TPixelDst> Blender { get; }
/// <summary>
/// Gets the location to draw the blended image
@ -58,10 +60,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
public Point Location { get; }
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
protected override void OnFrameApply(ImageFrame<TPixelDst> source, Rectangle sourceRectangle, Configuration configuration)
{
Image<TPixel> targetImage = this.Image;
PixelBlender<TPixel> blender = this.Blender;
Image<TPixelSrc> targetImage = this.Image;
PixelBlender<TPixelDst> blender = this.Blender;
int locationY = this.Location.Y;
// Align start/end positions.
@ -76,23 +78,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
int width = maxX - minX;
MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator;
using (IMemoryOwner<float> amount = memoryAllocator.Allocate<float>(width))
{
amount.GetSpan().Fill(this.Opacity);
ParallelFor.WithConfiguration(
minY,
maxY,
configuration,
y =>
{
Span<TPixel> background = source.GetPixelRowSpan(y).Slice(minX, width);
Span<TPixel> foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
blender.Blend(memoryAllocator, background, background, foreground, amount.GetSpan());
});
}
MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator;
ParallelFor.WithConfiguration(
minY,
maxY,
configuration,
y =>
{
Span<TPixelDst> background = source.GetPixelRowSpan(y).Slice(minX, width);
Span<TPixelSrc> foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
blender.Blend<TPixelSrc>(memoryAllocator, background, background, foreground, this.Opacity);
});
}
}
}

7
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.CopyTo.cs

@ -5,7 +5,6 @@ using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
@ -15,7 +14,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary>
/// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical.
/// </summary>
public void CopyTo(BufferArea<float> area, int horizontalScale, int verticalScale)
public void CopyTo(in BufferArea<float> area, int horizontalScale, int verticalScale)
{
if (horizontalScale == 1 && verticalScale == 1)
{
@ -57,7 +56,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CopyTo(BufferArea<float> area)
public void CopyTo(in BufferArea<float> area)
{
ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this);
ref byte destBase = ref Unsafe.As<float, byte>(ref area.GetReferenceToOrigin());
@ -81,7 +80,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float));
}
private void CopyTo2x2(BufferArea<float> area)
private void CopyTo2x2(in BufferArea<float> area)
{
ref float destBase = ref area.GetReferenceToOrigin();
int destStride = area.Stride;

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromCmyk.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
public override void ConvertToRgba(ComponentValues values, Span<Vector4> result)
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result)
{
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> cVals = values.Component0;

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromGrayScale.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
public override void ConvertToRgba(ComponentValues values, Span<Vector4> result)
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result)
{
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> yVals = values.Component0;

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgb.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
public override void ConvertToRgba(ComponentValues values, Span<Vector4> result)
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result)
{
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> rVals = values.Component0;

4
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrBasic.cs

@ -15,12 +15,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
public override void ConvertToRgba(ComponentValues values, Span<Vector4> result)
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result)
{
ConvertCore(values, result);
}
internal static void ConvertCore(ComponentValues values, Span<Vector4> result)
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result)
{
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> yVals = values.Component0;

4
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimd.cs

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
public override void ConvertToRgba(ComponentValues values, Span<Vector4> result)
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result)
{
int remainder = result.Length % 8;
int simdCount = result.Length - remainder;
@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// <summary>
/// SIMD convert using buffers of sizes divisable by 8.
/// </summary>
internal static void ConvertCore(ComponentValues values, Span<Vector4> result)
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result)
{
DebugGuard.IsTrue(result.Length % 8 == 0, nameof(result), "result.Length should be divisable by 8!");

4
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYCbCrSimdAvx2.cs

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
public static bool IsAvailable => Vector.IsHardwareAccelerated && SimdUtils.IsAvx2CompatibleArchitecture;
public override void ConvertToRgba(ComponentValues values, Span<Vector4> result)
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result)
{
int remainder = result.Length % 8;
int simdCount = result.Length - remainder;
@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// <summary>
/// SIMD convert using buffers of sizes divisable by 8.
/// </summary>
internal static void ConvertCore(ComponentValues values, Span<Vector4> result)
internal static void ConvertCore(in ComponentValues values, Span<Vector4> result)
{
// This implementation is actually AVX specific.
// An AVX register is capable of storing 8 float-s.

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromYccK.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
{
}
public override void ConvertToRgba(ComponentValues values, Span<Vector4> result)
public override void ConvertToRgba(in ComponentValues values, Span<Vector4> result)
{
// TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> yVals = values.Component0;

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.cs

@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// </summary>
/// <param name="values">The input as a stack-only <see cref="ComponentValues"/> struct</param>
/// <param name="result">The destination buffer of <see cref="Vector4"/> values</param>
public abstract void ConvertToRgba(ComponentValues values, Span<Vector4> result);
public abstract void ConvertToRgba(in ComponentValues values, Span<Vector4> result);
/// <summary>
/// Returns the <see cref="JpegColorConverter"/> for the YCbCr colorspace that matches the current CPU architecture.

8
src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBlockPostProcessor.cs

@ -2,9 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
@ -43,6 +41,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <summary>
/// Initializes a new instance of the <see cref="JpegBlockPostProcessor"/> struct.
/// </summary>
/// <param name="decoder">The raw jpeg data.</param>
/// <param name="component">The raw component.</param>
public JpegBlockPostProcessor(IRawJpegData decoder, IJpegComponent component)
{
int qtIndex = component.QuantizationTableIndex;
@ -61,9 +61,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// - Level shift by +128, clip to [0, 255]
/// - Copy the resultin color values into 'destArea' scaling up the block by amount defined in <see cref="subSamplingDivisors"/>
/// </summary>
/// <param name="sourceBlock">The source block.</param>
/// <param name="destArea">The destination buffer area.</param>
public void ProcessBlockColorsInto(
ref Block8x8 sourceBlock,
BufferArea<float> destArea)
in BufferArea<float> destArea)
{
ref Block8x8F b = ref this.SourceBlock;
b.LoadFrom(ref sourceBlock);

38
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -268,7 +268,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.fastACTables = new FastACTables(this.configuration.MemoryAllocator);
}
while (fileMarker.Marker != JpegConstants.Markers.EOI)
// Break only when we discover a valid EOI marker.
// https://github.com/SixLabors/ImageSharp/issues/695
while (fileMarker.Marker != JpegConstants.Markers.EOI
|| (fileMarker.Marker == JpegConstants.Markers.EOI && fileMarker.Invalid))
{
if (!fileMarker.Invalid)
{
@ -463,13 +466,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
else if (this.isExif)
{
double horizontalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.XResolution, out ExifValue horizontalTag)
? ((Rational)horizontalTag.Value).ToDouble()
: 0;
double verticalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.YResolution, out ExifValue verticalTag)
? ((Rational)verticalTag.Value).ToDouble()
: 0;
double horizontalValue = this.GetExifResolutionValue(ExifTag.XResolution);
double verticalValue = this.GetExifResolutionValue(ExifTag.YResolution);
if (horizontalValue > 0 && verticalValue > 0)
{
@ -480,6 +478,26 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
}
private double GetExifResolutionValue(ExifTag tag)
{
if (!this.MetaData.ExifProfile.TryGetValue(tag, out ExifValue exifValue))
{
return 0;
}
switch (exifValue.DataType)
{
case ExifDataType.Rational:
return ((Rational)exifValue.Value).ToDouble();
case ExifDataType.Long:
return (uint)exifValue.Value;
case ExifDataType.DoubleFloat:
return (double)exifValue.Value;
default:
return 0;
}
}
/// <summary>
/// Extends the profile with additional data.
/// </summary>
@ -899,9 +917,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="values">The values</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void BuildHuffmanTable(HuffmanTables tables, int index, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values)
{
tables[index] = new HuffmanTable(this.configuration.MemoryAllocator, codeLengths, values);
}
=> tables[index] = new HuffmanTable(this.configuration.MemoryAllocator, codeLengths, values);
/// <summary>
/// Reads a <see cref="ushort"/> from the stream advancing it by two bytes

24
src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs

@ -88,19 +88,19 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
}
uint ifdOffset = this.ReadUInt32();
this.AddValues(values, (int)ifdOffset);
this.AddValues(values, ifdOffset);
uint thumbnailOffset = this.ReadUInt32();
this.GetThumbnail((int)thumbnailOffset);
this.GetThumbnail(thumbnailOffset);
if (this.exifOffset != 0)
{
this.AddValues(values, (int)this.exifOffset);
this.AddValues(values, this.exifOffset);
}
if (this.gpsOffset != 0)
{
this.AddValues(values, (int)this.gpsOffset);
this.AddValues(values, this.gpsOffset);
}
return values;
@ -153,9 +153,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary>
/// <param name="values">The values.</param>
/// <param name="index">The index.</param>
private void AddValues(List<ExifValue> values, int index)
private void AddValues(List<ExifValue> values, uint index)
{
this.position = index;
if (index > (uint)this.exifData.Length)
{
return;
}
this.position = (int)index;
int count = this.ReadUInt16();
for (int i = 0; i < count; i++)
@ -431,7 +436,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return null;
}
private void GetThumbnail(int offset)
private void GetThumbnail(uint offset)
{
var values = new List<ExifValue>();
this.AddValues(values, offset);
@ -515,10 +520,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return new Rational(numerator, denominator, false);
}
private sbyte ConvertToSignedByte(ReadOnlySpan<byte> buffer)
{
return unchecked((sbyte)buffer[0]);
}
private sbyte ConvertToSignedByte(ReadOnlySpan<byte> buffer) => unchecked((sbyte)buffer[0]);
private int ConvertToInt32(ReadOnlySpan<byte> buffer) // SignedLong in Exif Specification
{

2
src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.Matrix.cs

@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
/// <param name="value">The matrix to write</param>
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</param>
/// <returns>The number of bytes written</returns>
public int WriteMatrix(DenseMatrix<float> value, bool isSingle)
public int WriteMatrix(in DenseMatrix<float> value, bool isSingle)
{
int count = 0;
for (int y = 0; y < value.Rows; y++)

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

File diff suppressed because it is too large

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

@ -12,9 +12,11 @@
// Licensed under the Apache License, Version 2.0.
// <auto-generated />
using System;
using System.Numerics;
using System.Buffers;
using System;
using System.Numerics;
using System.Buffers;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
@ -78,32 +80,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
/// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount)
{
return PorterDuffFunctions.<#=blender_composer#>(background, source, amount);
TPixel dest = default;
dest.PackFromScaledVector4(PorterDuffFunctions.<#=blender_composer#>(background.ToScaledVector4(), source.ToScaledVector4(), amount.Clamp(0, 1)));
return dest;
}
/// <inheritdoc />
public override void Blend(MemoryAllocator memoryManager, Span<TPixel> destination, Span<TPixel> background, Span<TPixel> source, Span<float> amount)
protected override void BlendFunction(Span<Vector4> destination, ReadOnlySpan<Vector4> background, ReadOnlySpan<Vector4> source, float amount)
{
Guard.MustBeGreaterThanOrEqualTo(background.Length, destination.Length, nameof(background.Length));
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length));
Guard.MustBeGreaterThanOrEqualTo(amount.Length, destination.Length, nameof(amount.Length));
using (IMemoryOwner<Vector4> buffer = memoryManager.Allocate<Vector4>(destination.Length * 3))
amount = amount.Clamp(0, 1);
for (int i = 0; i < destination.Length; i++)
{
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);
PixelOperations<TPixel>.Instance.ToVector4(background, backgroundSpan, destination.Length);
PixelOperations<TPixel>.Instance.ToVector4(source, sourceSpan, destination.Length);
for (int i = 0; i < destination.Length; i++)
{
destinationSpan[i] = PorterDuffFunctions.<#=blender_composer#>(backgroundSpan[i], sourceSpan[i], amount[i]);
}
destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount);
}
}
PixelOperations<TPixel>.Instance.PackFromVector4(destinationSpan, destination, destination.Length);
}
/// <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));
}
}
}

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

File diff suppressed because it is too large

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

@ -13,6 +13,11 @@
// <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;
@ -24,107 +29,96 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
<# void GeneratePixelBlenders(string blender) { #>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>Src(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
source.W *= opacity;
return source;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>SrcAtop(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
source.W *= opacity;
return Atop(backdrop, source, <#=blender#>(backdrop, source));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>SrcOver(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
source.W *= opacity;
return Over(backdrop, source, <#=blender#>(backdrop, source));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>SrcIn(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
source.W *= opacity;
return In(backdrop, source, <#=blender#>(backdrop, source));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>SrcOut(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
source.W *= opacity;
return Out(backdrop, source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>Dest(Vector4 backdrop, Vector4 source, float opacity)
{
return backdrop;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>DestAtop(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
source.W *= opacity;
return Atop(source, backdrop, <#=blender#>(source, backdrop));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>DestOver(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
source.W *= opacity;
return Over(source, backdrop, <#=blender#>(source, backdrop));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>DestIn(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
source.W *= opacity;
return In(source, backdrop, <#=blender#>(source, backdrop));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>DestOut(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
source.W *= opacity;
return Out(source, backdrop);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>Xor(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
source.W *= opacity;
return Xor(backdrop, source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>Clear(Vector4 backdrop, Vector4 source, float opacity)
{
opacity = opacity.Clamp(0, 1);
source.W *= opacity;
source.W *= opacity;
return Clear(backdrop, source);
}
@ -137,8 +131,9 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
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.PackFromVector4(<#=blender#><#=composer#>(backdrop.ToVector4(),source.ToVector4(),opacity));
dest.PackFromScaledVector4(<#=blender#><#=composer#>(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return dest;
}
@ -175,7 +170,7 @@ string[] blenders = new []{
foreach(var blender in blenders)
{
GeneratePixelBlenders(blender);
GeneratePixelBlenders(blender);
foreach(var composer in composers)
{

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

@ -8,7 +8,7 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{
/// <summary>
/// Collection of Porter Duff alpha blending functions applying an the 'Over' composition model.
/// 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,
@ -148,31 +148,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop));
}
/// <summary>
/// General composition function for all modes, with a general solution for alpha channel
/// </summary>
/// <param name="backdrop">Original Backdrop color</param>
/// <param name="source">Original source color</param>
/// <param name="xform">Desired transformed color, without taking Alpha channel in account</param>
/// <returns>The final color</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 SrcOverReference(Vector4 backdrop, Vector4 source, Vector4 xform)
{
// calculate weights
float xw = backdrop.W * source.W;
float bw = backdrop.W - xw;
float sw = source.W - xw;
// calculate final alpha
float a = xw + bw + sw;
// calculate final value
xform = ((xform * xw) + (backdrop * bw) + (source * sw)) / MathF.Max(a, Constants.Epsilon);
xform.W = a;
return xform;
}
public static Vector4 Over(Vector4 dst, Vector4 src, Vector4 blend)
{
// calculate weights
@ -193,6 +169,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Atop(Vector4 dst, Vector4 src, Vector4 blend)
{
// calculate weights
@ -212,6 +189,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 In(Vector4 dst, Vector4 src, Vector4 blend)
{
float alpha = dst.W * src.W;
@ -223,6 +201,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Out(Vector4 dst, Vector4 src)
{
float alpha = (1 - dst.W) * src.W;
@ -234,6 +213,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Xor(Vector4 dst, Vector4 src)
{
float srcW = 1 - dst.W;
@ -249,6 +229,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return color;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 Clear(Vector4 backdrop, Vector4 source)
{
return Vector4.Zero;

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

@ -2,6 +2,9 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.PixelFormats
@ -23,19 +26,114 @@ namespace SixLabors.ImageSharp.PixelFormats
/// 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 pixels together.
/// </summary>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/></param>
/// <param name="destination">The destination span.</param>
/// <param name="background">The background span.</param>
/// <param name="source">The source span.</param>
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>
public abstract void Blend(MemoryAllocator memoryAllocator, Span<TPixel> destination, Span<TPixel> background, Span<TPixel> source, Span<float> amount);
/// </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="memoryManager">memory manager 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(MemoryAllocator memoryManager, Span<TPixel> destination, ReadOnlySpan<TPixel> background, ReadOnlySpan<TPixel> source, ReadOnlySpan<float> amount)
{
this.Blend<TPixel>(memoryManager, destination, background, source, amount);
}
/// <summary>
/// Blends 2 rows together
/// </summary>
/// <typeparam name="TPixelSrc">the pixel format of the source span</typeparam>
/// <param name="memoryManager">memory manager 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>(MemoryAllocator memoryManager, 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 = memoryManager.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);
PixelOperations<TPixel>.Instance.ToScaledVector4(background, backgroundSpan, destination.Length);
PixelOperations<TPixelSrc>.Instance.ToScaledVector4(source, sourceSpan, destination.Length);
this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount);
PixelOperations<TPixel>.Instance.PackFromScaledVector4(destinationSpan, destination, destination.Length);
}
}
/// <summary>
/// Blends 2 rows together
/// </summary>
/// <typeparam name="TPixelSrc">the pixel format of the source span</typeparam>
/// <param name="memoryManager">memory manager 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>(MemoryAllocator memoryManager, 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 = memoryManager.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);
PixelOperations<TPixel>.Instance.ToScaledVector4(background, backgroundSpan, destination.Length);
PixelOperations<TPixelSrc>.Instance.ToScaledVector4(source, sourceSpan, destination.Length);
this.BlendFunction(destinationSpan, backgroundSpan, sourceSpan, amount);
PixelOperations<TPixel>.Instance.PackFromScaledVector4(destinationSpan, destination, destination.Length);
}
}
}
}

2
src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs

@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
Buffer2D<TPixel> targetPixels,
Buffer2D<TPixel> sourcePixels,
Rectangle sourceRectangle,
DenseMatrix<float> kernel,
DenseMatrix<float> kernel, // TODO: Can't use 'in' as pass by ref to lambda expression.
Configuration configuration)
{
int kernelHeight = kernel.Rows;

6
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs

@ -21,7 +21,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
TestImages.Jpeg.Baseline.Bad.BadEOF,
TestImages.Jpeg.Issues.MultiHuffmanBaseline394,
TestImages.Jpeg.Baseline.MultiScanBaselineCMYK,
TestImages.Jpeg.Baseline.Bad.BadRST
TestImages.Jpeg.Baseline.Bad.BadRST,
TestImages.Jpeg.Issues.MultiHuffmanBaseline394,
TestImages.Jpeg.Issues.ExifDecodeOutOfRange694,
TestImages.Jpeg.Issues.InvalidEOI695,
TestImages.Jpeg.Issues.ExifResizeOutOfRange696
};
public static string[] ProgressiveTestJpegs =

2
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs

@ -48,6 +48,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
TestImages.Jpeg.Issues.BadZigZagProgressive385,
TestImages.Jpeg.Issues.NoEoiProgressive517,
TestImages.Jpeg.Issues.BadRstProgressive518,
TestImages.Jpeg.Issues.InvalidEOI695,
TestImages.Jpeg.Issues.ExifResizeOutOfRange696
};
return !TestEnvironment.Is64BitProcess && largeImagesToSkipOn32Bit.Contains(provider.SourceFileOrDescription);

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

@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.AddSrcOver), PixelColorBlendingMode.Add },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.SubtractSrcOver), PixelColorBlendingMode.Subtract },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.MultiplySrcOver), PixelColorBlendingMode.Multiply },
};
};
[Theory]
[MemberData(nameof(BlenderMappings))]
@ -43,6 +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) },
};
[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 },
};
[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());
}
}
}

3
tests/ImageSharp.Tests/TestImages.cs

@ -152,6 +152,9 @@ namespace SixLabors.ImageSharp.Tests
public const string BadRstProgressive518 = "Jpg/issues/Issue518-Bad-RST-Progressive.jpg";
public const string InvalidCast520 = "Jpg/issues/Issue520-InvalidCast.jpg";
public const string DhtHasWrongLength624 = "Jpg/issues/Issue624-DhtHasWrongLength-Progressive-N.jpg";
public const string ExifDecodeOutOfRange694 = "Jpg/issues/Issue694-Decode-Exif-OutOfRange.jpg";
public const string InvalidEOI695 = "Jpg/issues/Issue695-Invalid-EOI.jpg";
public const string ExifResizeOutOfRange696 = "Jpg/issues/Issue696-Resize-Exif-OutOfRange.jpg";
}
public static readonly string[] All = Baseline.All.Concat(Progressive.All).ToArray();

2
tests/Images/External

@ -1 +1 @@
Subproject commit fcf311bf15bea061e552e4cc357cafe2d4f4bd70
Subproject commit 6abc3bc0ac253a24c9e88e68d7b7d853350a85da

3
tests/Images/Input/Jpg/issues/Issue694-Decode-Exif-OutOfRange.jpg

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1b94a283fbe8927ab59745dd67d0b33e90a253e674e5fe4f0ad7594ff868cca2
size 226421

3
tests/Images/Input/Jpg/issues/Issue695-Invalid-EOI.jpg

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e748a684c318b7424dd77b008fe92e179201d0a55106021b453a3fd2a22e9ab6
size 4805575

3
tests/Images/Input/Jpg/issues/Issue696-Resize-Exif-OutOfRange.jpg

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bb22005e1db0f6da8f49ca57979f8b9aa5db7111b717a53e817e24c04283ab43
size 3196058
Loading…
Cancel
Save