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 ### Prerequisites
- [ ] I have written a descriptive issue title - [ ] 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 .editorconfig = .editorconfig
.travis.yml = .travis.yml .travis.yml = .travis.yml
appveyor.yml = appveyor.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 codecov.yml = codecov.yml
CodeCoverage.runsettings = CodeCoverage.runsettings CodeCoverage.runsettings = CodeCoverage.runsettings
.github\CONTRIBUTING.md = .github\CONTRIBUTING.md .github\CONTRIBUTING.md = .github\CONTRIBUTING.md
.github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md
features.md = features.md features.md = features.md
ImageSharp.ruleset = ImageSharp.ruleset ImageSharp.ruleset = ImageSharp.ruleset
ImageSharp.sln.DotSettings = ImageSharp.sln.DotSettings ImageSharp.sln.DotSettings = ImageSharp.sln.DotSettings
.github\ISSUE_TEMPLATE.md = .github\ISSUE_TEMPLATE.md
NuGet.config = NuGet.config NuGet.config = NuGet.config
.github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md .github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md
README.md = README.md README.md = README.md

2
appveyor.yml

@ -1,5 +1,5 @@
version: 1.0.0.{build} 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 # prevent the double build when a branch has an active PR
skip_branch_with_pr: true skip_branch_with_pr: true

2
build.ps1

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

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

