Skip to content

Commit b69365e

Browse files
Add new Clip extension and tests
1 parent dfba96f commit b69365e

5 files changed

Lines changed: 162 additions & 1 deletion

File tree

src/ImageSharp.Drawing/Processing/Extensions/ClearExtensions.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Apache License, Version 2.0.
33

4-
using SixLabors.ImageSharp.Drawing.Processing.Processors.Drawing;
54
using SixLabors.ImageSharp.Processing;
65

76
namespace SixLabors.ImageSharp.Drawing.Processing
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using SixLabors.ImageSharp.Drawing.Processing.Processors.Drawing;
6+
using SixLabors.ImageSharp.Processing;
7+
8+
namespace SixLabors.ImageSharp.Drawing.Processing
9+
{
10+
/// <summary>
11+
/// Adds extensions that allow the application of processors within a clipped path.
12+
/// </summary>
13+
public static class ClipPathExtensions
14+
{
15+
/// <summary>
16+
/// Applies the processing operation against a given clipping path.
17+
/// </summary>
18+
/// <param name="source">The image processing context.</param>
19+
/// <param name="path">The target path to operate within.</param>
20+
/// <param name="operation">The operation to perform on the source.</param>
21+
/// <returns>The <see cref="IImageProcessingContext"/> to allow chaining of operations.</returns>
22+
public static IImageProcessingContext Clip(
23+
this IImageProcessingContext source,
24+
IPath path,
25+
Action<IImageProcessingContext> operation)
26+
=> source.ApplyProcessor(new RecursiveImageProcessor(source.GetDrawingOptions(), path, operation));
27+
}
28+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using SixLabors.ImageSharp.PixelFormats;
6+
using SixLabors.ImageSharp.Processing;
7+
using SixLabors.ImageSharp.Processing.Processors;
8+
9+
namespace SixLabors.ImageSharp.Drawing.Processing.Processors.Drawing
10+
{
11+
/// <summary>
12+
/// Allows the recursive application of processing operations against and image.
13+
/// </summary>
14+
public class RecursiveImageProcessor : IImageProcessor
15+
{
16+
/// <summary>
17+
/// Initializes a new instance of the <see cref="RecursiveImageProcessor"/> class.
18+
/// </summary>
19+
/// <param name="options">The drawing options.</param>
20+
/// <param name="path">The logic path.</param>
21+
/// <param name="operation">The operation to perform on the source.</param>
22+
public RecursiveImageProcessor(DrawingOptions options, IPath path, Action<IImageProcessingContext> operation)
23+
{
24+
this.Options = options;
25+
this.Path = path;
26+
this.Operation = operation;
27+
}
28+
29+
/// <summary>
30+
/// Gets the drawing options.
31+
/// </summary>
32+
public DrawingOptions Options { get; }
33+
34+
/// <summary>
35+
/// Gets the logic path.
36+
/// </summary>
37+
public IPath Path { get; }
38+
39+
/// <summary>
40+
/// Gets the operation to perform on the source.
41+
/// </summary>
42+
public Action<IImageProcessingContext> Operation { get; }
43+
44+
/// <inheritdoc/>
45+
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(
46+
Configuration configuration,
47+
Image<TPixel> source,
48+
Rectangle sourceRectangle)
49+
where TPixel : unmanaged, IPixel<TPixel>
50+
=> new RecursiveImageProcessorInner<TPixel>(this, source, configuration, sourceRectangle);
51+
52+
/// <summary>
53+
/// The main workhorse class. This has access to the pixel buffer but
54+
/// in an abstract/generic way.
55+
/// </summary>
56+
/// <typeparam name="TPixel">The type of pixel.</typeparam>
57+
private class RecursiveImageProcessorInner<TPixel> : IImageProcessor<TPixel>
58+
where TPixel : unmanaged, IPixel<TPixel>
59+
{
60+
private readonly RecursiveImageProcessor recursiveImageProcessor;
61+
private readonly Image<TPixel> source;
62+
private readonly Configuration configuration;
63+
private readonly Rectangle sourceRectangle;
64+
65+
public RecursiveImageProcessorInner(RecursiveImageProcessor recursiveImageProcessor, Image<TPixel> source, Configuration configuration, Rectangle sourceRectangle)
66+
{
67+
this.recursiveImageProcessor = recursiveImageProcessor;
68+
this.source = source;
69+
this.configuration = configuration;
70+
this.sourceRectangle = sourceRectangle;
71+
}
72+
73+
public void Dispose()
74+
{
75+
}
76+
77+
public void Execute()
78+
{
79+
// Perform any processing logic you want here, in this case we are going to
80+
// clone the source image then apply the changes requested before cropping
81+
// down and using an image brush to draw that portion the the original image
82+
// so clone out our source image so we can apply
83+
// various effects to it without mutating the origional yet.
84+
using Image<TPixel> clone = this.source.Clone(this.recursiveImageProcessor.Operation);
85+
86+
// Crop it down to just the size of the shape
87+
clone.Mutate(x => x.Crop((Rectangle)this.recursiveImageProcessor.Path.Bounds));
88+
89+
// Use an image brush to apply cloned image as the source for filling the shape
90+
var brush = new ImageBrush(clone);
91+
92+
// Grab hold of an image processor that can fill paths with a brush to allow it to do the hard pixel pushing for us
93+
var processor = new FillPathProcessor(this.recursiveImageProcessor.Options, brush, this.recursiveImageProcessor.Path);
94+
using IImageProcessor<TPixel> p = processor.CreatePixelSpecificProcessor(this.configuration, this.source, this.sourceRectangle);
95+
96+
// Fill the shape using the image brush
97+
p.Execute();
98+
}
99+
}
100+
}
101+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using SixLabors.ImageSharp.Drawing.Processing;
6+
using SixLabors.ImageSharp.PixelFormats;
7+
using SixLabors.ImageSharp.Processing;
8+
using Xunit;
9+
10+
namespace SixLabors.ImageSharp.Drawing.Tests.Drawing
11+
{
12+
[GroupOutput("Drawing")]
13+
public class ClipTests
14+
{
15+
[Theory]
16+
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32)]
17+
public void Clip<TPixel>(TestImageProvider<TPixel> provider)
18+
where TPixel : unmanaged, IPixel<TPixel>
19+
=> provider.RunValidatingProcessorTest(
20+
x =>
21+
{
22+
Size size = x.GetCurrentSize();
23+
int outerRadii = Math.Min(size.Width, size.Height) / 2;
24+
var star = new Star(new PointF(size.Width / 2, size.Height / 2), 5, outerRadii / 2, outerRadii);
25+
x.Clip(star, x => x.DetectEdges());
26+
},
27+
appendPixelTypeToFileName: false,
28+
appendSourceFileOrDescription: false);
29+
}
30+
}
Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)