예제 #1
0
        /// <summary>
        /// Rotates the image 90 degrees clockwise at the centre point.
        /// </summary>
        /// <param name="source">The source image.</param>
        /// <param name="destination">The destination image.</param>
        /// <param name="configuration">The configuration.</param>
        private void Rotate90(ImageFrame <TPixel> source, ImageFrame <TPixel> destination, Configuration configuration)
        {
            int       width             = source.Width;
            int       height            = source.Height;
            Rectangle destinationBounds = destination.Bounds();

            ParallelHelper.IterateRows(
                source.Bounds(),
                configuration,
                rows =>
            {
                for (int y = rows.Min; y < rows.Max; y++)
                {
                    Span <TPixel> sourceRow = source.GetPixelRowSpan(y);
                    int newX = height - y - 1;
                    for (int x = 0; x < width; x++)
                    {
                        // TODO: Optimize this:
                        if (destinationBounds.Contains(newX, x))
                        {
                            destination[newX, x] = sourceRow[x];
                        }
                    }
                }
            });
        }
        /// <inheritdoc/>
        protected override void OnApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            float  threshold = this.Threshold * 255F;
            TPixel upper     = this.UpperColor;
            TPixel lower     = this.LowerColor;

            var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
            int startY   = interest.Y;
            int endY     = interest.Bottom;
            int startX   = interest.X;
            int endX     = interest.Right;

            bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8);

            Parallel.For(
                startY,
                endY,
                configuration.ParallelOptions,
                y =>
            {
                Span <TPixel> row = source.GetPixelRowSpan(y);
                var rgba          = default(Rgba32);

                for (int x = startX; x < endX; x++)
                {
                    ref TPixel color = ref row[x];
                    color.ToRgba32(ref rgba);

                    // Convert to grayscale using ITU-R Recommendation BT.709 if required
                    float luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B);
                    color           = luminance >= threshold ? upper : lower;
                }
            });
        }
        /// <summary>
        /// Creates the quantized frame.
        /// </summary>
        /// <typeparam name="TPixel">The type of the pixel.</typeparam>
        /// <param name="options">The options.</param>
        /// <param name="image">The image.</param>
        public static IndexedImageFrame <TPixel> CreateQuantizedFrame <TPixel>(
            PngEncoderOptions options,
            Image <TPixel> image)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            if (options.ColorType != PngColorType.Palette)
            {
                return(null);
            }

            byte bits = (byte)options.BitDepth;

            if (Array.IndexOf(PngConstants.ColorTypes[options.ColorType.Value], bits) == -1)
            {
                throw new NotSupportedException("Bit depth is not supported or not valid.");
            }

            // Use the metadata to determine what quantization depth to use if no quantizer has been set.
            if (options.Quantizer is null)
            {
                var maxColors = ImageMaths.GetColorCountForBitDepth(bits);
                options.Quantizer = new WuQuantizer(new QuantizerOptions {
                    MaxColors = maxColors
                });
            }

            // Create quantized frame returning the palette and set the bit depth.
            using (IFrameQuantizer <TPixel> frameQuantizer = options.Quantizer.CreateFrameQuantizer <TPixel>(image.GetConfiguration()))
            {
                ImageFrame <TPixel> frame = image.Frames.RootFrame;
                return(frameQuantizer.QuantizeFrame(frame, frame.Bounds()));
            }
        }
        public TiffPaletteWriter(
            ImageFrame <TPixel> image,
            IQuantizer quantizer,
            MemoryAllocator memoryAllocator,
            Configuration configuration,
            TiffEncoderEntriesCollector entriesCollector,
            int bitsPerPixel)
            : base(image, memoryAllocator, configuration, entriesCollector)
        {
            DebugGuard.NotNull(quantizer, nameof(quantizer));
            DebugGuard.NotNull(configuration, nameof(configuration));
            DebugGuard.NotNull(entriesCollector, nameof(entriesCollector));
            DebugGuard.MustBeBetweenOrEqualTo(bitsPerPixel, 4, 8, nameof(bitsPerPixel));

            this.BitsPerPixel      = bitsPerPixel;
            this.maxColors         = this.BitsPerPixel == 4 ? 16 : 256;
            this.colorPaletteSize  = this.maxColors * 3;
            this.colorPaletteBytes = this.colorPaletteSize * 2;
            using IQuantizer <TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer <TPixel>(this.Configuration, new QuantizerOptions()
            {
                MaxColors = this.maxColors
            });
            this.quantizedImage = frameQuantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds());

            this.AddColorMapTag();
        }
