Beispiel #1
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            // this is a no-op as we have processes all as an image, we should be able to pass out of before email apply a skip frames outcome
            Draw(this.textRenderer.FillOperations, this.Brush);
            Draw(this.textRenderer.OutlineOperations, this.Pen?.StrokeFill);

            void Draw(List <DrawingOperation> operations, IBrush brush)
            {
                if (operations?.Count > 0)
                {
                    using (BrushApplicator <TPixel> app = brush.CreateApplicator(this.Configuration, this.textRenderer.Options, source, this.SourceRectangle))
                    {
                        foreach (DrawingOperation operation in operations)
                        {
                            Buffer2D <float> buffer = operation.Map;
                            int startY     = operation.Location.Y;
                            int startX     = operation.Location.X;
                            int offsetSpan = 0;

                            if (startX + buffer.Height < 0)
                            {
                                continue;
                            }

                            if (startX + buffer.Width < 0)
                            {
                                continue;
                            }

                            if (startX < 0)
                            {
                                offsetSpan = -startX;
                                startX     = 0;
                            }

                            if (startX >= source.Width)
                            {
                                continue;
                            }

                            int firstRow = 0;
                            if (startY < 0)
                            {
                                firstRow = -startY;
                            }

                            int maxHeight = source.Height - startY;
                            int end       = Math.Min(operation.Map.Height, maxHeight);

                            for (int row = firstRow; row < end; row++)
                            {
                                int          y    = startY + row;
                                Span <float> span = buffer.GetRowSpan(row).Slice(offsetSpan);
                                app.Apply(span, startX, y);
                            }
                        }
                    }
                }
            }
        }
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);
            }
Beispiel #3
0
        /// <inheritdoc/>
        protected override void OnApply(ImageFrame <TPixel> 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);

            // Reset offset if necessary.
            if (minX > 0)
            {
                startX = 0;
            }

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

            int width = maxX - minX;

            using (var amount = new Buffer <float>(width))
                using (BrushApplicator <TPixel> applicator = this.brush.CreateApplicator(source, sourceRectangle, this.options))
                {
                    for (int i = 0; i < width; i++)
                    {
                        amount[i] = this.options.BlendPercentage;
                    }

                    Parallel.For(
                        minY,
                        maxY,
                        configuration.ParallelOptions,
                        y =>
                    {
                        int offsetY = y - startY;
                        int offsetX = minX - startX;

                        applicator.Apply(amount, offsetX, offsetY);
                    });
                }
        }
Beispiel #4
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> 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);

            // Reset offset if necessary.
            if (minX > 0)
            {
                startX = 0;
            }

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

            int width = maxX - minX;

            using (IBuffer <float> amount = source.MemoryManager.Allocate <float>(width))
                using (BrushApplicator <TPixel> 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);
                    });
                }
        }
        /// <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);
        }
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            Region    region = this.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 (this.Options.Antialias)
            {
                offset        = 0f; // we are antialiasing skip offsetting as real antialiasing should take care of offset.
                subpixelCount = this.Options.AntialiasSubpixelDepth;
                if (subpixelCount < 4)
                {
                    subpixelCount = 4;
                }
            }

            using (BrushApplicator <TPixel> applicator = this.Brush.CreateApplicator(source, rect, this.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 <TPixel> solidBrush);

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

                            float yPlusOne = y + 1;
                            for (float subPixel = (float)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 += 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 (!this.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(solidBrush.Color);
                                        }

                                        continue;
                                    }
                                }

                                applicator.Apply(scanline, minX, y);
                            }
                        }
                    }
            }
        }
Beispiel #7
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);
                            }
                        });
                    }
            }
        }
        /// <inheritdoc/>
        protected override void OnApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            Region    region = this.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;
            }

            ArrayPool <float> arrayPool = ArrayPool <float> .Shared;

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

            if (this.Options.Antialias)
            {
                subpixelCount = this.Options.AntialiasSubpixelDepth;
                if (subpixelCount < 4)
                {
                    subpixelCount = 4;
                }
            }

            using (BrushApplicator <TPixel> applicator = this.Brush.CreateApplicator(source, rect, this.Options))
            {
                float[] buffer        = arrayPool.Rent(maxIntersections);
                int     scanlineWidth = maxX - minX;
                using (var scanline = new Buffer <float>(scanlineWidth))
                {
                    try
                    {
                        bool scanlineDirty = true;
                        for (int y = minY; y < maxY; y++)
                        {
                            if (scanlineDirty)
                            {
                                // clear the buffer
                                for (int x = 0; x < scanlineWidth; x++)
                                {
                                    scanline[x] = 0;
                                }

                                scanlineDirty = false;
                            }

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

                                QuickSort(new Span <float>(buffer, 0, pointsFound));

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

                                    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 (!this.Options.Antialias)
                                {
                                    for (int x = 0; x < scanlineWidth; x++)
                                    {
                                        if (scanline[x] > 0.5)
                                        {
                                            scanline[x] = 1;
                                        }
                                        else
                                        {
                                            scanline[x] = 0;
                                        }
                                    }
                                }

                                applicator.Apply(scanline, minX, y);
                            }
                        }
                    }
                    finally
                    {
                        arrayPool.Return(buffer);
                    }
                }
            }
        }
 public RowIntervalOperation(Rectangle bounds, BrushApplicator <TPixel> applicator, Memory <float> amount)
 {
     this.bounds     = bounds;
     this.applicator = applicator;
     this.amount     = amount;
 }
