Beispiel #1
0
            public PatternPenApplicator(IBrush <TColor, TPacked> brush, RectangleF region, float width, float[] pattern)
            {
                this.brush       = brush.CreateApplicator(region);
                this.halfWidth   = width / 2;
                this.totalLength = 0;

                this.pattern    = new float[pattern.Length + 1];
                this.pattern[0] = 0;
                for (var i = 0; i < pattern.Length; i++)
                {
                    this.totalLength   += pattern[i] * width;
                    this.pattern[i + 1] = this.totalLength;
                }

                this.RequiredRegion = RectangleF.Outset(region, width);
            }
Beispiel #2
0
            public PatternPenApplicator(PixelAccessor <TColor> sourcePixels, IBrush <TColor> brush, RectangleF region, float width, float[] pattern)
            {
                this.brush       = brush.CreateApplicator(sourcePixels, region);
                this.halfWidth   = width / 2;
                this.totalLength = 0;

                this.pattern    = new float[pattern.Length + 1];
                this.pattern[0] = 0;
                for (int i = 0; i < pattern.Length; i++)
                {
                    this.totalLength   += pattern[i] * width;
                    this.pattern[i + 1] = this.totalLength;
                }

                this.RequiredRegion = RectangleF.Outset(region, width);
            }
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());

            if (interest.Width == 0 || interest.Height == 0)
            {
                return;
            }

            Configuration   configuration = this.Configuration;
            IBrush          brush         = this.definition.Brush;
            GraphicsOptions options       = this.definition.Options;

            // If there's no reason for blending, then avoid it.
            if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush))
            {
                ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration)
                                                             .MultiplyMinimumPixelsPerTask(4);

                TPixel colorPixel = solidBrush.Color.ToPixel <TPixel>();

                var solidOperation = new SolidBrushRowIntervalOperation(interest, source, colorPixel);
                ParallelRowIterator.IterateRowIntervals(
                    interest,
                    parallelSettings,
                    in solidOperation);

                return;
            }

            using IMemoryOwner <float> amount         = configuration.MemoryAllocator.Allocate <float>(interest.Width);
            using BrushApplicator <TPixel> applicator = brush.CreateApplicator(
                      configuration,
                      options,
                      source,
                      interest);

            amount.Memory.Span.Fill(1F);

            var operation = new RowIntervalOperation(interest, applicator, amount.Memory);

            ParallelRowIterator.IterateRowIntervals(
                configuration,
                interest,
                in operation);
        }
