示例#1
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            int sourceWidth     = source.Width;
            int sourceHeight    = source.Height;
            int tileWidth       = (int)MathF.Ceiling(sourceWidth / (float)this.Tiles);
            int tileHeight      = (int)MathF.Ceiling(sourceHeight / (float)this.Tiles);
            int tileCount       = this.Tiles;
            int halfTileWidth   = tileWidth / 2;
            int halfTileHeight  = tileHeight / 2;
            int luminanceLevels = this.LuminanceLevels;

            // The image is split up into tiles. For each tile the cumulative distribution function will be calculated.
            using (var cdfData = new CdfTileData(this.Configuration, sourceWidth, sourceHeight, this.Tiles, this.Tiles, tileWidth, tileHeight, luminanceLevels))
            {
                cdfData.CalculateLookupTables(source, this);

                var tileYStartPositions = new List <(int y, int cdfY)>();
                int cdfY   = 0;
                int yStart = halfTileHeight;
                for (int tile = 0; tile < tileCount - 1; tile++)
                {
                    tileYStartPositions.Add((yStart, cdfY));
                    cdfY++;
                    yStart += tileHeight;
                }

                var operation = new RowIntervalOperation(cdfData, tileYStartPositions, tileWidth, tileHeight, tileCount, halfTileWidth, luminanceLevels, source);
                ParallelRowIterator.IterateRowIntervals(
                    this.Configuration,
                    new Rectangle(0, 0, sourceWidth, tileYStartPositions.Count),
                    in operation);

                ref TPixel pixelsBase = ref source.GetPixelReference(0, 0);

                // Fix left column
                ProcessBorderColumn(ref pixelsBase, cdfData, 0, sourceWidth, sourceHeight, this.Tiles, tileHeight, xStart: 0, xEnd: halfTileWidth, luminanceLevels);

                // Fix right column
                int rightBorderStartX = ((this.Tiles - 1) * tileWidth) + halfTileWidth;
                ProcessBorderColumn(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, sourceHeight, this.Tiles, tileHeight, xStart: rightBorderStartX, xEnd: sourceWidth, luminanceLevels);

                // Fix top row
                ProcessBorderRow(ref pixelsBase, cdfData, 0, sourceWidth, this.Tiles, tileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels);

                // Fix bottom row
                int bottomBorderStartY = ((this.Tiles - 1) * tileHeight) + halfTileHeight;
                ProcessBorderRow(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, this.Tiles, tileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels);

                // Left top corner
                ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, 0, xStart: 0, xEnd: halfTileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels);

                // Left bottom corner
                ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, this.Tiles - 1, xStart: 0, xEnd: halfTileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels);

                // Right top corner
                ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, 0, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels);

                // Right bottom corner
                ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, this.Tiles - 1, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels);
            }
示例#2
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            TPixel glowColor    = this.definition.GlowColor.ToPixel <TPixel>();
            float  blendPercent = this.definition.GraphicsOptions.BlendPercentage;

            var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());

            Vector2 center      = Rectangle.Center(interest);
            float   finalRadius = this.definition.Radius.Calculate(interest.Size);
            float   maxDistance = finalRadius > 0
                ? MathF.Min(finalRadius, interest.Width * .5F)
                : interest.Width * .5F;

            Configuration   configuration = this.Configuration;
            MemoryAllocator allocator     = configuration.MemoryAllocator;

            using IMemoryOwner <TPixel> rowColors = allocator.Allocate <TPixel>(interest.Width);
            rowColors.GetSpan().Fill(glowColor);

            var operation = new RowIntervalOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source);

            ParallelRowIterator.IterateRows <RowIntervalOperation, float>(
                configuration,
                interest,
                in operation);
        }
