// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
namespace SixLabors.ImageSharp.Drawing.Processing.Backends {
///
/// One explicit stroked two-point line-segment command queued by the canvas batcher.
///
public readonly struct StrokeLineSegmentCommand
{
private readonly PointF sourceStart;
private readonly PointF sourceEnd;
private readonly DrawingOptions drawingOptions;
///
/// Initializes a new instance of the struct.
///
/// The source line start point.
/// The source line end point.
/// The brush used to shade the stroke.
/// The drawing options (graphics, shape, transform) used during composition.
/// The rasterizer options used to generate coverage.
/// The absolute bounds of the logical target.
/// The absolute destination offset of the command.
/// The stroke metadata.
/// True if the command was recorded inside a layer.
public StrokeLineSegmentCommand(
PointF sourceStart,
PointF sourceEnd,
Brush brush,
DrawingOptions drawingOptions,
in RasterizerOptions rasterizerOptions,
Rectangle targetBounds,
Point destinationOffset,
Pen pen,
bool isInsideLayer)
{
this.sourceStart = sourceStart;
this.sourceEnd = sourceEnd;
this.drawingOptions = drawingOptions;
this.Brush = brush;
this.RasterizerOptions = rasterizerOptions;
this.TargetBounds = targetBounds;
this.DestinationOffset = destinationOffset;
this.Pen = pen;
this.IsInsideLayer = isInsideLayer;
}
///
/// Gets the brush used during composition.
///
public Brush Brush { get; }
///
/// Gets the drawing options carried by the command.
///
public DrawingOptions DrawingOptions => this.drawingOptions;
///
/// Gets the graphics options used during composition.
///
public GraphicsOptions GraphicsOptions => this.drawingOptions.GraphicsOptions;
///
/// Gets the rasterizer options used to generate coverage.
///
public RasterizerOptions RasterizerOptions { get; }
///
/// Gets the absolute bounds of the logical target for this command.
///
public Rectangle TargetBounds { get; }
///
/// Gets the absolute destination offset where the local coverage should be composited.
///
public Point DestinationOffset { get; }
///
/// Gets the stroke metadata for this command.
///
public Pen Pen { get; }
///
/// Gets the source line start point.
///
public PointF SourceStart => this.sourceStart;
///
/// Gets the source line end point.
///
public PointF SourceEnd => this.sourceEnd;
///
/// Gets the command transform.
///
public Matrix4x4 Transform => this.drawingOptions.Transform;
///
/// Gets a value indicating whether the command was recorded inside a layer.
///
public bool IsInsideLayer { get; }
///
/// Computes the conservative stroked bounds of one two-point line segment.
///
/// The line start point.
/// The line end point.
/// The stroke metadata.
/// The conservative stroked bounds.
public static RectangleF GetConservativeBounds(PointF start, PointF end, Pen pen)
{
float left = MathF.Min(start.X, end.X);
float top = MathF.Min(start.Y, end.Y);
float right = MathF.Max(start.X, end.X);
float bottom = MathF.Max(start.Y, end.Y);
RectangleF bounds = RectangleF.FromLTRB(left, top, right, bottom);
return InflateBounds(bounds, pen);
}
private static RectangleF InflateBounds(RectangleF bounds, Pen pen)
{
float halfWidth = pen.StrokeWidth * 0.5F;
float inflate = pen.StrokeOptions.LineJoin switch
{
LineJoin.Miter or LineJoin.MiterRevert or LineJoin.MiterRound => (float)(halfWidth * Math.Max(pen.StrokeOptions.MiterLimit, 1D)),
_ => halfWidth
};
bounds.Inflate(new SizeF(inflate, inflate));
return bounds;
}
}
}