// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageSharp.Drawing.Processors { using System; using System.Numerics; using System.Threading.Tasks; using ImageSharp.Memory; using ImageSharp.PixelFormats; using ImageSharp.Processing; using SixLabors.Primitives; /// /// Combines two images together by blending the pixels. /// /// The pixel format. internal class DrawImageProcessor : ImageProcessor where TPixel : struct, IPixel { private readonly PixelBlender blender; /// /// Initializes a new instance of the class. /// /// The image to blend with the currently processing image. /// The size to draw the blended image. /// The location to draw the blended image. /// The opacity of the image to blend. Between 0 and 100. public DrawImageProcessor(Image image, Size size, Point location, GraphicsOptions options) { Guard.MustBeBetweenOrEqualTo(options.BlendPercentage, 0, 1, nameof(options.BlendPercentage)); this.Image = image; this.Size = size; this.Alpha = options.BlendPercentage; this.blender = PixelOperations.Instance.GetPixelBlender(options.BlenderMode); this.Location = location; } /// /// Gets the image to blend. /// public Image Image { get; private set; } /// /// Gets the alpha percentage value. /// public float Alpha { get; } /// /// Gets the size to draw the blended image. /// public Size Size { get; } /// /// Gets the location to draw the blended image. /// public Point Location { get; } /// protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { Image disposableImage = null; Image targetImage = this.Image; try { if (targetImage.Bounds.Size != this.Size) { targetImage = disposableImage = new Image(this.Image).Resize(this.Size.Width, this.Size.Height); } // Align start/end positions. Rectangle bounds = this.Image.Bounds; int minX = Math.Max(this.Location.X, sourceRectangle.X); int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); maxX = Math.Min(this.Location.X + this.Size.Width, maxX); int minY = Math.Max(this.Location.Y, sourceRectangle.Y); int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); maxY = Math.Min(this.Location.Y + this.Size.Height, maxY); int width = maxX - minX; using (Buffer amount = new Buffer(width)) using (PixelAccessor toBlendPixels = targetImage.Lock()) using (PixelAccessor sourcePixels = source.Lock()) { for (int i = 0; i < width; i++) { amount[i] = this.Alpha; } Parallel.For( minY, maxY, this.ParallelOptions, y => { Span background = sourcePixels.GetRowSpan(y).Slice(minX, width); Span foreground = toBlendPixels.GetRowSpan(y - this.Location.Y).Slice(0, width); this.blender.Blend(background, background, foreground, amount); }); } } finally { disposableImage?.Dispose(); } } } }