Beispiel #11
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();
            }
        }
Beispiel #12
0
        /// <inheritdoc/>
        protected override void OnApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            Region    region = this.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;
            }

            ArrayPool <float> arrayPool = ArrayPool <float> .Shared;

            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 alline with the pixel grid.
            float offset = 0.5f;

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

            using (BrushApplicator <TPixel> applicator = this.Brush.CreateApplicator(source, rect, this.Options))
            {
                float[] buffer        = arrayPool.Rent(maxIntersections);
                int     scanlineWidth = maxX - minX;
                using (var scanline = new Buffer <float>(scanlineWidth))
                {
                    try
                    {
                        bool scanlineDirty = true;
                        for (int y = minY; y < maxY; y++)
                        {
                            if (scanlineDirty)
                            {
                                // clear the buffer
                                for (int x = 0; x < scanlineWidth; x++)
                                {
                                    scanline[x] = 0;
                                }

                                scanlineDirty = false;
                            }

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

                                QuickSort(new Span <float>(buffer, 0, pointsFound));

                                for (int point = 0; point < pointsFound; 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 (!this.Options.Antialias)
                                {
                                    for (int x = 0; x < scanlineWidth; x++)
                                    {
                                        if (scanline[x] >= 0.5)
                                        {
                                            scanline[x] = 1;
                                        }
                                        else
                                        {
                                            scanline[x] = 0;
                                        }
                                    }
                                }

                                applicator.Apply(scanline, minX, y);
                            }
                        }
                    }
                    finally
                    {
                        arrayPool.Return(buffer);
                    }
                }
            }
        }
Beispiel #13
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> 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 <TPixel> 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 <float> amount = source.MemoryManager.Allocate <float>(width))
                    using (BrushApplicator <TPixel> 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);
                        });
                    }
            }
        }
Beispiel #14
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            // this is a no-op as we have processes all as an image, we should be able to pass out of before email apply a skip frames outcome
            Draw(this.textRenderer.FillOperations, this.Brush);
            Draw(this.textRenderer.OutlineOperations, this.Pen?.StrokeFill);

            void Draw(List <DrawingOperation> operations, IBrush brush)
            {
                if (operations?.Count > 0)
                {
                    var brushes = new Dictionary <Color, BrushApplicator <TPixel> >();
                    foreach (DrawingOperation operation in operations)
                    {
                        if (operation.Color.HasValue)
                        {
                            if (!brushes.TryGetValue(operation.Color.Value, out _))
                            {
                                brushes[operation.Color.Value] = new SolidBrush(operation.Color.Value).CreateApplicator(
                                    this.Configuration,
                                    this.textRenderer.Options.GraphicsOptions,
                                    source,
                                    this.SourceRectangle);
                            }
                        }
                    }

                    using (BrushApplicator <TPixel> app = brush.CreateApplicator(this.Configuration, this.textRenderer.Options.GraphicsOptions, source, this.SourceRectangle))
                    {
                        foreach (DrawingOperation operation in operations)
                        {
                            var currentApp = app;
                            if (operation.Color != null)
                            {
                                brushes.TryGetValue(operation.Color.Value, out currentApp);
                            }

                            Buffer2D <float> buffer = operation.Map;
                            int startY     = operation.Location.Y;
                            int startX     = operation.Location.X;
                            int offsetSpan = 0;

                            if (startX + buffer.Height < 0)
                            {
                                continue;
                            }

                            if (startX + buffer.Width < 0)
                            {
                                continue;
                            }

                            if (startX < 0)
                            {
                                offsetSpan = -startX;
                                startX     = 0;
                            }

                            if (startX >= source.Width)
                            {
                                continue;
                            }

                            int firstRow = 0;
                            if (startY < 0)
                            {
                                firstRow = -startY;
                            }

                            int maxHeight = source.Height - startY;
                            int end       = Math.Min(operation.Map.Height, maxHeight);

                            for (int row = firstRow; row < end; row++)
                            {
                                int          y    = startY + row;
                                Span <float> span = buffer.GetRowSpan(row).Slice(offsetSpan);
                                currentApp.Apply(span, startX, y);
                            }
                        }
                    }

                    foreach (var app in brushes.Values)
                    {
                        app.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 #16
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);
 }