// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.Primitives; using SixLabors.Shapes; using Xunit; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Drawing { [GroupOutput("Drawing")] public class FillSolidBrushTests { [Theory] [WithBlankImages(1, 1, PixelTypes.Rgba32)] [WithBlankImages(7, 4, PixelTypes.Rgba32)] [WithBlankImages(16, 7, PixelTypes.Rgba32)] [WithBlankImages(33, 32, PixelTypes.Rgba32)] [WithBlankImages(400, 500, PixelTypes.Rgba32)] public void DoesNotDependOnSize(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { var color = Color.HotPink; image.Mutate(c => c.Fill(color)); image.DebugSave(provider, appendPixelTypeToFileName: false); image.ComparePixelBufferTo(color); } } [Theory] [WithBlankImages(16, 16, PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.RgbaVector)] public void DoesNotDependOnSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { var color = Color.HotPink; image.Mutate(c => c.Fill(color)); image.DebugSave(provider, appendSourceFileOrDescription: false); image.ComparePixelBufferTo(color); } } [Theory] [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, "Blue")] [WithSolidFilledImages(16, 16, "Yellow", PixelTypes.Rgba32, "Khaki")] public void WhenColorIsOpaque_OverridePreviousColor( TestImageProvider provider, string newColorName) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { Color color = TestUtils.GetColorByName(newColorName); image.Mutate(c => c.Fill(color)); image.DebugSave( provider, newColorName, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); image.ComparePixelBufferTo(color); } } [Theory] [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 5, 7, 3, 8)] [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 8, 5, 6, 4)] public void FillRegion(TestImageProvider provider, int x0, int y0, int w, int h) where TPixel : struct, IPixel { FormattableString testDetails = $"(x{x0},y{y0},w{w},h{h})"; var region = new RectangleF(x0, y0, w, h); Color color = TestUtils.GetColorByName("Blue"); provider.RunValidatingProcessorTest(c => c.Fill(color, region), testDetails, ImageComparer.Exact); } [Theory] [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 5, 7, 3, 8)] [WithSolidFilledImages(16, 16, "Red", PixelTypes.Rgba32, 8, 5, 6, 4)] public void FillRegion_WorksOnWrappedMemoryImage( TestImageProvider provider, int x0, int y0, int w, int h) where TPixel : struct, IPixel { FormattableString testDetails = $"(x{x0},y{y0},w{w},h{h})"; var region = new RectangleF(x0, y0, w, h); Color color = TestUtils.GetColorByName("Blue"); provider.RunValidatingProcessorTestOnWrappedMemoryImage( c => c.Fill(color, region), testDetails, ImageComparer.Exact, useReferenceOutputFrom: nameof(this.FillRegion)); } public static readonly TheoryData BlendData = new TheoryData { { false, "Blue", 0.5f, PixelColorBlendingMode.Normal, 1.0f }, { false, "Blue", 1.0f, PixelColorBlendingMode.Normal, 0.5f }, { false, "Green", 0.5f, PixelColorBlendingMode.Normal, 0.3f }, { false, "HotPink", 0.8f, PixelColorBlendingMode.Normal, 0.8f }, { false, "Blue", 0.5f, PixelColorBlendingMode.Multiply, 1.0f }, { false, "Blue", 1.0f, PixelColorBlendingMode.Multiply, 0.5f }, { false, "Green", 0.5f, PixelColorBlendingMode.Multiply, 0.3f }, { false, "HotPink", 0.8f, PixelColorBlendingMode.Multiply, 0.8f }, { false, "Blue", 0.5f, PixelColorBlendingMode.Add, 1.0f }, { false, "Blue", 1.0f, PixelColorBlendingMode.Add, 0.5f }, { false, "Green", 0.5f, PixelColorBlendingMode.Add, 0.3f }, { false, "HotPink", 0.8f, PixelColorBlendingMode.Add, 0.8f }, { true, "Blue", 0.5f, PixelColorBlendingMode.Normal, 1.0f }, { true, "Blue", 1.0f, PixelColorBlendingMode.Normal, 0.5f }, { true, "Green", 0.5f, PixelColorBlendingMode.Normal, 0.3f }, { true, "HotPink", 0.8f, PixelColorBlendingMode.Normal, 0.8f }, { true, "Blue", 0.5f, PixelColorBlendingMode.Multiply, 1.0f }, { true, "Blue", 1.0f, PixelColorBlendingMode.Multiply, 0.5f }, { true, "Green", 0.5f, PixelColorBlendingMode.Multiply, 0.3f }, { true, "HotPink", 0.8f, PixelColorBlendingMode.Multiply, 0.8f }, { true, "Blue", 0.5f, PixelColorBlendingMode.Add, 1.0f }, { true, "Blue", 1.0f, PixelColorBlendingMode.Add, 0.5f }, { true, "Green", 0.5f, PixelColorBlendingMode.Add, 0.3f }, { true, "HotPink", 0.8f, PixelColorBlendingMode.Add, 0.8f }, }; [Theory] [WithSolidFilledImages(nameof(BlendData), 16, 16, "Red", PixelTypes.Rgba32)] public void BlendFillColorOverBackground( TestImageProvider provider, bool triggerFillRegion, string newColorName, float alpha, PixelColorBlendingMode blenderMode, float blendPercentage) where TPixel : struct, IPixel { Color fillColor = TestUtils.GetColorByName(newColorName).WithAlpha(alpha); using (Image image = provider.GetImage()) { TPixel bgColor = image[0, 0]; var options = new GraphicsOptions(false) { ColorBlendingMode = blenderMode, BlendPercentage = blendPercentage }; if (triggerFillRegion) { var region = new ShapeRegion(new RectangularPolygon(0, 0, 16, 16)); image.Mutate(c => c.Fill(options, new SolidBrush(fillColor), region)); } else { image.Mutate(c => c.Fill(options, new SolidBrush(fillColor))); } var testOutputDetails = new { triggerFillRegion = triggerFillRegion, newColorName = newColorName, alpha = alpha, blenderMode = blenderMode, blendPercentage = blendPercentage }; image.DebugSave( provider, testOutputDetails, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); PixelBlender blender = PixelOperations.Instance.GetPixelBlender( blenderMode, PixelAlphaCompositionMode.SrcOver); TPixel expectedPixel = blender.Blend(bgColor, fillColor.ToPixel(), blendPercentage); image.ComparePixelBufferTo(expectedPixel); } } } }