// // 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 an image brush for painting images within areas. /// /// The pixel format. public class ImageBrush : IBrush where TPixel : struct, IPixel { /// /// The image to paint. /// private readonly IImageBase image; /// /// Initializes a new instance of the class. /// /// The image. public ImageBrush(IImageBase image) { this.image = image; } /// public BrushApplicator CreateApplicator(PixelAccessor sourcePixels, RectangleF region, GraphicsOptions options) { return new ImageBrushApplicator(sourcePixels, this.image, region, options); } /// /// The image brush applicator. /// private class ImageBrushApplicator : BrushApplicator { /// /// The source pixel accessor. /// private readonly PixelAccessor source; /// /// The y-length. /// private readonly int yLength; /// /// The x-length. /// private readonly int xLength; /// /// The Y offset. /// private readonly int offsetY; /// /// The X offset. /// private readonly int offsetX; /// /// Initializes a new instance of the class. /// /// /// The image. /// /// /// The region. /// /// The options /// /// The sourcePixels. /// public ImageBrushApplicator(PixelAccessor sourcePixels, IImageBase image, RectangleF region, GraphicsOptions options) : base(sourcePixels, options) { this.source = image.Lock(); this.xLength = image.Width; this.yLength = image.Height; this.offsetY = (int)MathF.Max(MathF.Floor(region.Top), 0); this.offsetX = (int)MathF.Max(MathF.Floor(region.Left), 0); } /// /// Gets the color for a single pixel. /// /// The x. /// The y. /// /// The color /// internal override TPixel this[int x, int y] { get { int srcX = (x - this.offsetX) % this.xLength; int srcY = (y - this.offsetY) % this.yLength; return this.source[srcX, srcY]; } } /// public override void Dispose() { this.source.Dispose(); } /// internal override void Apply(Span scanline, int x, int y) { // create a span for colors using (Buffer amountBuffer = new Buffer(scanline.Length)) using (Buffer overlay = new Buffer(scanline.Length)) { int sourceY = (y - this.offsetY) % this.yLength; int offsetX = x - this.offsetX; Span sourceRow = this.source.GetRowSpan(sourceY); for (int i = 0; i < scanline.Length; i++) { amountBuffer[i] = scanline[i] * this.Options.BlendPercentage; int sourceX = (i + offsetX) % this.xLength; TPixel pixel = sourceRow[sourceX]; overlay[i] = pixel; } Span destinationRow = this.Target.GetRowSpan(x, y).Slice(0, scanline.Length); this.Blender.Blend(destinationRow, destinationRow, overlay, amountBuffer); } } } } }