Exemple #1
0
        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);
                }
            });
        }
Exemple #4
0
        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);
            }
        }
Exemple #5
0
        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());
        }
Exemple #6
0
        /// <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);
                });
            }
        }
Exemple #10
0
        /// <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);
                            }
                    });
                }
        }