@ -1,121 +1,137 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.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));
/// <summary> using SixLabors.ImageSharp.PixelFormats;
/// Draws the given image together with the current one by blending their pixels. using SixLabors.ImageSharp.Processing.Processors.Drawing;
/// </summary> using SixLabors.Primitives;
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param> namespace SixLabors.ImageSharp.Processing
/// <param name="image">The image to blend with the currently processing image.</param> {
/// <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="colorBlending">The color blending mode.</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> /// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity)
where TPixel : struct, IPixel<TPixel> where TPixelDst : struct, IPixel<TPixelDst>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, Point.Empty, colorBlending, alphaComposition, opacity)); where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, Point.Empty, colorBlending, alphaComposition, opacity));
/// <summary> /// <summary>
/// Draws the given image together with the current one by blending their pixels. /// Draws the given image together with the current one by blending their pixels.
/// </summary> /// </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="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> /// <param name="options">The options, including the blending type and blending amount.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, GraphicsOptions options) public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel> where TPixelDst : struct, IPixel<TPixelDst>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, Point.Empty, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage)); where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, Point.Empty, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage));
/// <summary> /// <summary>
/// Draws the given image together with the current one by blending their pixels. /// Draws the given image together with the current one by blending their pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <param name="source">The image this method extends.</param> /// <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="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> /// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, float opacity) public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, Point location, float opacity)
where TPixel : struct, IPixel<TPixel> where TPixelDst : struct, IPixel<TPixelDst>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity)); where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, location, GraphicsOptions.Default.ColorBlendingMode, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary> /// <summary>
/// Draws the given image together with the current one by blending their pixels. /// Draws the given image together with the current one by blending their pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <param name="source">The image this method extends.</param> /// <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="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="colorBlending">The color blending to apply.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</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> /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, PixelColorBlendingMode colorBlending, float opacity) public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, Point location, PixelColorBlendingMode colorBlending, float opacity)
where TPixel : struct, IPixel<TPixel> where TPixelDst : struct, IPixel<TPixelDst>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity)); where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, location, colorBlending, GraphicsOptions.Default.AlphaCompositionMode, opacity));
/// <summary> /// <summary>
/// Draws the given image together with the current one by blending their pixels. /// Draws the given image together with the current one by blending their pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixelDst">The pixel format of the destination image.</typeparam>
/// <param name="source">The image this method extends.</param> /// <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="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="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> /// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity) public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, Point location, PixelColorBlendingMode colorBlending, PixelAlphaCompositionMode alphaComposition, float opacity)
where TPixel : struct, IPixel<TPixel> where TPixelDst : struct, IPixel<TPixelDst>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, colorBlending, alphaComposition, opacity)); where TPixelSrc : struct, IPixel<TPixelSrc>
=> source.ApplyProcessor(new DrawImageProcessor<TPixelDst, TPixelSrc>(image, location, colorBlending, alphaComposition, opacity));
/// <summary> /// <summary>
/// Draws the given image together with the current one by blending their pixels. /// Draws the given image together with the current one by blending their pixels.
/// </summary> /// </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="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="location">The location to draw the blended 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> /// <param name="options">The options containing the blend mode and opacity.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns> /// <returns>The <see cref="Image{TPixelDst}"/>.</returns>
public static IImageProcessingContext<TPixel> DrawImage<TPixel>(this IImageProcessingContext<TPixel> source, Image<TPixel> image, Point location, GraphicsOptions options) public static IImageProcessingContext<TPixelDst> DrawImage<TPixelDst, TPixelSrc>(this IImageProcessingContext<TPixelDst> source, Image<TPixelSrc> image, Point location, GraphicsOptions options)
where TPixel : struct, IPixel<TPixel> where TPixelDst : struct, IPixel<TPixelDst>
=> source.ApplyProcessor(new DrawImageProcessor<TPixel>(image, location, options.ColorBlendingMode, options.AlphaCompositionMode, options.BlendPercentage)); 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> /// <summary>
/// Combines two images together by blending the pixels. /// Combines two images together by blending the pixels.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixelDst">The pixel format of destination image.</typeparam>
internal class DrawImageProcessor<TPixel> : ImageProcessor<TPixel> /// <typeparam name="TPixelSrc">The pixel format os source image.</typeparam>
where TPixel : struct, IPixel<TPixel> internal class DrawImageProcessor<TPixelDst, TPixelSrc> : ImageProcessor<TPixelDst>
where TPixelDst : struct, IPixel<TPixelDst>
where TPixelSrc : struct, IPixel<TPixelSrc>
{ {
/// <summary> /// <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> /// </summary>
/// <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="location">The location to draw the blended image.</param> /// <param name="location">The location to draw the blended image.</param>
/// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param> /// <param name="colorBlendingMode">The blending mode to use when drawing the image.</param>
/// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param> /// <param name="alphaCompositionMode">The Alpha blending mode to use when drawing the image.</param>
/// <param name="opacity">The opacity of the image to blend. Must be between 0 and 1.</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)); Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
this.Image = image; this.Image = image;
this.Opacity = opacity; this.Opacity = opacity;
this.Blender = PixelOperations<TPixel>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode); this.Blender = PixelOperations<TPixelDst>.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode);
this.Location = location; this.Location = location;
} }
/// <summary> /// <summary>
/// Gets the image to blend /// Gets the image to blend
/// </summary> /// </summary>
public Image<TPixel> Image { get; } public Image<TPixelSrc> Image { get; }
/// <summary> /// <summary>
/// Gets the opacity of the image to blend /// Gets the opacity of the image to blend
@ -50,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
/// <summary> /// <summary>
/// Gets the pixel blender /// Gets the pixel blender
/// </summary> /// </summary>
public PixelBlender<TPixel> Blender { get; } public PixelBlender<TPixelDst> Blender { get; }
/// <summary> /// <summary>
/// Gets the location to draw the blended image /// Gets the location to draw the blended image
@ -58,10 +60,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
public Point Location { get; } public Point Location { get; }
/// <inheritdoc/> /// <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; Image<TPixelSrc> targetImage = this.Image;
PixelBlender<TPixel> blender = this.Blender; PixelBlender<TPixelDst> blender = this.Blender;
int locationY = this.Location.Y; int locationY = this.Location.Y;
// Align start/end positions. // Align start/end positions.
@ -76,23 +78,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
int width = maxX - minX; int width = maxX - minX;
MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator; MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator;
using (IMemoryOwner<float> amount = memoryAllocator.Allocate<float>(width)) ParallelFor.WithConfiguration(
{ minY,
amount.GetSpan().Fill(this.Opacity); maxY,
configuration,
ParallelFor.WithConfiguration( y =>
minY, {
maxY, Span<TPixelDst> background = source.GetPixelRowSpan(y).Slice(minX, width);
configuration, Span<TPixelSrc> foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width);
y => blender.Blend<TPixelSrc>(memoryAllocator, background, background, foreground, this.Opacity);
{ });
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());
});
}
} }
} }
} }

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

