// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.PixelFormats; using System; namespace SixLabors.ImageSharp.Drawing.Processing { /// /// Provides an implementation of a pattern brush for painting patterns. /// /// /// The patterns that are used to create a custom pattern brush are made up of a repeating matrix of flags, /// where each flag denotes whether to draw the foreground color or the background color. /// so to create a new bool[,] with your flags /// /// For example if you wanted to create a diagonal line that repeat every 4 pixels you would use a pattern like so /// 1000 /// 0100 /// 0010 /// 0001 /// /// /// or you want a horizontal stripe which is 3 pixels apart you would use a pattern like /// 1 /// 0 /// 0 /// /// public sealed class PatternBrush : Brush { /// /// Initializes a new instance of the class. /// /// Color of the fore. /// Color of the back. /// The pattern. public PatternBrush(Color foreColor, Color backColor, bool[,] pattern) : this(foreColor, backColor, new DenseMatrix(pattern)) { } /// /// Initializes a new instance of the class. /// /// Color of the fore. /// Color of the back. /// The pattern. internal PatternBrush(Color foreColor, Color backColor, in DenseMatrix pattern) { this.Pattern = new DenseMatrix(pattern.Columns, pattern.Rows); for (int i = 0; i < pattern.Data.Length; i++) { if (pattern.Data[i]) { this.Pattern.Data[i] = foreColor; } else { this.Pattern.Data[i] = backColor; } } } /// /// Initializes a new instance of the class. /// /// The brush. internal PatternBrush(PatternBrush brush) => this.Pattern = brush.Pattern; /// /// Gets the pattern color matrix. /// public DenseMatrix Pattern { get; } /// public override bool Equals(Brush? other) { if (other is PatternBrush sb) { return sb.Pattern.Equals(this.Pattern); } return false; } /// public override int GetHashCode() => this.Pattern.GetHashCode(); /// public override BrushRenderer CreateRenderer( Configuration configuration, GraphicsOptions options, int canvasWidth, RectangleF region) => new PatternBrushRenderer( configuration, options, canvasWidth, this.Pattern.ToPixelMatrix()); /// /// The pattern brush applicator. /// /// The pixel format. private sealed class PatternBrushRenderer : BrushRenderer where TPixel : unmanaged, IPixel { private readonly DenseMatrix pattern; /// /// Initializes a new instance of the class. /// /// The configuration instance to use when performing operations. /// The graphics options. /// The canvas width for the current render pass. /// The pattern. public PatternBrushRenderer( Configuration configuration, GraphicsOptions options, int canvasWidth, in DenseMatrix pattern) : base(configuration, options, canvasWidth) => this.pattern = pattern; internal TPixel this[int x, int y] { get { x %= this.pattern.Columns; y %= this.pattern.Rows; // 2d array index at row/column return this.pattern[y, x]; } } /// public override void Apply( Span destinationRow, ReadOnlySpan scanline, int x, int y, BrushWorkspace workspace) { int patternY = y % this.pattern.Rows; Span amounts = workspace.GetAmounts(scanline.Length); Span overlays = workspace.GetOverlays(scanline.Length); for (int i = 0; i < scanline.Length; i++) { amounts[i] = Math.Clamp(scanline[i] * this.Options.BlendPercentage, 0, 1F); int patternX = (x + i) % this.pattern.Columns; overlays[i] = this.pattern[patternY, patternX]; } this.Blender.Blend( this.Configuration, destinationRow, destinationRow, overlays, amounts, workspace.GetBlendScratch(scanline.Length, 3)); } } } }