ImageSharp/ImageSharp.Drawing/Processing/Pen.cs
2026-08-03 22:31:27 +02:00

121 lines
4.9 KiB
C#

// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
namespace SixLabors.ImageSharp.Drawing.Processing {
/// <summary>
/// The base class for pens that can apply a pattern to a line with a set brush and thickness
/// </summary>
/// <remarks>
/// The pattern will be in to the form of
/// <code>
/// new float[]{ 1f, 2f, 0.5f}
/// </code>
/// this will be converted into a pattern that is 3.5 times longer that the width with 3 sections.
/// <list type="bullet">
/// <item>Section 1 will be width long (making a square) and will be filled by the brush.</item>
/// <item>Section 2 will be width * 2 long and will be empty.</item>
/// <item>Section 3 will be width/2 long and will be filled.</item>
/// </list>
/// The pattern will immediately repeat without gap.
/// </remarks>
public abstract class Pen : IEquatable<Pen>
{
private readonly float[] pattern;
/// <summary>
/// Initializes a new instance of the <see cref="Pen"/> class.
/// </summary>
/// <param name="strokeFill">The brush used to fill the stroke outline.</param>
protected Pen(Brush strokeFill)
: this(strokeFill, 1)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Pen"/> class.
/// </summary>
/// <param name="strokeFill">The brush used to fill the stroke outline.</param>
/// <param name="strokeWidth">The stroke width in the path's local coordinate space before any drawing transform is applied.</param>
protected Pen(Brush strokeFill, float strokeWidth)
: this(strokeFill, strokeWidth, Pens.EmptyPattern)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Pen"/> class.
/// </summary>
/// <param name="strokeFill">The brush used to fill the stroke outline.</param>
/// <param name="strokeWidth">The stroke width in the path's local coordinate space before any drawing transform is applied.</param>
/// <param name="strokePattern">The stroke pattern.</param>
protected Pen(Brush strokeFill, float strokeWidth, float[] strokePattern)
{
Guard.NotNull(strokeFill, nameof(strokeFill));
Guard.MustBeGreaterThan(strokeWidth, 0, nameof(strokeWidth));
Guard.NotNull(strokePattern, nameof(strokePattern));
this.StrokeFill = strokeFill;
this.StrokeWidth = strokeWidth;
this.pattern = strokePattern;
this.StrokeOptions = new StrokeOptions();
}
/// <summary>
/// Initializes a new instance of the <see cref="Pen"/> class.
/// </summary>
/// <param name="options">The pen options.</param>
protected Pen(PenOptions options)
{
this.StrokeFill = options.StrokeFill;
this.StrokeWidth = options.StrokeWidth;
this.pattern = options.StrokePattern;
this.StrokeOptions = options.StrokeOptions ?? new StrokeOptions();
}
/// <inheritdoc cref="PenOptions.StrokeFill"/>
public Brush StrokeFill { get; }
/// <inheritdoc cref="PenOptions.StrokeWidth"/>
public float StrokeWidth { get; }
/// <inheritdoc cref="PenOptions.StrokePattern"/>
public ReadOnlyMemory<float> StrokePattern => this.pattern;
/// <inheritdoc cref="PenOptions.StrokeOptions"/>
public StrokeOptions StrokeOptions { get; }
/// <summary>
/// Applies the styling from the pen to a path and generate a new path with the final vector.
/// </summary>
/// <param name="path">The source path</param>
/// <returns>The <see cref="IPath"/> with the pen styling applied.</returns>
public IPath GeneratePath(IPath path)
=> this.GeneratePath(path, this.StrokeWidth);
/// <summary>
/// Applies the styling from the pen to a path and generate a new path with the final vector.
/// </summary>
/// <param name="path">The source path</param>
/// <param name="strokeWidth">The stroke width in the path's local coordinate space before any drawing transform is applied.</param>
/// <returns>The <see cref="IPath"/> with the pen styling applied.</returns>
public abstract IPath GeneratePath(IPath path, float strokeWidth);
/// <inheritdoc/>
public virtual bool Equals(Pen? other)
=> other != null
&& this.StrokeWidth == other.StrokeWidth
&& this.StrokeFill.Equals(other.StrokeFill)
&& this.StrokeOptions.Equals(other.StrokeOptions)
&& this.StrokePattern.Span.SequenceEqual(other.StrokePattern.Span);
/// <inheritdoc/>
public override bool Equals(object? obj) => this.Equals(obj as Pen);
/// <inheritdoc/>
public override int GetHashCode()
=> HashCode.Combine(this.StrokeWidth, this.StrokeFill, this.StrokeOptions, this.pattern);
}
}