Beispiel #4
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            Configuration   configuration = this.Configuration;
            GraphicsOptions options       = this.definition.Options;
            IBrush          brush         = this.definition.Brush;
            Region          region        = this.definition.Region;
            Rectangle       rect          = region.Bounds;

            // Align start/end positions.
            int minX = Math.Max(0, rect.Left);
            int maxX = Math.Min(source.Width, rect.Right);
            int minY = Math.Max(0, rect.Top);
            int maxY = Math.Min(source.Height, rect.Bottom);

            if (minX >= maxX)
            {
                return; // no effect inside image;
            }

            if (minY >= maxY)
            {
                return; // no effect inside image;
            }

            int   maxIntersections = region.MaxIntersections;
            float subpixelCount    = 4;

            // we need to offset the pixel grid to account for when we outline a path.
            // basically if the line is [1,2] => [3,2] then when outlining at 1 we end up with a region of [0.5,1.5],[1.5, 1.5],[3.5,2.5],[2.5,2.5]
            // and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the#
            // region to align with the pixel grid.
            float offset = 0.5f;

            if (options.Antialias)
            {
                offset        = 0f; // we are antialiasing skip offsetting as real antialiasing should take care of offset.
                subpixelCount = options.AntialiasSubpixelDepth;
                if (subpixelCount < 4)
                {
                    subpixelCount = 4;
                }
            }

            using (BrushApplicator <TPixel> applicator = brush.CreateApplicator(source, rect, options))
            {
                int scanlineWidth = maxX - minX;
                using (IMemoryOwner <float> bBuffer = source.MemoryAllocator.Allocate <float>(maxIntersections))
                    using (IMemoryOwner <float> bScanline = source.MemoryAllocator.Allocate <float>(scanlineWidth))
                    {
                        bool  scanlineDirty         = true;
                        float subpixelFraction      = 1f / subpixelCount;
                        float subpixelFractionPoint = subpixelFraction / subpixelCount;

                        Span <float> buffer   = bBuffer.GetSpan();
                        Span <float> scanline = bScanline.GetSpan();

                        bool   isSolidBrushWithoutBlending = this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush);
                        TPixel solidBrushColor             = isSolidBrushWithoutBlending ? solidBrush.Color.ToPixel <TPixel>() : default;

                        for (int y = minY; y < maxY; y++)
                        {
                            if (scanlineDirty)
                            {
                                scanline.Clear();
                                scanlineDirty = false;
                            }

                            float yPlusOne = y + 1;
                            for (float subPixel = y; subPixel < yPlusOne; subPixel += subpixelFraction)
                            {
                                int pointsFound = region.Scan(subPixel + offset, buffer, configuration);
                                if (pointsFound == 0)
                                {
                                    // nothing on this line, skip
                                    continue;
                                }

                                QuickSort.Sort(buffer.Slice(0, pointsFound));

                                for (int point = 0; point < pointsFound && point < buffer.Length - 1; point += 2)
                                {
                                    // points will be paired up
                                    float scanStart = buffer[point] - minX;
                                    float scanEnd   = buffer[point + 1] - minX;
                                    int   startX    = (int)MathF.Floor(scanStart + offset);
                                    int   endX      = (int)MathF.Floor(scanEnd + offset);

                                    if (startX >= 0 && startX < scanline.Length)
                                    {
                                        for (float x = scanStart; x < startX + 1; x += subpixelFraction)
                                        {
                                            scanline[startX] += subpixelFractionPoint;
                                            scanlineDirty     = true;
                                        }
                                    }

                                    if (endX >= 0 && endX < scanline.Length)
                                    {
                                        for (float x = endX; x < scanEnd; x += subpixelFraction)
                                        {
                                            scanline[endX] += subpixelFractionPoint;
                                            scanlineDirty   = true;
                                        }
                                    }

                                    int nextX = startX + 1;
                                    endX  = Math.Min(endX, scanline.Length); // reduce to end to the right edge
                                    nextX = Math.Max(nextX, 0);
                                    for (int x = nextX; x < endX; x++)
                                    {
                                        scanline[x]  += subpixelFraction;
                                        scanlineDirty = true;
                                    }
                                }
                            }

                            if (scanlineDirty)
                            {
                                if (!options.Antialias)
                                {
                                    bool hasOnes  = false;
                                    bool hasZeros = false;
                                    for (int x = 0; x < scanlineWidth; x++)
                                    {
                                        if (scanline[x] >= 0.5)
                                        {
                                            scanline[x] = 1;
                                            hasOnes     = true;
                                        }
                                        else
                                        {
                                            scanline[x] = 0;
                                            hasZeros    = true;
                                        }
                                    }

                                    if (isSolidBrushWithoutBlending && hasOnes != hasZeros)
                                    {
                                        if (hasOnes)
                                        {
                                            source.GetPixelRowSpan(y).Slice(minX, scanlineWidth).Fill(solidBrushColor);
                                        }

                                        continue;
                                    }
                                }

                                applicator.Apply(scanline, minX, y);
                            }
                        }
                    }
            }
        }
