// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Buffers; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Drawing { /// /// Combines two images together by blending the pixels. /// /// The pixel format of destination image. /// The pixel format of source image. internal class DrawImageProcessor : ImageProcessor where TPixelDst : struct, IPixel where TPixelSrc : struct, IPixel { /// /// Initializes a new instance of the class. /// /// The image to blend with the currently processing image. /// The location to draw the blended image. /// The blending mode to use when drawing the image. /// The Alpha blending mode to use when drawing the image. /// The opacity of the image to blend. Must be between 0 and 1. public DrawImageProcessor(Image image, Point location, PixelColorBlendingMode colorBlendingMode, PixelAlphaCompositionMode alphaCompositionMode, float opacity) { Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); this.Image = image; this.Opacity = opacity; this.Blender = PixelOperations.Instance.GetPixelBlender(colorBlendingMode, alphaCompositionMode); this.Location = location; } /// /// Gets the image to blend /// public Image Image { get; } /// /// Gets the opacity of the image to blend /// public float Opacity { get; } /// /// Gets the pixel blender /// public PixelBlender Blender { get; } /// /// Gets the location to draw the blended image /// public Point Location { get; } /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { Image targetImage = this.Image; PixelBlender blender = this.Blender; int locationY = this.Location.Y; // Align start/end positions. Rectangle bounds = targetImage.Bounds(); int minX = Math.Max(this.Location.X, sourceRectangle.X); int maxX = Math.Min(this.Location.X + bounds.Width, sourceRectangle.Width); int targetX = minX - this.Location.X; int minY = Math.Max(this.Location.Y, sourceRectangle.Y); int maxY = Math.Min(this.Location.Y + bounds.Height, sourceRectangle.Bottom); int width = maxX - minX; var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); ParallelHelper.IterateRows( workingRect, configuration, rows => { for (int y = rows.Min; y < rows.Max; y++) { Span background = source.GetPixelRowSpan(y).Slice(minX, width); Span foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); blender.Blend(configuration, background, background, foreground, this.Opacity); } }); } } }