Skip to content

Commit 718945c

Browse files
Merge pull request #1388 from Evangelink/pixel-swizzle
Pixel remapping/Swizzling
2 parents 0e0dc2a + 7c5f39d commit 718945c

File tree

6 files changed

+224
-0
lines changed

6 files changed

+224
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using SixLabors.ImageSharp.Processing.Processors.Transforms;
5+
6+
namespace SixLabors.ImageSharp.Processing.Extensions.Transforms
7+
{
8+
/// <summary>
9+
/// Defines extensions that allow the application of swizzle operations on an <see cref="Image"/>
10+
/// </summary>
11+
public static class SwizzleExtensions
12+
{
13+
/// <summary>
14+
/// Swizzles an image.
15+
/// </summary>
16+
/// <param name="source">The image to swizzle.</param>
17+
/// <param name="swizzler">The swizzler function.</param>
18+
/// <typeparam name="TSwizzler">The swizzler function type.</typeparam>
19+
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
20+
public static IImageProcessingContext Swizzle<TSwizzler>(this IImageProcessingContext source, TSwizzler swizzler)
21+
where TSwizzler : struct, ISwizzler
22+
=> source.ApplyProcessor(new SwizzleProcessor<TSwizzler>(swizzler));
23+
}
24+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
5+
{
6+
/// <summary>
7+
/// Encapsulate an algorithm to swizzle pixels in an image.
8+
/// </summary>
9+
public interface ISwizzler
10+
{
11+
/// <summary>
12+
/// Gets the size of the image after transformation.
13+
/// </summary>
14+
Size DestinationSize { get; }
15+
16+
/// <summary>
17+
/// Applies the swizzle transformation to a given point.
18+
/// </summary>
19+
/// <param name="point">Point to transform.</param>
20+
/// <returns>The transformed point.</returns>
21+
Point Transform(Point point);
22+
}
23+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using SixLabors.ImageSharp.PixelFormats;
6+
7+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
8+
{
9+
internal class SwizzleProcessor<TSwizzler, TPixel> : TransformProcessor<TPixel>
10+
where TSwizzler : struct, ISwizzler
11+
where TPixel : unmanaged, IPixel<TPixel>
12+
{
13+
private readonly TSwizzler swizzler;
14+
private readonly Size destinationSize;
15+
16+
public SwizzleProcessor(Configuration configuration, TSwizzler swizzler, Image<TPixel> source, Rectangle sourceRectangle)
17+
: base(configuration, source, sourceRectangle)
18+
{
19+
this.swizzler = swizzler;
20+
this.destinationSize = swizzler.DestinationSize;
21+
}
22+
23+
protected override Size GetDestinationSize()
24+
=> this.destinationSize;
25+
26+
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
27+
{
28+
Point p = default;
29+
Point newPoint;
30+
for (p.Y = 0; p.Y < source.Height; p.Y++)
31+
{
32+
Span<TPixel> rowSpan = source.GetPixelRowSpan(p.Y);
33+
for (p.X = 0; p.X < source.Width; p.X++)
34+
{
35+
newPoint = this.swizzler.Transform(p);
36+
destination[newPoint.X, newPoint.Y] = rowSpan[p.X];
37+
}
38+
}
39+
}
40+
}
41+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using SixLabors.ImageSharp.PixelFormats;
5+
6+
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
7+
{
8+
/// <summary>
9+
/// Defines a swizzle operation on an image.
10+
/// </summary>
11+
/// <typeparam name="TSwizzler">The swizzle function type.</typeparam>
12+
public sealed class SwizzleProcessor<TSwizzler> : IImageProcessor
13+
where TSwizzler : struct, ISwizzler
14+
{
15+
/// <summary>
16+
/// Initializes a new instance of the <see cref="SwizzleProcessor{TSwizzler}"/> class.
17+
/// </summary>
18+
/// <param name="swizzler">The swizzler operation.</param>
19+
public SwizzleProcessor(TSwizzler swizzler)
20+
{
21+
this.Swizzler = swizzler;
22+
}
23+
24+
/// <summary>
25+
/// Gets the swizzler operation.
26+
/// </summary>
27+
public TSwizzler Swizzler { get; }
28+
29+
/// <inheritdoc />
30+
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Configuration configuration, Image<TPixel> source, Rectangle sourceRectangle)
31+
where TPixel : unmanaged, IPixel<TPixel>
32+
=> new SwizzleProcessor<TSwizzler, TPixel>(configuration, this.Swizzler, source, sourceRectangle);
33+
}
34+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using SixLabors.ImageSharp.PixelFormats;
5+
using SixLabors.ImageSharp.Processing;
6+
using SixLabors.ImageSharp.Processing.Extensions.Transforms;
7+
using SixLabors.ImageSharp.Processing.Processors.Transforms;
8+
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
9+
using Xunit;
10+
11+
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
12+
{
13+
[GroupOutput("Transforms")]
14+
public class SwizzleTests
15+
{
16+
private struct InvertXAndYSwizzler : ISwizzler
17+
{
18+
public InvertXAndYSwizzler(Size sourceSize)
19+
{
20+
this.DestinationSize = new Size(sourceSize.Height, sourceSize.Width);
21+
}
22+
23+
public Size DestinationSize { get; }
24+
25+
public Point Transform(Point point)
26+
=> new Point(point.Y, point.X);
27+
}
28+
29+
[Theory]
30+
[WithTestPatternImages(20, 37, PixelTypes.Rgba32)]
31+
[WithTestPatternImages(53, 37, PixelTypes.Byte4)]
32+
[WithTestPatternImages(17, 32, PixelTypes.Rgba32)]
33+
public void InvertXAndYSwizzle<TPixel>(TestImageProvider<TPixel> provider)
34+
where TPixel : unmanaged, IPixel<TPixel>
35+
{
36+
using Image<TPixel> expectedImage = provider.GetImage();
37+
using Image<TPixel> image = provider.GetImage();
38+
39+
image.Mutate(ctx => ctx.Swizzle(new InvertXAndYSwizzler(new Size(image.Width, image.Height))));
40+
41+
image.DebugSave(
42+
provider,
43+
nameof(InvertXAndYSwizzler),
44+
appendPixelTypeToFileName: false,
45+
appendSourceFileOrDescription: true);
46+
47+
image.Mutate(ctx => ctx.Swizzle(new InvertXAndYSwizzler(new Size(image.Width, image.Height))));
48+
49+
image.DebugSave(
50+
provider,
51+
"Unswizzle",
52+
appendPixelTypeToFileName: false,
53+
appendSourceFileOrDescription: true);
54+
55+
ImageComparer.Exact.VerifySimilarity(expectedImage, image);
56+
}
57+
}
58+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using SixLabors.ImageSharp.Processing.Extensions.Transforms;
5+
using SixLabors.ImageSharp.Processing.Processors.Transforms;
6+
using Xunit;
7+
8+
namespace SixLabors.ImageSharp.Tests.Processing.Transforms
9+
{
10+
public class SwizzleTests : BaseImageOperationsExtensionTest
11+
{
12+
private struct InvertXAndYSwizzler : ISwizzler
13+
{
14+
public InvertXAndYSwizzler(Size sourceSize)
15+
{
16+
this.DestinationSize = new Size(sourceSize.Height, sourceSize.Width);
17+
}
18+
19+
public Size DestinationSize { get; }
20+
21+
public Point Transform(Point point)
22+
=> new Point(point.Y, point.X);
23+
}
24+
25+
[Fact]
26+
public void InvertXAndYSwizzlerSetsCorrectSizes()
27+
{
28+
int width = 5;
29+
int height = 10;
30+
31+
this.operations.Swizzle(new InvertXAndYSwizzler(new Size(width, height)));
32+
SwizzleProcessor<InvertXAndYSwizzler> processor = this.Verify<SwizzleProcessor<InvertXAndYSwizzler>>();
33+
34+
Assert.Equal(processor.Swizzler.DestinationSize.Width, height);
35+
Assert.Equal(processor.Swizzler.DestinationSize.Height, width);
36+
37+
this.operations.Swizzle(new InvertXAndYSwizzler(processor.Swizzler.DestinationSize));
38+
SwizzleProcessor<InvertXAndYSwizzler> processor2 = this.Verify<SwizzleProcessor<InvertXAndYSwizzler>>(1);
39+
40+
Assert.Equal(processor2.Swizzler.DestinationSize.Width, width);
41+
Assert.Equal(processor2.Swizzler.DestinationSize.Height, height);
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)