Beispiel #5
0
 public SolidPenApplicator(PixelAccessor <TColor> sourcePixels, IBrush <TColor> brush, RectangleF region, float width)
 {
     this.brush          = brush.CreateApplicator(sourcePixels, region);
     this.halfWidth      = width / 2;
     this.RequiredRegion = RectangleF.Outset(region, width);
 }
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            Rectangle     sourceRectangle = this.SourceRectangle;
            Configuration configuration   = this.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;

            var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);

            IBrush          brush   = this.definition.Brush;
            GraphicsOptions options = this.definition.Options;

            // If there's no reason for blending, then avoid it.
            if (this.IsSolidBrushWithoutBlending(out SolidBrush solidBrush))
            {
                ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4);

                TPixel colorPixel = solidBrush.Color.ToPixel <TPixel>();

                ParallelHelper.IterateRows(
                    workingRect,
                    parallelSettings,
                    rows =>
                {
                    for (int y = rows.Min; y < rows.Max; y++)
                    {
                        source.GetPixelRowSpan(y).Slice(minX, width).Fill(colorPixel);
                    }
                });
            }
            else
            {
                // Reset offset if necessary.
                if (minX > 0)
                {
                    startX = 0;
                }

                if (minY > 0)
                {
                    startY = 0;
                }

                using (IMemoryOwner <float> amount = source.MemoryAllocator.Allocate <float>(width))
                    using (BrushApplicator <TPixel> applicator = brush.CreateApplicator(
                               source,
                               sourceRectangle,
                               options))
                    {
                        amount.GetSpan().Fill(1f);

                        ParallelHelper.IterateRows(
                            workingRect,
                            configuration,
                            rows =>
                        {
                            for (int y = rows.Min; y < rows.Max; y++)
                            {
                                int offsetY = y - startY;
                                int offsetX = minX - startX;

                                applicator.Apply(amount.GetSpan(), offsetX, offsetY);
                            }
                        });
                    }
            }
        }
Beispiel #7
0
 public SolidPenApplicator(IBrush <TColor, TPacked> brush, RectangleF region, float width)
 {
     this.brush          = brush.CreateApplicator(region);
     this.halfWidth      = width / 2;
     this.RequiredRegion = RectangleF.Outset(region, width);
 }