示例#3
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source, ImageFrame <TPixel> destination)
        {
            Rectangle     sourceRectangle = this.SourceRectangle;
            Configuration configuration   = this.Configuration;

            // Handle resize dimensions identical to the original
            if (source.Width == destination.Width &&
                source.Height == destination.Height &&
                sourceRectangle == this.targetRectangle)
            {
                // The cloned will be blank here copy all the pixel data over
                source.GetPixelMemoryGroup().CopyTo(destination.GetPixelMemoryGroup());
                return;
            }

            int width    = this.targetWidth;
            int height   = this.targetHeight;
            var interest = Rectangle.Intersect(this.targetRectangle, new Rectangle(0, 0, width, height));

            if (this.resampler is NearestNeighborResampler)
            {
                // Scaling factors
                float widthFactor  = sourceRectangle.Width / (float)this.targetRectangle.Width;
                float heightFactor = sourceRectangle.Height / (float)this.targetRectangle.Height;

                var operation = new RowIntervalOperation(sourceRectangle, this.targetRectangle, widthFactor, heightFactor, source, destination);
                ParallelRowIterator.IterateRows(
                    configuration,
                    interest,
                    in operation);

                return;
            }

            PixelConversionModifiers conversionModifiers =
                PixelConversionModifiers.Premultiply.ApplyCompanding(this.compand);

            BufferArea <TPixel> sourceArea = source.PixelBuffer.GetArea(sourceRectangle);

            // To reintroduce parallel processing, we to launch multiple workers
            // for different row intervals of the image.
            using (var worker = new ResizeWorker <TPixel>(
                       configuration,
                       sourceArea,
                       conversionModifiers,
                       this.horizontalKernelMap,
                       this.verticalKernelMap,
                       width,
                       interest,
                       this.targetRectangle.Location))
            {
                worker.Initialize();

                var workingInterval = new RowInterval(interest.Top, interest.Bottom);
                worker.FillDestinationPixels(workingInterval, destination.PixelBuffer);
            }
        }
示例#4
0
        /// <summary>
        /// Swaps the image at the Y-axis, which goes vertically through the middle at half of the width of the image.
        /// </summary>
        /// <param name="source">The source image to apply the process to.</param>
        /// <param name="configuration">The configuration.</param>
        private void FlipY(ImageFrame <TPixel> source, Configuration configuration)
        {
            var operation = new RowIntervalOperation(source);

            ParallelRowIterator.IterateRows(
                configuration,
                source.Bounds(),
                in operation);
        }
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            var interest  = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
            var operation = new RowIntervalOperation(interest.X, source, this.definition.Matrix, this.Configuration);

            ParallelRowIterator.IterateRows <RowIntervalOperation, Vector4>(
                this.Configuration,
                interest,
                in operation);
        }
        /// <inheritdoc />
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            Configuration configuration = this.Configuration;

            using IFrameQuantizer <TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer <TPixel>(configuration);
            using IQuantizedFrame <TPixel> quantized      = frameQuantizer.QuantizeFrame(source);

            var operation = new RowIntervalOperation(this.SourceRectangle, source, quantized);

            ParallelRowIterator.IterateRows(
                configuration,
                this.SourceRectangle,
                in operation);
        }
        protected override void OnFrameApply(ImageFrame <TPixelBg> source)
        {
            var targetBounds = this.TargetImage.Bounds();
            var sourceBounds = this.Source.Bounds();
            var maxWidth     = Math.Min(targetBounds.Width, sourceBounds.Width);

            var operation = new RowIntervalOperation(
                source,
                this.TargetImage,
                this.Blender,
                this.Configuration,
                maxWidth);

            ParallelRowIterator.IterateRowIntervals(this.Configuration, this.SourceRectangle, in operation);
        }
示例#8
0
        /// <inheritdoc />
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            var interest = Rectangle.Intersect(source.Bounds(), this.SourceRectangle);

            Configuration configuration = this.Configuration;

            using IQuantizer <TPixel> frameQuantizer   = this.quantizer.CreatePixelSpecificQuantizer <TPixel>(configuration);
            using IndexedImageFrame <TPixel> quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(source, interest);

            var operation = new RowIntervalOperation(this.SourceRectangle, source, quantized);

            ParallelRowIterator.IterateRowIntervals(
                configuration,
                interest,
                in operation);
        }
        /// <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);
        }
