// 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.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 /// /// /// The pixel format. public class PatternBrush : IBrush where TPixel : struct, IPixel { /// /// 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(TPixel foreColor, TPixel 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(TPixel foreColor, TPixel backColor, DenseMatrix pattern) { var foreColorVector = foreColor.ToVector4(); var backColorVector = backColor.ToVector4(); 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(ImageFrame source, RectangleF region, GraphicsOptions options) { return new PatternBrushApplicator(source, this.pattern, this.patternVector, options); } /// /// The pattern brush applicator. /// private class PatternBrushApplicator : BrushApplicator { /// /// The pattern. /// private readonly DenseMatrix pattern; private readonly DenseMatrix patternVector; /// /// Initializes a new instance of the class. /// /// The source image. /// The pattern. /// The patternVector. /// The options public PatternBrushApplicator(ImageFrame source, DenseMatrix pattern, DenseMatrix patternVector, GraphicsOptions options) : base(source, options) { this.pattern = pattern; this.patternVector = patternVector; } /// /// Gets the color for a single pixel. /// # /// The x. /// The y. /// /// The Color. /// internal override TPixel this[int x, int y] { get { x = x % this.pattern.Columns; y = y % this.pattern.Rows; // 2d array index at row/column return this.pattern[y, x]; } } /// public override void Dispose() { // noop } /// 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.GetSpan(); Span overlaySpan = overlay.GetSpan(); 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(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan); } } } } }