Beispiel #8
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            Configuration   configuration   = this.Configuration;
            ShapeOptions    shapeOptions    = this.definition.Options.ShapeOptions;
            GraphicsOptions graphicsOptions = this.definition.Options.GraphicsOptions;
            IBrush          brush           = this.definition.Brush;
            Region          region          = this.definition.Region;
            Rectangle       rect            = region.Bounds;

            bool   isSolidBrushWithoutBlending = IsSolidBrushWithoutBlending(graphicsOptions, this.definition.Brush, out SolidBrush solidBrush);
            TPixel solidBrushColor             = isSolidBrushWithoutBlending ? solidBrush.Color.ToPixel <TPixel>() : default;

            // Align start/end positions.
            int minX = Math.Max(0, rect.Left);
            int maxX = Math.Min(source.Width, rect.Right);
            int minY = Math.Max(0, rect.Top);
            int maxY = Math.Min(source.Height, rect.Bottom);

            if (minX >= maxX)
            {
                return; // no effect inside image;
            }

            if (minY >= maxY)
            {
                return; // no effect inside image;
            }

            int subpixelCount = FillRegionProcessor.MinimumSubpixelCount;

            // we need to offset the pixel grid to account for when we outline a path.
            // basically if the line is [1,2] => [3,2] then when outlining at 1 we end up with a region of [0.5,1.5],[1.5, 1.5],[3.5,2.5],[2.5,2.5]
            // and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the#
            // region to align with the pixel grid.
            if (graphicsOptions.Antialias)
            {
                subpixelCount = Math.Max(subpixelCount, graphicsOptions.AntialiasSubpixelDepth);
            }

            using BrushApplicator <TPixel> applicator = brush.CreateApplicator(configuration, graphicsOptions, source, rect);
            int             scanlineWidth             = maxX - minX;
            MemoryAllocator allocator     = this.Configuration.MemoryAllocator;
            bool            scanlineDirty = true;

            var scanner = PolygonScanner.Create(
                region.Shape,
                minY,
                maxY,
                subpixelCount,
                shapeOptions.IntersectionRule,
                configuration.MemoryAllocator);

            try
            {
                using IMemoryOwner <float> bScanline = allocator.Allocate <float>(scanlineWidth);
                Span <float> scanline = bScanline.Memory.Span;

                while (scanner.MoveToNextPixelLine())
                {
                    if (scanlineDirty)
                    {
                        scanline.Clear();
                    }

                    scanlineDirty = scanner.ScanCurrentPixelLineInto(minX, 0, scanline);

                    if (scanlineDirty)
                    {
                        int y = scanner.PixelLineY;
                        if (!graphicsOptions.Antialias)
                        {
                            bool hasOnes  = false;
                            bool hasZeros = false;
                            for (int x = 0; x < scanline.Length; x++)
                            {
                                if (scanline[x] >= 0.5)
                                {
                                    scanline[x] = 1;
                                    hasOnes     = true;
                                }
                                else
                                {
                                    scanline[x] = 0;
                                    hasZeros    = true;
                                }
                            }

                            if (isSolidBrushWithoutBlending && hasOnes != hasZeros)
                            {
                                if (hasOnes)
                                {
                                    source.GetPixelRowSpan(y).Slice(minX, scanlineWidth).Fill(solidBrushColor);
                                }

                                continue;
                            }
                        }

                        applicator.Apply(scanline, minX, y);
                    }
                }
            }
            finally
            {
                // ref structs can't implement interfaces so technically PolygonScanner is not IDisposable
                scanner.Dispose();
            }
        }
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            Configuration   configuration               = this.Configuration;
            ShapeOptions    shapeOptions                = this.definition.Options.ShapeOptions;
            GraphicsOptions graphicsOptions             = this.definition.Options.GraphicsOptions;
            IBrush          brush                       = this.definition.Brush;
            bool            isSolidBrushWithoutBlending = IsSolidBrushWithoutBlending(graphicsOptions, brush, out SolidBrush solidBrush);
            TPixel          solidBrushColor             = isSolidBrushWithoutBlending ? solidBrush.Color.ToPixel <TPixel>() : default;

            // Align start/end positions.
            var interest = Rectangle.Intersect(this.bounds, source.Bounds());

            if (interest.Equals(Rectangle.Empty))
            {
                return; // No effect inside image;
            }

            int minX          = interest.Left;
            int subpixelCount = FillPathProcessor.MinimumSubpixelCount;

            // We need to offset the pixel grid to account for when we outline a path.
            // basically if the line is [1,2] => [3,2] then when outlining at 1 we end up with a region of [0.5,1.5],[1.5, 1.5],[3.5,2.5],[2.5,2.5]
            // and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the#
            // region to align with the pixel grid.
            if (graphicsOptions.Antialias)
            {
                subpixelCount = Math.Max(subpixelCount, graphicsOptions.AntialiasSubpixelDepth);
            }

            using BrushApplicator <TPixel> applicator = brush.CreateApplicator(configuration, graphicsOptions, source, interest);
            int             scanlineWidth             = interest.Width;
            MemoryAllocator allocator     = this.Configuration.MemoryAllocator;
            bool            scanlineDirty = true;

            var scanner = PolygonScanner.Create(
                this.path,
                interest.Top,
                interest.Bottom,
                subpixelCount,
                shapeOptions.IntersectionRule,
                configuration.MemoryAllocator);

            try
            {
                using IMemoryOwner <float> bScanline = allocator.Allocate <float>(scanlineWidth);
                Span <float> scanline = bScanline.Memory.Span;

                while (scanner.MoveToNextPixelLine())
                {
                    if (scanlineDirty)
                    {
                        scanline.Clear();
                    }

                    scanlineDirty = scanner.ScanCurrentPixelLineInto(minX, 0F, scanline);

                    if (scanlineDirty)
                    {
                        int y = scanner.PixelLineY;
                        if (!graphicsOptions.Antialias)
                        {
                            bool hasOnes  = false;
                            bool hasZeros = false;
                            for (int x = 0; x < scanline.Length; x++)
                            {
                                if (scanline[x] >= 0.5F)
                                {
                                    scanline[x] = 1F;
                                    hasOnes     = true;
                                }
                                else
                                {
                                    scanline[x] = 0F;
                                    hasZeros    = true;
                                }
                            }

                            if (isSolidBrushWithoutBlending && hasOnes != hasZeros)
                            {
                                if (hasOnes)
                                {
                                    source.PixelBuffer.DangerousGetRowSpan(y).Slice(minX, scanlineWidth).Fill(solidBrushColor);
                                }

                                continue;
                            }
                        }

                        applicator.Apply(scanline, minX, y);
                    }
                }
            }
            finally
            {
                scanner.Dispose();
            }
        }
Beispiel #10
0
 public SolidPenApplicator(PixelAccessor <TPixel> sourcePixels, IBrush <TPixel> brush, RectangleF region, float width, GraphicsOptions options)
 {
     this.brush          = brush.CreateApplicator(sourcePixels, region, options);
     this.halfWidth      = width / 2;
     this.RequiredRegion = RectangleF.Outset(region, width);
 }