// // 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.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) { return new PatternBrushApplicator(sourcePixels, this.pattern, this.patternVector); } /// /// 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. public PatternBrushApplicator(PixelAccessor sourcePixels, Fast2DArray pattern, Fast2DArray patternVector) : base(sourcePixels) { 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(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y) { Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); using (Buffer buffer = new Buffer(scanlineBuffer)) { BufferSpan slice = buffer.Slice(offset); for (int xPos = 0; xPos < scanlineWidth; xPos++) { int targetX = xPos + x; int targetY = y; float opacity = slice[xPos]; if (opacity > Constants.Epsilon) { Vector4 backgroundVector = this.Target[targetX, targetY].ToVector4(); // 2d array index at row/column Vector4 sourceVector = this.patternVector[targetY % this.patternVector.Height, targetX % this.patternVector.Width]; Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); TPixel packed = default(TPixel); packed.PackFromVector4(finalColor); this.Target[targetX, targetY] = packed; } } } } } } }