예제 #5
0
        /// <summary>
        /// Rotates the image 270 degrees clockwise at the centre point.
        /// </summary>
        /// <param name="source">The source image.</param>
        /// <param name="destination">The destination image.</param>
        /// <param name="configuration">The configuration.</param>
        private void Rotate270(ImageFrame <TPixel> source, ImageFrame <TPixel> destination, Configuration configuration)
        {
            int       width             = source.Width;
            int       height            = source.Height;
            Rectangle destinationBounds = destination.Bounds();

            Parallel.For(
                0,
                height,
                configuration.ParallelOptions,
                y =>
            {
                Span <TPixel> sourceRow = source.GetPixelRowSpan(y);
                for (int x = 0; x < width; x++)
                {
                    int newX = height - y - 1;
                    newX     = height - newX - 1;
                    int newY = width - x - 1;

                    if (destinationBounds.Contains(newX, newY))
                    {
                        destination[newX, newY] = sourceRow[x];
                    }
                }
            });
        }
예제 #6
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
            int startX   = interest.X;

            ColorMatrix matrix = this.definition.Matrix;

            ParallelHelper.IterateRowsWithTempBuffer <Vector4>(
                interest,
                configuration,
                (rows, vectorBuffer) =>
            {
                for (int y = rows.Min; y < rows.Max; y++)
                {
                    Span <Vector4> vectorSpan = vectorBuffer.Span;
                    int length            = vectorSpan.Length;
                    Span <TPixel> rowSpan = source.GetPixelRowSpan(y).Slice(startX, length);
                    PixelOperations <TPixel> .Instance.ToVector4(configuration, rowSpan, vectorSpan);

                    Vector4Utils.Transform(vectorSpan, ref matrix);

                    PixelOperations <TPixel> .Instance.FromVector4Destructive(configuration, vectorSpan, rowSpan);
                }
            });
        }
예제 #7
0
        /// <inheritdoc/>
        protected override void OnApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            int       height       = this.CanvasRectangle.Height;
            int       width        = this.CanvasRectangle.Width;
            Matrix3x2 matrix       = this.GetCenteredMatrix(source, this.processMatrix);
            Rectangle sourceBounds = source.Bounds();

            using (var targetPixels = new PixelAccessor <TPixel>(width, height))
            {
                Parallel.For(
                    0,
                    height,
                    configuration.ParallelOptions,
                    y =>
                {
                    Span <TPixel> targetRow = targetPixels.GetRowSpan(y);

                    for (int x = 0; x < width; x++)
                    {
                        var transformedPoint = Point.Skew(new Point(x, y), matrix);

                        if (sourceBounds.Contains(transformedPoint.X, transformedPoint.Y))
                        {
                            targetRow[x] = source[transformedPoint.X, transformedPoint.Y];
                        }
                    }
                });

                source.SwapPixelsBuffers(targetPixels);
            }
        }
예제 #8
0
        /// <summary>
        /// Creates the quantized frame.
        /// </summary>
        /// <typeparam name="TPixel">The type of the pixel.</typeparam>
        /// <param name="options">The options.</param>
        /// <param name="image">The image.</param>
        public static IndexedImageFrame <TPixel> CreateQuantizedFrame <TPixel>(
            PngEncoderOptions options,
            Image <TPixel> image)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            if (options.ColorType != PngColorType.Palette)
            {
                return(null);
            }

            // Use the metadata to determine what quantization depth to use if no quantizer has been set.
            if (options.Quantizer is null)
            {
                byte bits      = (byte)options.BitDepth;
                var  maxColors = ImageMaths.GetColorCountForBitDepth(bits);
                options.Quantizer = new WuQuantizer(new QuantizerOptions {
                    MaxColors = maxColors
                });
            }

            // Create quantized frame returning the palette and set the bit depth.
            using (IQuantizer <TPixel> frameQuantizer = options.Quantizer.CreatePixelSpecificQuantizer <TPixel>(image.GetConfiguration()))
            {
                ImageFrame <TPixel> frame = image.Frames.RootFrame;
                return(frameQuantizer.BuildPaletteAndQuantizeFrame(frame, frame.Bounds()));
            }
        }
