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