Browse Source

Merge pull request #1388 from Evangelink/pixel-swizzle

Pixel remapping/Swizzling
js/color-alpha-handling
James Jackson-South 6 years ago
committed by GitHub
parent
commit
14243d667a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      src/ImageSharp/Processing/Extensions/Transforms/SwizzleExtensions.cs
  2. 23
      src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs
  3. 41
      src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs
  4. 34
      src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler}.cs
  5. 58
      tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs
  6. 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));
}
}

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