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