public void BlendFillColorOverBackround <TPixel>( TestImageProvider <TPixel> provider, bool triggerFillRegion, string newColorName, float alpha, PixelBlenderMode blenderMode, float blendPercentage) where TPixel : struct, IPixel <TPixel> { var vec = TestUtils.GetPixelOfNamedColor <RgbaVector>(newColorName).ToVector4(); vec.W = alpha; TPixel fillColor = default; fillColor.PackFromVector4(vec); using (Image <TPixel> image = provider.GetImage()) { TPixel bgColor = image[0, 0]; var options = new GraphicsOptions(false) { BlenderMode = blenderMode, BlendPercentage = blendPercentage }; if (triggerFillRegion) { var region = new ShapeRegion(new RectangularPolygon(0, 0, 16, 16)); image.Mutate(c => c.Fill(options, new SolidBrush <TPixel>(fillColor), region)); } else { image.Mutate(c => c.Fill(options, new SolidBrush <TPixel>(fillColor))); } var testOutputDetails = new { triggerFillRegion = triggerFillRegion, newColorName = newColorName, alpha = alpha, blenderMode = blenderMode, blendPercentage = blendPercentage }; image.DebugSave( provider, testOutputDetails, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); PixelBlender <TPixel> blender = PixelOperations <TPixel> .Instance.GetPixelBlender(blenderMode); TPixel expectedPixel = blender.Blend(bgColor, fillColor, blendPercentage); image.ComparePixelBufferTo(expectedPixel); } }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixelDst> source, Rectangle sourceRectangle, Configuration configuration) { Image <TPixelSrc> targetImage = this.Image; PixelBlender <TPixelDst> 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; ParallelFor.WithConfiguration( minY, maxY, configuration, y => { Span <TPixelDst> background = source.GetPixelRowSpan(y).Slice(minX, width); Span <TPixelSrc> foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); blender.Blend <TPixelSrc>(memoryAllocator, background, background, foreground, this.Opacity); }); }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixelDst> source, Rectangle sourceRectangle, Configuration configuration) { Image <TPixelSrc> targetImage = this.Image; PixelBlender <TPixelDst> 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 <TPixelDst> background = source.GetPixelRowSpan(y).Slice(minX, width); Span <TPixelSrc> foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); blender.Blend <TPixelSrc>(configuration, background, background, foreground, this.Opacity); } }); }
public void BlendFillColorOverBackground <TPixel>( TestImageProvider <TPixel> provider, bool triggerFillRegion, string newColorName, float alpha, PixelColorBlendingMode blenderMode, float blendPercentage) where TPixel : unmanaged, IPixel <TPixel> { Color fillColor = TestUtils.GetColorByName(newColorName).WithAlpha(alpha); using (Image <TPixel> image = provider.GetImage()) { TPixel bgColor = image[0, 0]; var options = new DrawingOptions { GraphicsOptions = new GraphicsOptions { Antialias = false, ColorBlendingMode = blenderMode, BlendPercentage = blendPercentage } }; if (triggerFillRegion) { var path = new RectangularPolygon(0, 0, 16, 16); image.Mutate(c => c.SetGraphicsOptions(options.GraphicsOptions).Fill(new SolidBrush(fillColor), path)); } else { image.Mutate(c => c.Fill(options, new SolidBrush(fillColor))); } var testOutputDetails = new { triggerFillRegion, newColorName, alpha, blenderMode, blendPercentage }; image.DebugSave( provider, testOutputDetails, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); PixelBlender <TPixel> blender = PixelOperations <TPixel> .Instance.GetPixelBlender( blenderMode, PixelAlphaCompositionMode.SrcOver); TPixel expectedPixel = blender.Blend(bgColor, fillColor.ToPixel <TPixel>(), blendPercentage); image.ComparePixelBufferTo(expectedPixel); } }
public void TestAlphaCompositionModes(Rgba32 backdrop, Rgba32 source, float opacity, PixelAlphaCompositionMode mode, Rgba32 expectedResult) { PixelBlender <Rgba32> blender = PixelOperations <Rgba32> .Instance.GetPixelBlender(PixelColorBlendingMode.Normal, mode); Rgba32 actualResult = blender.Blend(backdrop, source, opacity); // var str = actualResult.Rgba.ToString("X8"); // used to extract expectedResults Assert.Equal(actualResult.ToVector4(), expectedResult.ToVector4()); }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; // 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); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } int width = maxX - minX; using (IBuffer <TPixel> colors = source.MemoryAllocator.Allocate <TPixel>(width)) using (IBuffer <float> amount = source.MemoryAllocator.Allocate <float>(width)) { // Be careful! Do not capture colorSpan & amountSpan in the lambda below! Span <TPixel> colorSpan = colors.GetSpan(); Span <float> amountSpan = amount.GetSpan(); // TODO: Use Span.Fill? for (int i = 0; i < width; i++) { colorSpan[i] = this.Color; amountSpan[i] = this.GraphicsOptions.BlendPercentage; } PixelBlender <TPixel> blender = PixelOperations <TPixel> .Instance.GetPixelBlender(this.GraphicsOptions.BlenderMode); Parallel.For( minY, maxY, configuration.ParallelOptions, y => { Span <TPixel> destination = source.GetPixelRowSpan(y - startY).Slice(minX - startX, width); // This switched color & destination in the 2nd and 3rd places because we are applying the target color under the current one blender.Blend(source.MemoryAllocator, destination, colors.GetSpan(), destination, amount.GetSpan()); }); } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TPixel> source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; // 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); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } int width = maxX - minX; using (Buffer <TPixel> colors = new Buffer <TPixel>(width)) using (Buffer <float> amount = new Buffer <float>(width)) using (PixelAccessor <TPixel> sourcePixels = source.Lock()) { for (int i = 0; i < width; i++) { colors[i] = this.Value; amount[i] = this.options.BlendPercentage; } PixelBlender <TPixel> blender = PixelOperations <TPixel> .Instance.GetPixelBlender(this.options.BlenderMode); Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; Span <TPixel> destination = sourcePixels.GetRowSpan(offsetY).Slice(minX - startX, width); // this switched color & destination in the 2nd and 3rd places because we are applying the target colour under the current one blender.Blend(destination, colors, destination, amount); }); } }
/// <inheritdoc/> protected override void OnFrameApply( ImageFrame <TPixelBg> source, Rectangle sourceRectangle, Configuration configuration) { Image <TPixelFg> targetImage = this.Image; PixelBlender <TPixelBg> 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.Right); 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); // not a valid operation because rectangle does not overlap with this image. if (workingRect.Width <= 0 || workingRect.Height <= 0) { throw new ImageProcessingException( "Cannot draw image because the source image does not overlap the target image."); } ParallelHelper.IterateRows( workingRect, configuration, rows => { for (int y = rows.Min; y < rows.Max; y++) { Span <TPixelBg> background = source.GetPixelRowSpan(y).Slice(minX, width); Span <TPixelFg> foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); blender.Blend <TPixelFg>(configuration, background, background, foreground, this.Opacity); } }); }
/// <inheritdoc/> protected override void OnApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { Image <TPixel> targetImage = this.Image; PixelBlender <TPixel> 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; using (var amount = new Buffer <float>(width)) { for (int i = 0; i < width; i++) { amount[i] = this.Opacity; } Parallel.For( minY, maxY, configuration.ParallelOptions, y => { Span <TPixel> background = source.GetPixelRowSpan(y).Slice(minX, width); Span <TPixel> foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); blender.Blend(background, background, foreground, amount); }); } }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { Image <TPixel> targetImage = this.Image; PixelBlender <TPixel> 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; MemoryManager memoryManager = this.Image.GetConfiguration().MemoryManager; using (IBuffer <float> amount = memoryManager.Allocate <float>(width)) { amount.Span.Fill(this.Opacity); Parallel.For( minY, maxY, configuration.ParallelOptions, y => { Span <TPixel> background = source.GetPixelRowSpan(y).Slice(minX, width); Span <TPixel> foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); blender.Blend(memoryManager, background, background, foreground, amount.Span); }); } }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { TPixel color = this.definition.Color.ToPixel <TPixel>(); GraphicsOptions graphicsOptions = this.definition.GraphicsOptions; int startY = this.SourceRectangle.Y; int endY = this.SourceRectangle.Bottom; int startX = this.SourceRectangle.X; int endX = this.SourceRectangle.Right; // 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); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } int width = maxX - minX; var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); using (IMemoryOwner <TPixel> colors = source.MemoryAllocator.Allocate <TPixel>(width)) using (IMemoryOwner <float> amount = source.MemoryAllocator.Allocate <float>(width)) { // Be careful! Do not capture colorSpan & amountSpan in the lambda below! Span <TPixel> colorSpan = colors.GetSpan(); Span <float> amountSpan = amount.GetSpan(); colorSpan.Fill(color); amountSpan.Fill(graphicsOptions.BlendPercentage); PixelBlender <TPixel> blender = PixelOperations <TPixel> .Instance.GetPixelBlender(graphicsOptions); ParallelHelper.IterateRows( workingRect, this.Configuration, rows => { for (int y = rows.Min; y < rows.Max; y++) { Span <TPixel> destination = source.GetPixelRowSpan(y - startY).Slice(minX - startX, width); // This switched color & destination in the 2nd and 3rd places because we are applying the target color under the current one blender.Blend( source.Configuration, destination, colors.GetSpan(), destination, amount.GetSpan()); } }); } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TPixel> source, Rectangle sourceRectangle) { using (PixelAccessor <TPixel> sourcePixels = source.Lock()) using (PenApplicator <TPixel> applicator = this.Pen.CreateApplicator(sourcePixels, this.Path.Bounds, this.Options)) { Rectangle rect = RectangleF.Ceiling(applicator.RequiredRegion); int polyStartY = rect.Y - PaddingFactor; int polyEndY = rect.Bottom + PaddingFactor; int startX = rect.X - PaddingFactor; int endX = rect.Right + PaddingFactor; int minX = Math.Max(sourceRectangle.Left, startX); int maxX = Math.Min(sourceRectangle.Right, endX); int minY = Math.Max(sourceRectangle.Top, polyStartY); int maxY = Math.Min(sourceRectangle.Bottom, polyEndY); // Align start/end positions. minX = Math.Max(0, minX); maxX = Math.Min(source.Width, maxX); minY = Math.Max(0, minY); maxY = Math.Min(source.Height, maxY); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { polyStartY = 0; } int width = maxX - minX; PixelBlender <TPixel> blender = PixelOperations <TPixel> .Instance.GetPixelBlender(this.Options.BlenderMode); Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - polyStartY; using (Buffer <float> amount = new Buffer <float>(width)) using (Buffer <TPixel> colors = new Buffer <TPixel>(width)) { for (int i = 0; i < width; i++) { int x = i + minX; int offsetX = x - startX; PointInfo info = this.Path.GetPointInfo(offsetX, offsetY); ColoredPointInfo <TPixel> color = applicator.GetColor(offsetX, offsetY, info); amount[i] = (this.Opacity(color.DistanceFromElement) * this.Options.BlendPercentage).Clamp(0, 1); colors[i] = color.Color; } BufferSpan <TPixel> destination = sourcePixels.GetRowSpan(offsetY).Slice(minX - startX, width); blender.Blend(destination, destination, colors, amount); } }); } }