// // 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 Processors; /// /// Provides an implementation of an image brush for painting images within areas. /// /// The pixel format. public class ImageBrush : IBrush where TColor : 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) { return new ImageBrushApplicator(sourcePixels, this.image, region); } /// /// 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 offset. /// private readonly Vector2 offset; /// /// Initializes a new instance of the class. /// /// /// The image. /// /// /// The region. /// /// /// The sourcePixels. /// public ImageBrushApplicator(PixelAccessor sourcePixels, IImageBase image, RectangleF region) : base(sourcePixels) { this.source = image.Lock(); this.xLength = image.Width; this.yLength = image.Height; this.offset = new Vector2((float)Math.Max(Math.Floor(region.Top), 0), (float)Math.Max(Math.Floor(region.Left), 0)); } /// /// Gets the color for a single pixel. /// /// The x. /// The y. /// /// The color /// internal override TColor this[int x, int y] { get { Vector2 point = new Vector2(x, y); // Offset the requested pixel by the value in the rectangle (the shapes position) point = point - this.offset; int srcX = (int)point.X % this.xLength; int srcY = (int)point.Y % this.yLength; return this.source[srcX, srcY]; } } /// public override void Dispose() { this.source.Dispose(); } /// internal override void Apply(float[] scanlineBuffer, int scanlineWidth, int offset, int x, int y) { Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); using (PinnedBuffer buffer = new PinnedBuffer(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(); Vector4 sourceVector = this[targetX, targetY].ToVector4(); Vector4 finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, opacity); TColor packed = default(TColor); packed.PackFromVector4(finalColor); this.Target[targetX, targetY] = packed; } } } } } } }