Skip to content

Commit fe045b5

Browse files
Avoid crop
1 parent 08d8550 commit fe045b5

2 files changed

Lines changed: 47 additions & 33 deletions

File tree

src/ImageSharp.Drawing/Processing/ImageBrush.cs

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,27 @@ public class ImageBrush : IBrush
1818
/// </summary>
1919
private readonly Image image;
2020

21+
private readonly RectangleF? region;
22+
2123
/// <summary>
2224
/// Initializes a new instance of the <see cref="ImageBrush"/> class.
2325
/// </summary>
2426
/// <param name="image">The image.</param>
2527
public ImageBrush(Image image)
28+
=> this.image = image;
29+
30+
/// <summary>
31+
/// Initializes a new instance of the <see cref="ImageBrush"/> class.
32+
/// </summary>
33+
/// <param name="image">The image.</param>
34+
/// <param name="region">
35+
/// The region of interest.
36+
/// This overrides any region used to intitialize the brush applicator.
37+
/// </param>
38+
internal ImageBrush(Image image, RectangleF region)
2639
{
2740
this.image = image;
41+
this.region = region;
2842
}
2943

3044
/// <inheritdoc />
@@ -35,19 +49,22 @@ public BrushApplicator<TPixel> CreateApplicator<TPixel>(
3549
RectangleF region)
3650
where TPixel : unmanaged, IPixel<TPixel>
3751
{
52+
RectangleF interest = this.region ?? region;
53+
3854
if (this.image is Image<TPixel> specificImage)
3955
{
40-
return new ImageBrushApplicator<TPixel>(configuration, options, source, specificImage, region, false);
56+
return new ImageBrushApplicator<TPixel>(configuration, options, source, specificImage, interest, false);
4157
}
4258

4359
specificImage = this.image.CloneAs<TPixel>();
4460

45-
return new ImageBrushApplicator<TPixel>(configuration, options, source, specificImage, region, true);
61+
return new ImageBrushApplicator<TPixel>(configuration, options, source, specificImage, interest, true);
4662
}
4763

4864
/// <summary>
4965
/// The image brush applicator.
5066
/// </summary>
67+
/// <typeparam name="TPixel">The pixel format.</typeparam>
5168
private class ImageBrushApplicator<TPixel> : BrushApplicator<TPixel>
5269
where TPixel : unmanaged, IPixel<TPixel>
5370
{
@@ -139,32 +156,30 @@ public override void Apply(Span<float> scanline, int x, int y)
139156
{
140157
// Create a span for colors
141158
MemoryAllocator allocator = this.Configuration.MemoryAllocator;
142-
using (IMemoryOwner<float> amountBuffer = allocator.Allocate<float>(scanline.Length))
143-
using (IMemoryOwner<TPixel> overlay = allocator.Allocate<TPixel>(scanline.Length))
159+
using IMemoryOwner<float> amountBuffer = allocator.Allocate<float>(scanline.Length);
160+
using IMemoryOwner<TPixel> overlay = allocator.Allocate<TPixel>(scanline.Length);
161+
Span<float> amountSpan = amountBuffer.Memory.Span;
162+
Span<TPixel> overlaySpan = overlay.Memory.Span;
163+
164+
int sourceY = (y - this.offsetY) % this.yLength;
165+
int offsetX = x - this.offsetX;
166+
Span<TPixel> sourceRow = this.sourceFrame.GetPixelRowSpan(sourceY);
167+
168+
for (int i = 0; i < scanline.Length; i++)
144169
{
145-
Span<float> amountSpan = amountBuffer.Memory.Span;
146-
Span<TPixel> overlaySpan = overlay.Memory.Span;
147-
148-
int sourceY = (y - this.offsetY) % this.yLength;
149-
int offsetX = x - this.offsetX;
150-
Span<TPixel> sourceRow = this.sourceFrame.GetPixelRowSpan(sourceY);
151-
152-
for (int i = 0; i < scanline.Length; i++)
153-
{
154-
amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
155-
156-
int sourceX = (i + offsetX) % this.xLength;
157-
overlaySpan[i] = sourceRow[sourceX];
158-
}
159-
160-
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
161-
this.Blender.Blend(
162-
this.Configuration,
163-
destinationRow,
164-
destinationRow,
165-
overlaySpan,
166-
amountSpan);
170+
amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
171+
172+
int sourceX = (i + offsetX) % this.xLength;
173+
overlaySpan[i] = sourceRow[sourceX];
167174
}
175+
176+
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
177+
this.Blender.Blend(
178+
this.Configuration,
179+
destinationRow,
180+
destinationRow,
181+
overlaySpan,
182+
amountSpan);
168183
}
169184
}
170185
}

src/ImageSharp.Drawing/Processing/Processors/Drawing/RecursiveImageProcessor.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,16 @@ public void Dispose()
7777
public void Execute()
7878
{
7979
// 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
80+
// clone the source image then apply the changes requested before using an
81+
// image brush to draw that portion the the original image
8282
// so clone out our source image so we can apply
8383
// various effects to it without mutating the origional yet.
8484
using Image<TPixel> clone = this.source.Clone(this.recursiveImageProcessor.Operation);
8585

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);
86+
// Use an image brush to apply cloned image as the source for filling the shape.
87+
// We pass explicit bounds to avoid the need to crop the clone;
88+
RectangleF bounds = this.recursiveImageProcessor.Path.Bounds;
89+
var brush = new ImageBrush(clone, new RectangleF(0, 0, bounds.Width, bounds.Height));
9190

9291
// Grab hold of an image processor that can fill paths with a brush to allow it to do the hard pixel pushing for us
9392
var processor = new FillPathProcessor(this.recursiveImageProcessor.Options, brush, this.recursiveImageProcessor.Path);

0 commit comments

Comments
 (0)