@ -5,7 +5,6 @@ using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Components namespace SixLabors.ImageSharp.Formats.Jpeg.Components
@ -15,7 +14,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// <summary> /// <summary>
/// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical. /// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical.
/// </summary> /// </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) if (horizontalScale == 1 && verticalScale == 1)
{ {
@ -57,7 +56,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
} }
// [MethodImpl(MethodImplOptions.AggressiveInlining)] // [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 selfBase = ref Unsafe.As<Block8x8F, byte>(ref this);
ref byte destBase = ref Unsafe.As<float, byte>(ref area.GetReferenceToOrigin()); 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)); 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(); ref float destBase = ref area.GetReferenceToOrigin();
int destStride = area.Stride; 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()! // TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> cVals = values.Component0; 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()! // TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> yVals = values.Component0; 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()! // TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> rVals = values.Component0; 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); 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()! // TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> yVals = values.Component0; 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 remainder = result.Length % 8;
int simdCount = result.Length - remainder; int simdCount = result.Length - remainder;
@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// <summary> /// <summary>
/// SIMD convert using buffers of sizes divisable by 8. /// SIMD convert using buffers of sizes divisable by 8.
/// </summary> /// </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!"); 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 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 remainder = result.Length % 8;
int simdCount = result.Length - remainder; int simdCount = result.Length - remainder;
@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
/// <summary> /// <summary>
/// SIMD convert using buffers of sizes divisable by 8. /// SIMD convert using buffers of sizes divisable by 8.
/// </summary> /// </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. // This implementation is actually AVX specific.
// An AVX register is capable of storing 8 float-s. // 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()! // TODO: We can optimize a lot here with Vector<float> and SRCS.Unsafe()!
ReadOnlySpan<float> yVals = values.Component0; 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> /// </summary>
/// <param name="values">The input as a stack-only <see cref="ComponentValues"/> struct</param> /// <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> /// <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> /// <summary>
/// Returns the <see cref="JpegColorConverter"/> for the YCbCr colorspace that matches the current CPU architecture. /// 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. // Licensed under the Apache License, Version 2.0.
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
@ -43,6 +41,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JpegBlockPostProcessor"/> struct. /// Initializes a new instance of the <see cref="JpegBlockPostProcessor"/> struct.
/// </summary> /// </summary>
/// <param name="decoder">The raw jpeg data.</param>
/// <param name="component">The raw component.</param>
public JpegBlockPostProcessor(IRawJpegData decoder, IJpegComponent component) public JpegBlockPostProcessor(IRawJpegData decoder, IJpegComponent component)
{ {
int qtIndex = component.QuantizationTableIndex; int qtIndex = component.QuantizationTableIndex;
@ -61,9 +61,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// - Level shift by +128, clip to [0, 255] /// - 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"/> /// - Copy the resultin color values into 'destArea' scaling up the block by amount defined in <see cref="subSamplingDivisors"/>
/// </summary> /// </summary>
/// <param name="sourceBlock">The source block.</param>
/// <param name="destArea">The destination buffer area.</param>
public void ProcessBlockColorsInto( public void ProcessBlockColorsInto(
ref Block8x8 sourceBlock, ref Block8x8 sourceBlock,
BufferArea<float> destArea) in BufferArea<float> destArea)
{ {
ref Block8x8F b = ref this.SourceBlock; ref Block8x8F b = ref this.SourceBlock;
b.LoadFrom(ref 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); 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) if (!fileMarker.Invalid)
{ {
@ -463,13 +466,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
} }
else if (this.isExif) else if (this.isExif)
{ {
double horizontalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.XResolution, out ExifValue horizontalTag) double horizontalValue = this.GetExifResolutionValue(ExifTag.XResolution);
? ((Rational)horizontalTag.Value).ToDouble() double verticalValue = this.GetExifResolutionValue(ExifTag.YResolution);
: 0;
double verticalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.YResolution, out ExifValue verticalTag)
? ((Rational)verticalTag.Value).ToDouble()
: 0;
if (horizontalValue > 0 && verticalValue > 0) 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> /// <summary>
/// Extends the profile with additional data. /// Extends the profile with additional data.
/// </summary> /// </summary>
@ -899,9 +917,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <param name="values">The values</param> /// <param name="values">The values</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void BuildHuffmanTable(HuffmanTables tables, int index, ReadOnlySpan<byte> codeLengths, ReadOnlySpan<byte> values) 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> /// <summary>
/// Reads a <see cref="ushort"/> from the stream advancing it by two bytes /// 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(); uint ifdOffset = this.ReadUInt32();
this.AddValues(values, (int)ifdOffset); this.AddValues(values, ifdOffset);
uint thumbnailOffset = this.ReadUInt32(); uint thumbnailOffset = this.ReadUInt32();
this.GetThumbnail((int)thumbnailOffset); this.GetThumbnail(thumbnailOffset);
if (this.exifOffset != 0) if (this.exifOffset != 0)
{ {
this.AddValues(values, (int)this.exifOffset); this.AddValues(values, this.exifOffset);
} }
if (this.gpsOffset != 0) if (this.gpsOffset != 0)
{ {
this.AddValues(values, (int)this.gpsOffset); this.AddValues(values, this.gpsOffset);
} }
return values; return values;
@ -153,9 +153,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary> /// </summary>
/// <param name="values">The values.</param> /// <param name="values">The values.</param>
/// <param name="index">The index.</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(); int count = this.ReadUInt16();
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
@ -431,7 +436,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return null; return null;
} }
private void GetThumbnail(int offset) private void GetThumbnail(uint offset)
{ {
var values = new List<ExifValue>(); var values = new List<ExifValue>();
this.AddValues(values, offset); this.AddValues(values, offset);
@ -515,10 +520,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return new Rational(numerator, denominator, false); return new Rational(numerator, denominator, false);
} }
private sbyte ConvertToSignedByte(ReadOnlySpan<byte> buffer) private sbyte ConvertToSignedByte(ReadOnlySpan<byte> buffer) => unchecked((sbyte)buffer[0]);
{
return unchecked((sbyte)buffer[0]);
}
private int ConvertToInt32(ReadOnlySpan<byte> buffer) // SignedLong in Exif Specification 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="value">The matrix to write</param>
/// <param name="isSingle">True if the values are encoded as Single; false if encoded as Fix16</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> /// <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; int count = 0;
for (int y = 0; y < value.Rows; y++) 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. // Licensed under the Apache License, Version 2.0.
// <auto-generated /> // <auto-generated />
using System; using System;
using System.Numerics; using System.Numerics;
using System.Buffers; using System.Buffers;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory; using SixLabors.Memory;
namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
@ -78,32 +80,28 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
/// <inheritdoc /> /// <inheritdoc />
public override TPixel Blend(TPixel background, TPixel source, float amount) 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 /> /// <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)); amount = amount.Clamp(0, 1);
Guard.MustBeGreaterThanOrEqualTo(source.Length, destination.Length, nameof(source.Length)); for (int i = 0; i < destination.Length; i++)
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); destination[i] = PorterDuffFunctions.<#=blender_composer#>(background[i], source[i], amount);
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]);
}
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 /> // <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;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -24,107 +29,96 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
<# void GeneratePixelBlenders(string blender) { #> <# void GeneratePixelBlenders(string blender) { #>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>Src(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>Src(Vector4 backdrop, Vector4 source, float opacity)
{ {
opacity = opacity.Clamp(0, 1); source.W *= opacity;
source.W *= opacity;
return source; return source;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>SrcAtop(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>SrcAtop(Vector4 backdrop, Vector4 source, float opacity)
{ {
opacity = opacity.Clamp(0, 1); source.W *= opacity;
source.W *= opacity;
return Atop(backdrop, source, <#=blender#>(backdrop, source)); return Atop(backdrop, source, <#=blender#>(backdrop, source));
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>SrcOver(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>SrcOver(Vector4 backdrop, Vector4 source, float opacity)
{ {
opacity = opacity.Clamp(0, 1); source.W *= opacity;
source.W *= opacity;
return Over(backdrop, source, <#=blender#>(backdrop, source)); return Over(backdrop, source, <#=blender#>(backdrop, source));
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>SrcIn(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>SrcIn(Vector4 backdrop, Vector4 source, float opacity)
{ {
opacity = opacity.Clamp(0, 1); source.W *= opacity;
source.W *= opacity;
return In(backdrop, source, <#=blender#>(backdrop, source)); return In(backdrop, source, <#=blender#>(backdrop, source));
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>SrcOut(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>SrcOut(Vector4 backdrop, Vector4 source, float opacity)
{ {
opacity = opacity.Clamp(0, 1); source.W *= opacity;
source.W *= opacity;
return Out(backdrop, source); return Out(backdrop, source);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>Dest(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>Dest(Vector4 backdrop, Vector4 source, float opacity)
{ {
return backdrop; return backdrop;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>DestAtop(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>DestAtop(Vector4 backdrop, Vector4 source, float opacity)
{ {
opacity = opacity.Clamp(0, 1); source.W *= opacity;
source.W *= opacity;
return Atop(source, backdrop, <#=blender#>(source, backdrop)); return Atop(source, backdrop, <#=blender#>(source, backdrop));
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>DestOver(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>DestOver(Vector4 backdrop, Vector4 source, float opacity)
{ {
opacity = opacity.Clamp(0, 1); source.W *= opacity;
source.W *= opacity;
return Over(source, backdrop, <#=blender#>(source, backdrop)); return Over(source, backdrop, <#=blender#>(source, backdrop));
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>DestIn(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>DestIn(Vector4 backdrop, Vector4 source, float opacity)
{ {
opacity = opacity.Clamp(0, 1); source.W *= opacity;
source.W *= opacity;
return In(source, backdrop, <#=blender#>(source, backdrop)); return In(source, backdrop, <#=blender#>(source, backdrop));
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>DestOut(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>DestOut(Vector4 backdrop, Vector4 source, float opacity)
{ {
opacity = opacity.Clamp(0, 1); source.W *= opacity;
source.W *= opacity;
return Out(source, backdrop); return Out(source, backdrop);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>Xor(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>Xor(Vector4 backdrop, Vector4 source, float opacity)
{ {
opacity = opacity.Clamp(0, 1); source.W *= opacity;
source.W *= opacity;
return Xor(backdrop, source); return Xor(backdrop, source);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
public static Vector4 <#=blender#>Clear(Vector4 backdrop, Vector4 source, float opacity) public static Vector4 <#=blender#>Clear(Vector4 backdrop, Vector4 source, float opacity)
{ {
opacity = opacity.Clamp(0, 1); source.W *= opacity;
source.W *= opacity;
return Clear(backdrop, source); 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) public static TPixel <#=blender#><#=composer#><TPixel>(TPixel backdrop, TPixel source, float opacity)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
opacity = opacity.Clamp(0, 1);
TPixel dest = default; TPixel dest = default;
dest.PackFromVector4(<#=blender#><#=composer#>(backdrop.ToVector4(),source.ToVector4(),opacity)); dest.PackFromScaledVector4(<#=blender#><#=composer#>(backdrop.ToScaledVector4(), source.ToScaledVector4(), opacity));
return dest; return dest;
} }
@ -175,7 +170,7 @@ string[] blenders = new []{
foreach(var blender in blenders) foreach(var blender in blenders)
{ {
GeneratePixelBlenders(blender); GeneratePixelBlenders(blender);
foreach(var composer in composers) 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 namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
{ {
/// <summary> /// <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> /// </summary>
/// <remarks> /// <remarks>
/// These functions are designed to be a general solution for all color cases, /// These functions are designed to be a general solution for all color cases,
@ -148,31 +148,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop)); return backdrop <= 0.5f ? (2 * backdrop * source) : 1 - ((2 * (1 - source)) * (1 - backdrop));
} }
/// <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)] [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) public static Vector4 Over(Vector4 dst, Vector4 src, Vector4 blend)
{ {
// calculate weights // calculate weights
@ -193,6 +169,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return color; return color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Atop(Vector4 dst, Vector4 src, Vector4 blend) public static Vector4 Atop(Vector4 dst, Vector4 src, Vector4 blend)
{ {
// calculate weights // calculate weights
@ -212,6 +189,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return color; return color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 In(Vector4 dst, Vector4 src, Vector4 blend) public static Vector4 In(Vector4 dst, Vector4 src, Vector4 blend)
{ {
float alpha = dst.W * src.W; float alpha = dst.W * src.W;
@ -223,6 +201,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return color; return color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Out(Vector4 dst, Vector4 src) public static Vector4 Out(Vector4 dst, Vector4 src)
{ {
float alpha = (1 - dst.W) * src.W; float alpha = (1 - dst.W) * src.W;
@ -234,6 +213,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return color; return color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Xor(Vector4 dst, Vector4 src) public static Vector4 Xor(Vector4 dst, Vector4 src)
{ {
float srcW = 1 - dst.W; float srcW = 1 - dst.W;
@ -249,6 +229,7 @@ namespace SixLabors.ImageSharp.PixelFormats.PixelBlenders
return color; return color;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 Clear(Vector4 backdrop, Vector4 source) private static Vector4 Clear(Vector4 backdrop, Vector4 source)
{ {
return Vector4.Zero; return Vector4.Zero;

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

@ -2,6 +2,9 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers;
using System.Numerics;
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory; using SixLabors.Memory;
namespace SixLabors.ImageSharp.PixelFormats namespace SixLabors.ImageSharp.PixelFormats
@ -23,19 +26,114 @@ namespace SixLabors.ImageSharp.PixelFormats
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param> /// </param>
/// <returns>The final pixel value after composition</returns> /// <returns>The final pixel value after composition</returns>
public abstract TPixel Blend(TPixel background, TPixel source, float amount); public abstract TPixel Blend(TPixel background, TPixel source, float amount);
/// <summary> /// <summary>
/// Blend 2 pixels together. /// Blend 2 rows together.
/// </summary> /// </summary>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/></param> /// <param name="destination">destination span</param>
/// <param name="destination">The destination span.</param> /// <param name="background">the background span</param>
/// <param name="background">The background span.</param> /// <param name="source">the source span</param>
/// <param name="source">The source span.</param>
/// <param name="amount"> /// <param name="amount">
/// A value between 0 and 1 indicating the weight of the second source vector. /// A value between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned. /// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param> /// </param>
public abstract void Blend(MemoryAllocator memoryAllocator, Span<TPixel> destination, Span<TPixel> background, Span<TPixel> source, Span<float> amount); 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> targetPixels,
Buffer2D<TPixel> sourcePixels, Buffer2D<TPixel> sourcePixels,
Rectangle sourceRectangle, Rectangle sourceRectangle,
DenseMatrix<float> kernel, DenseMatrix<float> kernel, // TODO: Can't use 'in' as pass by ref to lambda expression.
Configuration configuration) Configuration configuration)
{ {
int kernelHeight = kernel.Rows; 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.Baseline.Bad.BadEOF,
TestImages.Jpeg.Issues.MultiHuffmanBaseline394, TestImages.Jpeg.Issues.MultiHuffmanBaseline394,
TestImages.Jpeg.Baseline.MultiScanBaselineCMYK, 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 = 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.BadZigZagProgressive385,
TestImages.Jpeg.Issues.NoEoiProgressive517, TestImages.Jpeg.Issues.NoEoiProgressive517,
TestImages.Jpeg.Issues.BadRstProgressive518, TestImages.Jpeg.Issues.BadRstProgressive518,
TestImages.Jpeg.Issues.InvalidEOI695,
TestImages.Jpeg.Issues.ExifResizeOutOfRange696
}; };
return !TestEnvironment.Is64BitProcess && largeImagesToSkipOn32Bit.Contains(provider.SourceFileOrDescription); 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>.AddSrcOver), PixelColorBlendingMode.Add },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.SubtractSrcOver), PixelColorBlendingMode.Subtract }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.SubtractSrcOver), PixelColorBlendingMode.Subtract },
{ new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.MultiplySrcOver), PixelColorBlendingMode.Multiply }, { new TestPixel<RgbaVector>(), typeof(DefaultPixelBlenders<RgbaVector>.MultiplySrcOver), PixelColorBlendingMode.Multiply },
}; };
[Theory] [Theory]
[MemberData(nameof(BlenderMappings))] [MemberData(nameof(BlenderMappings))]
@ -43,6 +43,62 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats
{ {
PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(mode, PixelAlphaCompositionMode.SrcOver); PixelBlender<TPixel> blender = PixelOperations<TPixel>.Instance.GetPixelBlender(mode, PixelAlphaCompositionMode.SrcOver);
Assert.IsType(type, blender); Assert.IsType(type, blender);
}
public static TheoryData<Rgba32, Rgba32, float, PixelColorBlendingMode, Rgba32> ColorBlendingExpectedResults = new TheoryData<Rgba32, Rgba32, float, PixelColorBlendingMode, Rgba32>()
{
{ 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 BadRstProgressive518 = "Jpg/issues/Issue518-Bad-RST-Progressive.jpg";
public const string InvalidCast520 = "Jpg/issues/Issue520-InvalidCast.jpg"; public const string InvalidCast520 = "Jpg/issues/Issue520-InvalidCast.jpg";
public const string DhtHasWrongLength624 = "Jpg/issues/Issue624-DhtHasWrongLength-Progressive-N.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(); 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