// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Buffers; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; namespace SixLabors.ImageSharp.Processing { /// /// A primitive that converts a point into a color for discovering the fill color based on an implementation. /// /// The pixel format. /// public abstract class BrushApplicator : IDisposable where TPixel : struct, IPixel { /// /// Initializes a new instance of the class. /// /// The configuration instance to use when performing operations. /// The graphics options. /// The target. internal BrushApplicator(Configuration configuration, GraphicsOptions options, ImageFrame target) { this.Configuration = configuration; this.Target = target; this.Options = options; this.Blender = PixelOperations.Instance.GetPixelBlender(options); } /// /// Gets the configuration instance to use when performing operations. /// protected Configuration Configuration { get; } /// /// Gets the pixel blender. /// internal PixelBlender Blender { get; } /// /// Gets the target image. /// protected ImageFrame Target { get; } /// /// Gets thegraphics options /// protected GraphicsOptions Options { get; } /// /// Gets the overlay pixel at the specified position. /// /// The x-coordinate. /// The y-coordinate. /// The at the specified position. internal abstract TPixel this[int x, int y] { get; } /// public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } /// /// Disposes the object and frees resources for the Garbage Collector. /// /// Whether to dispose managed and unmanaged objects. protected virtual void Dispose(bool disposing) { } /// /// Applies the opacity weighting for each pixel in a scanline to the target based on the pattern contained in the brush. /// /// A collection of opacity values between 0 and 1 to be merged with the brushed color value before being applied to the target. /// The x-position in the target pixel space that the start of the scanline data corresponds to. /// The y-position in the target pixel space that whole scanline corresponds to. /// scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs. internal virtual void Apply(Span scanline, int x, int y) { MemoryAllocator memoryAllocator = this.Target.MemoryAllocator; using (IMemoryOwner amountBuffer = memoryAllocator.Allocate(scanline.Length)) using (IMemoryOwner overlay = memoryAllocator.Allocate(scanline.Length)) { Span amountSpan = amountBuffer.Memory.Span; Span overlaySpan = overlay.Memory.Span; float blendPercentage = this.Options.BlendPercentage; if (blendPercentage < 1) { for (int i = 0; i < scanline.Length; i++) { amountSpan[i] = scanline[i] * blendPercentage; overlaySpan[i] = this[x + i, y]; } } else { for (int i = 0; i < scanline.Length; i++) { amountSpan[i] = scanline[i]; overlaySpan[i] = this[x + i, y]; } } Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend(this.Configuration, destinationRow, destinationRow, overlaySpan, amountSpan); } } } }