예제 #9
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 RowOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source);

            ParallelRowIterator.IterateRows <RowOperation, float>(
                configuration,
                interest,
                in operation);
        }
예제 #10
0
        /// <inheritdoc />
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            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, in this.kernels[0], true, this.Source, interest))
            {
                processor.Apply(source);
            }

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

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

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

                var operation = new RowOperation(source.PixelBuffer, pass.PixelBuffer, interest);
                ParallelRowIterator.IterateRows(
                    this.Configuration,
                    interest,
                    in operation);
            }
        }
        /// <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);

            ReadOnlySpan <TPixel> paletteSpan = quantized.Palette.Span;
            int offsetY = interest.Top;
            int offsetX = interest.Left;
            Buffer2D <TPixel> sourceBuffer = source.PixelBuffer;

            for (int y = interest.Y; y < interest.Height; y++)
            {
                Span <TPixel>       row          = sourceBuffer.DangerousGetRowSpan(y);
                ReadOnlySpan <byte> quantizedRow = quantized.DangerousGetRowSpan(y - offsetY);

                for (int x = interest.Left; x < interest.Right; x++)
                {
                    row[x] = paletteSpan[quantizedRow[x - offsetX]];
                }
            }
        }
예제 #12
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            var       interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
            int       startY   = interest.Y;
            int       endY     = interest.Bottom;
            int       startX   = interest.X;
            int       endX     = interest.Right;
            Matrix4x4 matrix   = this.Matrix;

            ParallelFor.WithConfiguration(
                startY,
                endY,
                configuration,
                y =>
            {
                Span <TPixel> row = source.GetPixelRowSpan(y);

                for (int x = startX; x < endX; x++)
                {
                    ref TPixel pixel = ref row[x];
                    var vector       = Vector4.Transform(pixel.ToVector4(), matrix);
                    pixel.PackFromVector4(vector);
                }
            });
        }
예제 #13
0
        /// <summary>
        /// Quantizes an image frame and return the resulting output pixels.
        /// </summary>
        /// <typeparam name="TFrameQuantizer">The type of frame quantizer.</typeparam>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="quantizer">The frame </param>
        /// <param name="source">The source image frame to quantize.</param>
        /// <param name="bounds">The bounds within the frame to quantize.</param>
        /// <returns>
        /// A <see cref="QuantizedFrame{TPixel}"/> representing a quantized version of the source frame pixels.
        /// </returns>
        public static QuantizedFrame <TPixel> QuantizeFrame <TFrameQuantizer, TPixel>(
            ref TFrameQuantizer quantizer,
            ImageFrame <TPixel> source,
            Rectangle bounds)
            where TFrameQuantizer : struct, IFrameQuantizer <TPixel>
            where TPixel : unmanaged, IPixel <TPixel>
        {
            Guard.NotNull(source, nameof(source));
            var interest = Rectangle.Intersect(source.Bounds(), bounds);

            // Collect the palette. Required before the second pass runs.
            ReadOnlyMemory <TPixel> palette         = quantizer.BuildPalette(source, interest);
            MemoryAllocator         memoryAllocator = quantizer.Configuration.MemoryAllocator;

            var           quantizedFrame = new QuantizedFrame <TPixel>(memoryAllocator, interest.Width, interest.Height, palette);
            Memory <byte> output         = quantizedFrame.GetWritablePixelMemory();

            if (quantizer.Options.Dither is null)
            {
                SecondPass(ref quantizer, source, interest, output, palette);
            }
            else
            {
                // We clone the image as we don't want to alter the original via error diffusion based dithering.
                using (ImageFrame <TPixel> clone = source.Clone())
                {
                    SecondPass(ref quantizer, clone, interest, output, palette);
                }
            }

            return(quantizedFrame);
        }
