// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Drawing.Brushes; using SixLabors.ImageSharp.Processing.Processors; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Drawing.Processors { /// /// Using the brush as a source of pixels colors blends the brush color with source. /// /// The pixel format. internal class FillProcessor : ImageProcessor where TPixel : struct, IPixel { /// /// The brush. /// private readonly IBrush brush; private readonly GraphicsOptions options; /// /// Initializes a new instance of the class. /// /// The brush to source pixel colors from. /// The options public FillProcessor(IBrush brush, GraphicsOptions options) { this.brush = brush; this.options = options; } /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { int startX = sourceRectangle.X; int endX = sourceRectangle.Right; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; // Align start/end positions. int minX = Math.Max(0, startX); int maxX = Math.Min(source.Width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); int width = maxX - minX; // If there's no reason for blending, then avoid it. if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush)) { Parallel.For( minY, maxY, configuration.ParallelOptions, y => { source.GetPixelRowSpan(y).Slice(minX, width).Fill(solidBrush.Color); }); } else { // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } using (IBuffer amount = source.MemoryManager.Allocate(width)) using (BrushApplicator applicator = this.brush.CreateApplicator( source, sourceRectangle, this.options)) { amount.Span.Fill(1f); Parallel.For( minY, maxY, configuration.ParallelOptions, y => { int offsetY = y - startY; int offsetX = minX - startX; applicator.Apply(amount.Span, offsetX, offsetY); }); } } } private bool IsSolidBrushWithoutBlending(out SolidBrush solidBrush) { solidBrush = this.brush as SolidBrush; return solidBrush != null && ((this.options.BlenderMode == PixelBlenderMode.Normal && this.options.BlendPercentage == 1f && solidBrush.Color.ToVector4().W == 1f) || (this.options.BlenderMode == PixelBlenderMode.Over && this.options.BlendPercentage == 1f && solidBrush.Color.ToVector4().W == 1f) || (this.options.BlenderMode == PixelBlenderMode.Src)); } } }