Skip to content

Commit 99dca57

Browse files
committed
apply transform to shape on render
1 parent c917752 commit 99dca57

5 files changed

Lines changed: 42 additions & 4 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Configuratio
4545
where TPixel : unmanaged, IPixel<TPixel>
4646
{
4747
// offset drawlines to align drawing outlines to pixel centers
48-
var shape = this.Shape.Translate(0.5f, 0.5f);
48+
var shape = this.Shape.Translate(0.5f, 0.5f).Transform(this.Options.ShapeOptions.Transform);
4949
return new FillRegionProcessor(this.Options, this.Pen.StrokeFill, new ShapePath(shape, this.Pen)).CreatePixelSpecificProcessor(configuration, source, sourceRectangle);
5050
}
5151
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ public FillPathProcessor(ShapeGraphicsOptions options, IBrush brush, IPath shape
4545
public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Configuration configuration, Image<TPixel> source, Rectangle sourceRectangle)
4646
where TPixel : unmanaged, IPixel<TPixel>
4747
{
48-
if (this.Shape is RectangularPolygon rectPoly)
48+
var shape = this.Shape.Transform(this.Options.ShapeOptions.Transform);
49+
50+
if (shape is RectangularPolygon rectPoly)
4951
{
5052
var rectF = new RectangleF(rectPoly.Location, rectPoly.Size);
5153
var rect = (Rectangle)rectF;
@@ -58,7 +60,7 @@ public IImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Configuratio
5860
}
5961
}
6062

61-
return new FillRegionProcessor(this.Options, this.Brush, new ShapeRegion(this.Shape)).CreatePixelSpecificProcessor(configuration, source, sourceRectangle);
63+
return new FillRegionProcessor(this.Options, this.Brush, new ShapeRegion(shape)).CreatePixelSpecificProcessor(configuration, source, sourceRectangle);
6264
}
6365
}
6466
}

src/ImageSharp.Drawing/Processing/ShapeOptions.cs

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

4+
using System.Numerics;
45
using SixLabors.ImageSharp.Drawing.Shapes;
56

67
namespace SixLabors.ImageSharp.Drawing.Processing
@@ -28,6 +29,11 @@ private ShapeOptions(ShapeOptions source)
2829
/// </summary>
2930
public IntersectionRule IntersectionRule { get; set; } = IntersectionRule.OddEven;
3031

32+
/// <summary>
33+
/// Gets or sets the transform to apply to the shape during rendering.
34+
/// </summary>
35+
public Matrix3x2 Transform { get; set; } = Matrix3x2.Identity;
36+
3137
/// <inheritdoc/>
3238
public ShapeOptions DeepClone() => new ShapeOptions(this);
3339
}

tests/ImageSharp.Drawing.Tests/Drawing/DrawPolygonTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,20 @@ public void DrawPolygon<TPixel>(TestImageProvider<TPixel> provider, string color
3636
outputDetails,
3737
appendSourceFileOrDescription: false);
3838
}
39+
40+
[Theory]
41+
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32)]
42+
public void DrawPolygon_Transformed<TPixel>(TestImageProvider<TPixel> provider)
43+
where TPixel : unmanaged, IPixel<TPixel>
44+
{
45+
PointF[] simplePath =
46+
{
47+
new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300)
48+
};
49+
50+
provider.RunValidatingProcessorTest(
51+
c => c.SetShapeOptions(x => x.Transform = Matrix3x2.CreateSkew(GeometryUtilities.DegreeToRadian(-15), 0, new Vector2(200, 200)))
52+
.DrawPolygon(Color.White, 2.5f, simplePath));
53+
}
3954
}
4055
}

tests/ImageSharp.Drawing.Tests/Drawing/FillPolygonTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,21 @@ public void FillPolygon_Solid<TPixel>(TestImageProvider<TPixel> provider, string
6767
appendSourceFileOrDescription: false);
6868
}
6969

70+
[Theory]
71+
[WithBasicTestPatternImages(250, 350, PixelTypes.Rgba32)]
72+
public void FillPolygon_Solid_Transformed<TPixel>(TestImageProvider<TPixel> provider)
73+
where TPixel : unmanaged, IPixel<TPixel>
74+
{
75+
PointF[] simplePath =
76+
{
77+
new Vector2(10, 10), new Vector2(200, 150), new Vector2(50, 300)
78+
};
79+
80+
provider.RunValidatingProcessorTest(
81+
c => c.SetShapeOptions(x => x.Transform = Matrix3x2.CreateSkew(GeometryUtilities.DegreeToRadian(-15), 0, new Vector2(200, 200)))
82+
.FillPolygon(Color.White, simplePath));
83+
}
84+
7085
public static TheoryData<bool, IntersectionRule> FillPolygon_Complex_Data { get; } =
7186
new TheoryData<bool, IntersectionRule>()
7287
{

0 commit comments

Comments
 (0)