예제 #14
0
        private void EncodeLocal <TPixel>(Image <TPixel> image, IndexedImageFrame <TPixel> quantized, Stream stream)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            ImageFrame <TPixel> previousFrame = null;
            GifFrameMetadata    previousMeta  = null;

            for (int i = 0; i < image.Frames.Count; i++)
            {
                ImageFrame <TPixel> frame         = image.Frames[i];
                ImageFrameMetadata  metadata      = frame.Metadata;
                GifFrameMetadata    frameMetadata = metadata.GetGifMetadata();
                if (quantized is null)
                {
                    // Allow each frame to be encoded at whatever color depth the frame designates if set.
                    if (previousFrame != null && previousMeta.ColorTableLength != frameMetadata.ColorTableLength &&
                        frameMetadata.ColorTableLength > 0)
                    {
                        var options = new QuantizerOptions
                        {
                            Dither      = this.quantizer.Options.Dither,
                            DitherScale = this.quantizer.Options.DitherScale,
                            MaxColors   = frameMetadata.ColorTableLength
                        };

                        using IFrameQuantizer <TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer <TPixel>(this.configuration, options);
                        quantized = frameQuantizer.QuantizeFrame(frame, frame.Bounds());
                    }
                    else
                    {
                        using IFrameQuantizer <TPixel> frameQuantizer = this.quantizer.CreateFrameQuantizer <TPixel>(this.configuration);
                        quantized = frameQuantizer.QuantizeFrame(frame, frame.Bounds());
                    }
                }

                this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(quantized.Palette.Length);
                this.WriteGraphicalControlExtension(frameMetadata, this.GetTransparentIndex(quantized), stream);
                this.WriteImageDescriptor(frame, true, stream);
                this.WriteColorTable(quantized, stream);
                this.WriteImageData(quantized, stream);

                quantized.Dispose();
                quantized     = null; // So next frame can regenerate it
                previousFrame = frame;
                previousMeta  = frameMetadata;
            }
        }
예제 #15
0
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());

            var operation = new OpaqueRowOperation(this.Configuration, source.PixelBuffer, interest);

            ParallelRowIterator.IterateRows <OpaqueRowOperation, Vector4>(this.Configuration, interest, in operation);
        }
 /// <inheritdoc/>
 protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
 {
     using (Buffer2D <TPixel> firstPassPixels = configuration.MemoryAllocator.Allocate2D <TPixel>(source.Size()))
     {
         this.ApplyConvolution(firstPassPixels, source.PixelBuffer, source.Bounds(), this.KernelX, configuration);
         this.ApplyConvolution(source.PixelBuffer, firstPassPixels, sourceRectangle, this.KernelY, configuration);
     }
 }
예제 #17
0
 /// <summary>
 /// This method pre-seeds the PaletteQuantizer in the AoT compiler for iOS.
 /// </summary>
 /// <typeparam name="TPixel">The pixel format.</typeparam>
 private static void AotCompilePaletteQuantizer <TPixel>()
     where TPixel : unmanaged, IPixel <TPixel>
 {
     using (var test = (PaletteFrameQuantizer <TPixel>) new PaletteQuantizer(Array.Empty <Color>()).CreateFrameQuantizer <TPixel>(Configuration.Default))
     {
         var frame = new ImageFrame <TPixel>(Configuration.Default, 1, 1);
         test.QuantizeFrame(frame, frame.Bounds());
     }
 }
예제 #18
0
 /// <summary>
 /// This method pre-seeds the WuQuantizer in the AoT compiler for iOS.
 /// </summary>
 /// <typeparam name="TPixel">The pixel format.</typeparam>
 private static void AotCompileWuQuantizer <TPixel>()
     where TPixel : unmanaged, IPixel <TPixel>
 {
     using (var test = new WuFrameQuantizer <TPixel>(Configuration.Default, new WuQuantizer().Options))
     {
         var frame = new ImageFrame <TPixel>(Configuration.Default, 1, 1);
         test.QuantizeFrame(frame, frame.Bounds());
     }
 }
예제 #19
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 RowOperation(source);

            ParallelRowIterator.IterateRows(
                configuration,
                source.Bounds(),
                in operation);
        }
예제 #20
0
        /// <inheritdoc/>
        protected override void OnApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            ParallelOptions parallelOptions = configuration.ParallelOptions;

            using (Buffer2D <TPixel> firstPassPixels = configuration.MemoryManager.Allocate2D <TPixel>(source.Size()))
            {
                this.ApplyConvolution(firstPassPixels, source.PixelBuffer, source.Bounds(), this.KernelX, parallelOptions);
                this.ApplyConvolution(source.PixelBuffer, firstPassPixels, sourceRectangle, this.KernelY, parallelOptions);
            }
        }
