// 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);
}
}
}
}