Browse Source

Merge branch 'master' into ci-cache-action

js/color-alpha-handling
Anton Firszov 5 years ago
committed by GitHub
parent
commit
e338792d60
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      src/ImageSharp/Processing/Extensions/Transforms/SwizzleExtensions.cs
  2. 56
      src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs
  3. 50
      src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
  4. 23
      src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs
  5. 41
      src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs
  6. 34
      src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler}.cs
  7. 58
      tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs
  8. 44
      tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs

24
src/ImageSharp/Processing/Extensions/Transforms/SwizzleExtensions.cs

@ -0,0 +1,24 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing.Processors.Transforms;
namespace SixLabors.ImageSharp.Processing.Extensions.Transforms
{
/// <summary>
/// Defines extensions that allow the application of swizzle operations on an <see cref="Image"/>
/// </summary>
public static class SwizzleExtensions
{
/// <summary>
/// Swizzles an image.
/// </summary>
/// <param name="source">The image to swizzle.</param>
/// <param name="swizzler">The swizzler function.</param>
/// <typeparam name="TSwizzler">The swizzler function type.</typeparam>
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
public static IImageProcessingContext Swizzle<TSwizzler>(this IImageProcessingContext source, TSwizzler swizzler)
where TSwizzler : struct, ISwizzler
=> source.ApplyProcessor(new SwizzleProcessor<TSwizzler>(swizzler));
}
}

56
src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs

@ -1,6 +1,11 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Convolution
@ -77,5 +82,56 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Configuration configuration, Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : unmanaged, IPixel<TPixel>
=> new BokehBlurProcessor<TPixel>(configuration, this, source, sourceRectangle);
/// <summary>
/// A <see langword="struct"/> implementing the horizontal convolution logic for <see cref="BokehBlurProcessor{T}"/>.
/// </summary>
/// <remarks>
/// This type is located in the non-generic <see cref="BokehBlurProcessor"/> class and not in <see cref="BokehBlurProcessor{TPixel}"/>, where
/// it is actually used, because it does not use any generic parameters internally. Defining in a non-generic class means that there will only
/// ever be a single instantiation of this type for the JIT/AOT compilers to process, instead of having duplicate versions for each pixel type.
/// </remarks>
internal readonly struct ApplyHorizontalConvolutionRowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly Buffer2D<Vector4> targetValues;
private readonly Buffer2D<ComplexVector4> sourceValues;
private readonly Complex64[] kernel;
private readonly float z;
private readonly float w;
private readonly int maxY;
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
public ApplyHorizontalConvolutionRowOperation(
Rectangle bounds,
Buffer2D<Vector4> targetValues,
Buffer2D<ComplexVector4> sourceValues,
Complex64[] kernel,
float z,
float w)
{
this.bounds = bounds;
this.maxY = this.bounds.Bottom - 1;
this.maxX = this.bounds.Right - 1;
this.targetValues = targetValues;
this.sourceValues = sourceValues;
this.kernel = kernel;
this.z = z;
this.w = w;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y)
{
Span<Vector4> targetRowSpan = this.targetValues.GetRowSpan(y).Slice(this.bounds.X);
for (int x = 0; x < this.bounds.Width; x++)
{
Buffer2DUtils.Convolve4AndAccumulatePartials(this.kernel, this.sourceValues, targetRowSpan, y, x, this.bounds.Y, this.maxY, this.bounds.X, this.maxX, this.z, this.w);
}
}
}
}
}

50
src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs

@ -127,7 +127,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
in verticalOperation);
// Compute the horizontal 1D convolutions and accumulate the partial results on the target buffer
var horizontalOperation = new ApplyHorizontalConvolutionRowOperation(sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W);
var horizontalOperation = new BokehBlurProcessor.ApplyHorizontalConvolutionRowOperation(sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W);
ParallelRowIterator.IterateRows(
configuration,
sourceRectangle,
@ -175,52 +175,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
}
}
/// <summary>
/// A <see langword="struct"/> implementing the horizontal convolution logic for <see cref="BokehBlurProcessor{T}"/>.
/// </summary>
private readonly struct ApplyHorizontalConvolutionRowOperation : IRowOperation
{
private readonly Rectangle bounds;
private readonly Buffer2D<Vector4> targetValues;
private readonly Buffer2D<ComplexVector4> sourceValues;
private readonly Complex64[] kernel;
private readonly float z;
private readonly float w;
private readonly int maxY;
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
public ApplyHorizontalConvolutionRowOperation(
Rectangle bounds,
Buffer2D<Vector4> targetValues,
Buffer2D<ComplexVector4> sourceValues,
Complex64[] kernel,
float z,
float w)
{
this.bounds = bounds;
this.maxY = this.bounds.Bottom - 1;
this.maxX = this.bounds.Right - 1;
this.targetValues = targetValues;
this.sourceValues = sourceValues;
this.kernel = kernel;
this.z = z;
this.w = w;
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y)
{
Span<Vector4> targetRowSpan = this.targetValues.GetRowSpan(y).Slice(this.bounds.X);
for (int x = 0; x < this.bounds.Width; x++)
{
Buffer2DUtils.Convolve4AndAccumulatePartials(this.kernel, this.sourceValues, targetRowSpan, y, x, this.bounds.Y, this.maxY, this.bounds.X, this.maxX, this.z, this.w);
}
}
}
/// <summary>
/// A <see langword="struct"/> implementing the gamma exposure logic for <see cref="BokehBlurProcessor{T}"/>.
/// </summary>
@ -304,7 +258,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
for (int x = 0; x < this.bounds.Width; x++)
{
ref Vector4 v = ref Unsafe.Add(ref sourceRef, x);
var clamp = Numerics.Clamp(v, low, high);
Vector4 clamp = Numerics.Clamp(v, low, high);
v.X = MathF.Pow(clamp.X, this.inverseGamma);
v.Y = MathF.Pow(clamp.Y, this.inverseGamma);
v.Z = MathF.Pow(clamp.Z, this.inverseGamma);

23
src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs

@ -0,0 +1,23 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <summary>
/// Encapsulate an algorithm to swizzle pixels in an image.
/// </summary>
public interface ISwizzler
{
/// <summary>
/// Gets the size of the image after transformation.
/// </summary>
Size DestinationSize { get; }
/// <summary>
/// Applies the swizzle transformation to a given point.
/// </summary>
/// <param name="point">Point to transform.</param>
/// <returns>The transformed point.</returns>
Point Transform(Point point);
}
}