예제 #21
0
        /// <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)
        {
            var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());

            this.dither.ApplyPaletteDither(
                this.Configuration,
                this.palette.Memory,
                source,
                interest,
                this.ditherScale);
        }
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            byte threshold   = (byte)MathF.Round(this.Definition.Threshold * 255F);
            bool isAlphaOnly = typeof(TPixel) == typeof(Alpha8);

            var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
            int startY   = interest.Y;
            int endY     = interest.Bottom;
            int startX   = interest.X;
            int endX     = interest.Right;

            // Collect the values before looping so we can reduce our calculation count for identical sibling pixels
            TPixel             sourcePixel   = source[startX, startY];
            TPixel             previousPixel = sourcePixel;
            PixelPair <TPixel> pair          = this.GetClosestPixelPair(ref sourcePixel);
            Rgba32             rgba          = default;

            sourcePixel.ToRgba32(ref rgba);

            // Convert to grayscale using ITU-R Recommendation BT.709 if required
            byte luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B);

            for (int y = startY; y < endY; y++)
            {
                Span <TPixel> row = source.GetPixelRowSpan(y);

                for (int x = startX; x < endX; x++)
                {
                    sourcePixel = row[x];

                    // Check if this is the same as the last pixel. If so use that value
                    // rather than calculating it again. This is an inexpensive optimization.
                    if (!previousPixel.Equals(sourcePixel))
                    {
                        pair = this.GetClosestPixelPair(ref sourcePixel);

                        // No error to spread, exact match.
                        if (sourcePixel.Equals(pair.First))
                        {
                            continue;
                        }

                        sourcePixel.ToRgba32(ref rgba);
                        luminance = isAlphaOnly ? rgba.A : ImageMaths.Get8BitBT709Luminance(rgba.R, rgba.G, rgba.B);

                        // Setup the previous pointer
                        previousPixel = sourcePixel;
                    }

                    TPixel transformedPixel = luminance >= threshold ? pair.Second : pair.First;
                    this.Definition.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY);
                }
            }
        }
예제 #24
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            Rgba32 rgba        = default;
            bool   isAlphaOnly = typeof(TPixel) == typeof(Alpha8);

            var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
            int startY   = interest.Y;
            int endY     = interest.Bottom;
            int startX   = interest.X;
            int endX     = interest.Right;

            // Collect the values before looping so we can reduce our calculation count for identical sibling pixels
            TPixel             sourcePixel   = source[startX, startY];
            TPixel             previousPixel = sourcePixel;
            PixelPair <TPixel> pair          = this.GetClosestPixelPair(ref sourcePixel);

            sourcePixel.ToRgba32(ref rgba);

            // Convert to grayscale using ITU-R Recommendation BT.709 if required
            float luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B);

            for (int y = startY; y < endY; y++)
            {
                Span <TPixel> row = source.GetPixelRowSpan(y);

                for (int x = startX; x < endX; x++)
                {
                    sourcePixel = row[x];

                    // Check if this is the same as the last pixel. If so use that value
                    // rather than calculating it again. This is an inexpensive optimization.
                    if (!previousPixel.Equals(sourcePixel))
                    {
                        pair = this.GetClosestPixelPair(ref sourcePixel);

                        // No error to spread, exact match.
                        if (sourcePixel.Equals(pair.First))
                        {
                            continue;
                        }

                        sourcePixel.ToRgba32(ref rgba);
                        luminance = isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B);

                        // Setup the previous pointer
                        previousPixel = sourcePixel;
                    }

                    this.Dither.Dither(source, sourcePixel, pair.Second, pair.First, luminance, x, y);
                }
            }
        }
예제 #25
0
        /// <inheritdoc/>
        protected override void OnApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            int             width           = source.Width;
            int             height          = source.Height;
            ParallelOptions parallelOptions = configuration.ParallelOptions;

            using (var firstPassPixels = new PixelAccessor <TPixel>(width, height))
                using (PixelAccessor <TPixel> sourcePixels = source.Lock())
                {
                    this.ApplyConvolution(firstPassPixels, sourcePixels, source.Bounds(), this.KernelX, parallelOptions);
                    this.ApplyConvolution(sourcePixels, firstPassPixels, sourceRectangle, this.KernelY, parallelOptions);
                }
        }
예제 #26
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)
 {
     ParallelHelper.IterateRows(
         source.Bounds(),
         configuration,
         rows =>
     {
         for (int y = rows.Min; y < rows.Max; y++)
         {
             source.GetPixelRowSpan(y).Reverse();
         }
     });
 }