示例#10
0
        /// <summary>
        /// Returns a copy of the image frame in the given pixel format.
        /// </summary>
        /// <typeparam name="TPixel2">The pixel format.</typeparam>
        /// <param name="configuration">The configuration providing initialization code which allows extending the library.</param>
        /// <returns>The <see cref="ImageFrame{TPixel2}"/></returns>
        internal ImageFrame <TPixel2> CloneAs <TPixel2>(Configuration configuration)
            where TPixel2 : unmanaged, IPixel <TPixel2>
        {
            if (typeof(TPixel2) == typeof(TPixel))
            {
                return(this.Clone(configuration) as ImageFrame <TPixel2>);
            }

            var target    = new ImageFrame <TPixel2>(configuration, this.Width, this.Height, this.Metadata.DeepClone());
            var operation = new RowIntervalOperation <TPixel2>(this, target, configuration);

            ParallelRowIterator.IterateRowIntervals(
                configuration,
                this.Bounds(),
                in operation);

            return(target);
        }
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            byte   threshold = (byte)MathF.Round(this.definition.Threshold * 255F);
            TPixel upper     = this.definition.UpperColor.ToPixel <TPixel>();
            TPixel lower     = this.definition.LowerColor.ToPixel <TPixel>();

            Rectangle     sourceRectangle = this.SourceRectangle;
            Configuration configuration   = this.Configuration;

            var  interest    = Rectangle.Intersect(sourceRectangle, source.Bounds());
            bool isAlphaOnly = typeof(TPixel) == typeof(A8);

            var operation = new RowIntervalOperation(interest, source, upper, lower, threshold, isAlphaOnly);

            ParallelRowIterator.IterateRows(
                configuration,
                interest,
                in operation);
        }
示例#12
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            int brushSize = this.definition.BrushSize;

            if (brushSize <= 0 || brushSize > source.Height || brushSize > source.Width)
            {
                throw new ArgumentOutOfRangeException(nameof(brushSize));
            }

            using Buffer2D <TPixel> targetPixels = this.Configuration.MemoryAllocator.Allocate2D <TPixel>(source.Size());

            source.CopyTo(targetPixels);

            var operation = new RowIntervalOperation(this.SourceRectangle, targetPixels, source, this.Configuration, brushSize >> 1, this.definition.Levels);

            ParallelRowIterator.IterateRowIntervals(
                this.Configuration,
                this.SourceRectangle,
                in operation);

            Buffer2D <TPixel> .SwapOrCopyContent(source.PixelBuffer, targetPixels);
        }
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixelBg> source)
        {
            Rectangle     sourceRectangle = this.SourceRectangle;
            Configuration configuration   = this.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.");
            }

            var operation = new RowIntervalOperation(source, targetImage, blender, configuration, minX, width, locationY, targetX, this.Opacity);

            ParallelRowIterator.IterateRows(
                configuration,
                workingRect,
                in operation);
        }
        /// <inheritdoc />
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            DenseMatrix <float>[] kernels = this.Kernels.Flatten();

            var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());

            // We need a clean copy for each pass to start from
            using ImageFrame <TPixel> cleanCopy = source.Clone();

            using (var processor = new ConvolutionProcessor <TPixel>(this.Configuration, kernels[0], true, this.Source, interest))
            {
                processor.Apply(source);
            }

            if (kernels.Length == 1)
            {
                return;
            }

            // Additional runs
            for (int i = 1; i < kernels.Length; i++)
            {
                using ImageFrame <TPixel> pass = cleanCopy.Clone();

                using (var processor = new ConvolutionProcessor <TPixel>(this.Configuration, kernels[i], true, this.Source, interest))
                {
                    processor.Apply(pass);
                }

                var operation = new RowIntervalOperation(source.PixelBuffer, pass.PixelBuffer, interest);
                ParallelRowIterator.IterateRows(
                    this.Configuration,
                    interest,
                    in operation);
            }
        }
示例#15
0
        private static void SecondPass <TFrameQuantizer, TPixel>(
            ref TFrameQuantizer quantizer,
            ImageFrame <TPixel> source,
            Rectangle bounds,
            Memory <byte> output,
            ReadOnlyMemory <TPixel> palette)
            where TFrameQuantizer : struct, IFrameQuantizer <TPixel>
            where TPixel : unmanaged, IPixel <TPixel>
        {
            IDither dither = quantizer.Options.Dither;

            if (dither is null)
            {
                var operation = new RowIntervalOperation <TFrameQuantizer, TPixel>(quantizer, source, output, bounds, palette);
                ParallelRowIterator.IterateRowIntervals(
                    quantizer.Configuration,
                    bounds,
                    in operation);

                return;
            }

            dither.ApplyQuantizationDither(ref quantizer, palette, source, output, bounds);
        }