// 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.PixelFormats; using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Drawing { /// /// Combines two images together by blending the pixels. /// /// The pixel format. internal class DrawImageProcessor : ImageProcessor where TPixel : struct, IPixel { /// /// Initializes a new instance of the class. /// /// The image to blend with the currently processing image. /// The opacity of the image to blend. Must be between 0 and 1. public DrawImageProcessor(Image image, float opacity) : this(image, Point.Empty, opacity) { } /// /// Initializes a new instance of the class. /// /// The image to blend with the currently processing image. /// /// The options containing the opacity of the image to blend and blending mode. /// Opacity must be between 0 and 1. /// public DrawImageProcessor(Image image, GraphicsOptions options) : this(image, Point.Empty, options) { } /// /// Initializes a new instance of the class. /// /// The image to blend with the currently processing image. /// The location to draw the blended image. /// The opacity of the image to blend. Must be between 0 and 1. public DrawImageProcessor(Image image, Point location, float opacity) : this(image, location, opacity, GraphicsOptions.Default.BlenderMode) { } /// /// Initializes a new instance of the class. /// /// The image to blend with the currently processing image. /// The location to draw the blended image. /// /// The options containing the opacity of the image to blend and blending mode. /// Opacity must be between 0 and 1. /// public DrawImageProcessor(Image image, Point location, GraphicsOptions options) : this(image, location, options.BlendPercentage, options.BlenderMode) { } /// /// Initializes a new instance of the class. /// /// The image to blend with the currently processing image. /// The opacity of the image to blend. Must be between 0 and 1. /// The blending mode to use when drawing the image. public DrawImageProcessor(Image image, float opacity, PixelBlenderMode blenderMode) : this(image, Point.Empty, opacity, blenderMode) { } /// /// Initializes a new instance of the class. /// /// The image to blend with the currently processing image. /// The location to draw the blended image. /// The opacity of the image to blend. Must be between 0 and 1. /// The blending mode to use when drawing the image. public DrawImageProcessor(Image image, Point location, float opacity, PixelBlenderMode blenderMode) { Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity)); this.Image = image; this.Opacity = opacity; this.Blender = PixelOperations.Instance.GetPixelBlender(blenderMode); 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; MemoryAllocator memoryAllocator = this.Image.GetConfiguration().MemoryAllocator; using (IMemoryOwner amount = memoryAllocator.Allocate(width)) { amount.GetSpan().Fill(this.Opacity); Parallel.For( minY, maxY, configuration.ParallelOptions, y => { Span background = source.GetPixelRowSpan(y).Slice(minX, width); Span foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); blender.Blend(memoryAllocator, background, background, foreground, amount.GetSpan()); }); } } } }