예제 #27
0
        /// <summary>
        /// This method pre-seeds the default dithering engine (FloydSteinbergDiffuser) in the AoT compiler for iOS.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        private static void AotCompileDithering <TPixel>()
            where TPixel : unmanaged, IPixel <TPixel>
        {
            ErrorDither   errorDither   = ErrorDither.FloydSteinberg;
            OrderedDither orderedDither = OrderedDither.Bayer2x2;
            TPixel        pixel         = default;

            using (var image = new ImageFrame <TPixel>(Configuration.Default, 1, 1))
            {
                errorDither.Dither(image, image.Bounds(), pixel, pixel, 0, 0, 0);
                orderedDither.Dither(pixel, 0, 0, 0, 0);
            }
        }
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            var intersect = Rectangle.Intersect(this.SourceRectangle, source.Bounds());

            Configuration configuration  = this.Configuration;
            TPixel        upper          = this.definition.Upper.ToPixel <TPixel>();
            TPixel        lower          = this.definition.Lower.ToPixel <TPixel>();
            float         thresholdLimit = this.definition.ThresholdLimit;

            int startY = intersect.Y;
            int endY   = intersect.Bottom;
            int startX = intersect.X;
            int endX   = intersect.Right;

            int width  = intersect.Width;
            int height = intersect.Height;

            // ClusterSize defines the size of cluster to used to check for average. Tweaked to support up to 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1'
            byte clusterSize = (byte)Math.Truncate((width / 16f) - 1);

            Buffer2D <TPixel> sourceBuffer = source.PixelBuffer;

            // Using pooled 2d buffer for integer image table and temp memory to hold Rgb24 converted pixel data.
            using (Buffer2D <ulong> intImage = this.Configuration.MemoryAllocator.Allocate2D <ulong>(width, height))
            {
                Rgba32 rgb = default;
                for (int x = startX; x < endX; x++)
                {
                    ulong sum = 0;
                    for (int y = startY; y < endY; y++)
                    {
                        Span <TPixel> row    = sourceBuffer.DangerousGetRowSpan(y);
                        ref TPixel    rowRef = ref MemoryMarshal.GetReference(row);
                        ref TPixel    color  = ref Unsafe.Add(ref rowRef, x);
                        color.ToRgba32(ref rgb);

                        sum += (ulong)(rgb.R + rgb.G + rgb.B);

                        if (x - startX != 0)
                        {
                            intImage[x - startX, y - startY] = intImage[x - startX - 1, y - startY] + sum;
                        }
                        else
                        {
                            intImage[x - startX, y - startY] = sum;
                        }
                    }
                }
예제 #29
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);
        }
예제 #30
0
        /// <inheritdoc/>
        protected override void OnApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            float threshold   = this.Threshold * 255F;
            var   rgba        = default(Rgba32);
            bool  isAlphaOnly = typeof(TPixel) == typeof(Alpha8);

            var interest = Rectangle.Intersect(sourceRectangle, source.Bounds());
            int startY   = interest.Y;
            int endY     = interest.Bottom;
            int startX   = interest.X;
            int endX     = interest.Right;

            // Collect the values before looping so we can reduce our calculation count for identical sibling pixels
            TPixel             sourcePixel   = source[startX, startY];
            TPixel             previousPixel = sourcePixel;
            PixelPair <TPixel> pair          = this.GetClosestPixelPair(ref sourcePixel, this.Palette);

            sourcePixel.ToRgba32(ref rgba);

            // Convert to grayscale using ITU-R Recommendation BT.709 if required
            byte luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255);

            for (int y = startY; y < endY; y++)
            {
                Span <TPixel> row = source.GetPixelRowSpan(y);

                for (int x = startX; x < endX; x++)
                {
                    sourcePixel = row[x];

                    // Check if this is the same as the last pixel. If so use that value
                    // rather than calculating it again. This is an inexpensive optimization.
                    if (!previousPixel.Equals(sourcePixel))
                    {
                        pair = this.GetClosestPixelPair(ref sourcePixel, this.Palette);
                        sourcePixel.ToRgba32(ref rgba);
                        luminance = (byte)(isAlphaOnly ? rgba.A : (.2126F * rgba.R) + (.7152F * rgba.G) + (.0722F * rgba.B)).Clamp(0, 255);

                        // Setup the previous pointer
                        previousPixel = sourcePixel;
                    }

                    TPixel transformedPixel = luminance >= threshold ? pair.First : pair.Second;
                    this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY);
                }
            }
        }