// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageSharp.Drawing.Brushes { using System; using System.Numerics; using ImageSharp.Memory; using ImageSharp.PixelFormats; using Processors; /// /// 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 Fast2DArray pattern; private readonly Fast2DArray 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 Fast2DArray(pattern)) { } /// /// Initializes a new instance of the class. /// /// Color of the fore. /// Color of the back. /// The pattern. internal PatternBrush(TPixel foreColor, TPixel backColor, Fast2DArray pattern) { Vector4 foreColorVector = foreColor.ToVector4(); Vector4 backColorVector = backColor.ToVector4(); this.pattern = new Fast2DArray(pattern.Width, pattern.Height); this.patternVector = new Fast2DArray(pattern.Width, pattern.Height); 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(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options) { return new PatternBrushApplicator(sourcePixels, this.pattern, this.patternVector, options); } /// /// The pattern brush applicator. /// private class PatternBrushApplicator : BrushApplicator { /// /// The pattern. /// private readonly Fast2DArray pattern; private readonly Fast2DArray patternVector; /// /// Initializes a new instance of the class. /// /// The sourcePixels. /// The pattern. /// The patternVector. /// The options public PatternBrushApplicator(PixelAccessor sourcePixels, Fast2DArray pattern, Fast2DArray patternVector, GraphicsOptions options) : base(sourcePixels, 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.Width; y = y % this.pattern.Height; // 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.Height; using (Buffer amountBuffer = new Buffer(scanline.Length)) using (Buffer overlay = new Buffer(scanline.Length)) { for (int i = 0; i < scanline.Length; i++) { amountBuffer[i] = (scanline[i] * this.Options.BlendPercentage).Clamp(0, 1); int patternX = (x + i) % this.pattern.Width; overlay[i] = this.pattern[patternY, patternX]; } Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); } } } } }