// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Buffers; using System.Numerics; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.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 class PatternBrush : IBrush { /// /// The pattern. /// private readonly DenseMatrix pattern; private readonly DenseMatrix patternVector; /// /// 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) { var foreColorVector = (Vector4)foreColor; var backColorVector = (Vector4)backColor; this.pattern = new DenseMatrix(pattern.Columns, pattern.Rows); this.patternVector = new DenseMatrix(pattern.Columns, pattern.Rows); for (int i = 0; i < pattern.Data.Length; i++) { if (pattern.Data[i]) { this.pattern.Data[i] = foreColor; this.patternVector.Data[i] = foreColorVector; } else { this.pattern.Data[i] = backColor; this.patternVector.Data[i] = backColorVector; } } } /// /// Initializes a new instance of the class. /// /// The brush. internal PatternBrush(PatternBrush brush) { this.pattern = brush.pattern; this.patternVector = brush.patternVector; } /// public BrushApplicator CreateApplicator( Configuration configuration, ImageFrame source, RectangleF region, GraphicsOptions options) where TPixel : struct, IPixel => new PatternBrushApplicator( configuration, source, this.pattern.ToPixelMatrix(configuration), options); /// /// The pattern brush applicator. /// private class PatternBrushApplicator : BrushApplicator where TPixel : struct, IPixel { /// /// The pattern. /// private readonly DenseMatrix pattern; /// /// Initializes a new instance of the class. /// /// The configuration instance to use when performing operations. /// The source image. /// The pattern. /// The graphics options. public PatternBrushApplicator( Configuration configuration, ImageFrame source, in DenseMatrix pattern, GraphicsOptions options) : base(configuration, source, options) { this.pattern = pattern; } /// internal override 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]; } } /// internal override void Apply(Span scanline, int x, int y) { int patternY = y % this.pattern.Rows; MemoryAllocator memoryAllocator = this.Target.MemoryAllocator; using (IMemoryOwner amountBuffer = memoryAllocator.Allocate(scanline.Length)) using (IMemoryOwner overlay = memoryAllocator.Allocate(scanline.Length)) { Span amountSpan = amountBuffer.Memory.Span; Span overlaySpan = overlay.Memory.Span; for (int i = 0; i < scanline.Length; i++) { amountSpan[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1); int patternX = (x + i) % this.pattern.Columns; overlaySpan[i] = this.pattern[patternY, patternX]; } Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend( this.Configuration, destinationRow, destinationRow, overlaySpan, amountSpan); } } } } }