41
src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs

@ -0,0 +1,41 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
internal class SwizzleProcessor<TSwizzler, TPixel> : TransformProcessor<TPixel>
where TSwizzler : struct, ISwizzler
where TPixel : unmanaged, IPixel<TPixel>
{
private readonly TSwizzler swizzler;
private readonly Size destinationSize;
public SwizzleProcessor(Configuration configuration, TSwizzler swizzler, Image<TPixel> source, Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle)
{
this.swizzler = swizzler;
this.destinationSize = swizzler.DestinationSize;
}
protected override Size GetDestinationSize()
=> this.destinationSize;
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
{
Point p = default;
Point newPoint;
for (p.Y = 0; p.Y < source.Height; p.Y++)
{
Span<TPixel> rowSpan = source.GetPixelRowSpan(p.Y);
for (p.X = 0; p.X < source.Width; p.X++)
{
newPoint = this.swizzler.Transform(p);
destination[newPoint.X, newPoint.Y] = rowSpan[p.X];
}
}
}
}
}

34
src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler}.cs

@ -0,0 +1,34 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <summary>
/// Defines a swizzle operation on an image.
/// </summary>
/// <typeparam name="TSwizzler">The swizzle function type.</typeparam>
public sealed class SwizzleProcessor<TSwizzler> : IImageProcessor
where TSwizzler : struct, ISwizzler
{
/// <summary>
/// Initializes a new instance of the <see cref="SwizzleProcessor{TSwizzler}"/> class.
/// </summary>
/// <param name="swizzler">The swizzler operation.</param>
public SwizzleProcessor(TSwizzler swizzler)
{
this.Swizzler = swizzler;
}
/// <summary>
/// Gets the swizzler operation.
/// </summary>
public TSwizzler Swizzler { get; }
/// <inheritdoc />
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Configuration configuration, Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : unmanaged, IPixel<TPixel>
=> new SwizzleProcessor<TSwizzler, TPixel>(configuration, this.Swizzler, source, sourceRectangle);
}
}

58
tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs

@ -0,0 +1,58 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Extensions.Transforms;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{
[GroupOutput("Transforms")]
public class SwizzleTests
{
private struct InvertXAndYSwizzler : ISwizzler
{
public InvertXAndYSwizzler(Size sourceSize)
{
this.DestinationSize = new Size(sourceSize.Height, sourceSize.Width);
}
public Size DestinationSize { get; }
public Point Transform(Point point)
=> new Point(point.Y, point.X);
}
[Theory]
[WithTestPatternImages(20, 37, PixelTypes.Rgba32)]
[WithTestPatternImages(53, 37, PixelTypes.Byte4)]
[WithTestPatternImages(17, 32, PixelTypes.Rgba32)]
public void InvertXAndYSwizzle<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> expectedImage = provider.GetImage();
using Image<TPixel> image = provider.GetImage();
image.Mutate(ctx => ctx.Swizzle(new InvertXAndYSwizzler(new Size(image.Width, image.Height))));
image.DebugSave(
provider,
nameof(InvertXAndYSwizzler),
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: true);
image.Mutate(ctx => ctx.Swizzle(new InvertXAndYSwizzler(new Size(image.Width, image.Height))));
image.DebugSave(
provider,
"Unswizzle",
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: true);
ImageComparer.Exact.VerifySimilarity(expectedImage, image);
}
}
}

44
tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs

@ -0,0 +1,44 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Processing.Extensions.Transforms;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{
public class SwizzleTests : BaseImageOperationsExtensionTest
{
private struct InvertXAndYSwizzler : ISwizzler
{
public InvertXAndYSwizzler(Size sourceSize)
{
this.DestinationSize = new Size(sourceSize.Height, sourceSize.Width);
}
public Size DestinationSize { get; }
public Point Transform(Point point)
=> new Point(point.Y, point.X);
}
[Fact]
public void InvertXAndYSwizzlerSetsCorrectSizes()
{
int width = 5;
int height = 10;
this.operations.Swizzle(new InvertXAndYSwizzler(new Size(width, height)));
SwizzleProcessor<InvertXAndYSwizzler> processor = this.Verify<SwizzleProcessor<InvertXAndYSwizzler>>();
Assert.Equal(processor.Swizzler.DestinationSize.Width, height);
Assert.Equal(processor.Swizzler.DestinationSize.Height, width);
this.operations.Swizzle(new InvertXAndYSwizzler(processor.Swizzler.DestinationSize));
SwizzleProcessor<InvertXAndYSwizzler> processor2 = this.Verify<SwizzleProcessor<InvertXAndYSwizzler>>(1);
Assert.Equal(processor2.Swizzler.DestinationSize.Width, width);
Assert.Equal(processor2.Swizzler.DestinationSize.Height, height);
}
}
}
Loading…
Cancel
Save