Example #1
0
 private void TestSpanPropertyIsAlwaysTheSame <T>(int desiredLength)
     where T : struct
 {
     using (IMemoryOwner <T> buffer = this.MemoryAllocator.Allocate <T>(desiredLength, AllocationOptions.None))
     {
         ref T a = ref MemoryMarshal.GetReference(buffer.GetSpan());
         ref T b = ref MemoryMarshal.GetReference(buffer.GetSpan());
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            MemoryAllocator memoryAllocator = configuration.MemoryAllocator;
            int             numberOfPixels  = source.Width * source.Height;
            Span <TPixel>   pixels          = source.GetPixelSpan();

            // Build the histogram of the grayscale levels.
            using (IMemoryOwner <int> histogramBuffer = memoryAllocator.Allocate <int>(this.LuminanceLevels, AllocationOptions.Clean))
                using (IMemoryOwner <int> cdfBuffer = memoryAllocator.Allocate <int>(this.LuminanceLevels, AllocationOptions.Clean))
                {
                    Span <int> histogram = histogramBuffer.GetSpan();
                    for (int i = 0; i < pixels.Length; i++)
                    {
                        TPixel sourcePixel = pixels[i];
                        int    luminance   = this.GetLuminance(sourcePixel, this.LuminanceLevels);
                        histogram[luminance]++;
                    }

                    // Calculate the cumulative distribution function, which will map each input pixel to a new value.
                    Span <int> cdf    = cdfBuffer.GetSpan();
                    int        cdfMin = this.CalculateCdf(cdf, histogram);

                    // Apply the cdf to each pixel of the image
                    float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin;
                    for (int i = 0; i < pixels.Length; i++)
                    {
                        TPixel sourcePixel = pixels[i];

                        int   luminance          = this.GetLuminance(sourcePixel, this.LuminanceLevels);
                        float luminanceEqualized = cdf[luminance] / numberOfPixelsMinusCdfMin;

                        pixels[i].FromVector4(new Vector4(luminanceEqualized));
                    }
                }
        }
Example #3
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            MemoryAllocator memoryAllocator = this.Configuration.MemoryAllocator;
            int             numberOfPixels  = source.Width * source.Height;
            var             workingRect     = new Rectangle(0, 0, source.Width, source.Height);

            using (IMemoryOwner <int> histogramBuffer = memoryAllocator.Allocate <int>(this.LuminanceLevels, AllocationOptions.Clean))
                using (IMemoryOwner <int> cdfBuffer = memoryAllocator.Allocate <int>(this.LuminanceLevels, AllocationOptions.Clean))
                {
                    // Build the histogram of the grayscale levels.
                    ParallelHelper.IterateRows(
                        workingRect,
                        this.Configuration,
                        rows =>
                    {
                        ref int histogramBase = ref MemoryMarshal.GetReference(histogramBuffer.GetSpan());
                        for (int y = rows.Min; y < rows.Max; y++)
                        {
                            ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y));

                            for (int x = 0; x < workingRect.Width; x++)
                            {
                                int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.LuminanceLevels);
                                Unsafe.Add(ref histogramBase, luminance)++;
                            }
                        }
                    });
Example #4
0
            /// <inheritdoc />
            internal override void Apply(Span <float> scanline, int x, int y)
            {
                MemoryAllocator memoryAllocator = this.Target.MemoryAllocator;

                using (IMemoryOwner <float> amountBuffer = memoryAllocator.Allocate <float>(scanline.Length))
                    using (IMemoryOwner <TPixel> overlay = memoryAllocator.Allocate <TPixel>(scanline.Length))
                    {
                        Span <float>  amountSpan  = amountBuffer.GetSpan();
                        Span <TPixel> overlaySpan = overlay.GetSpan();

                        for (int i = 0; i < scanline.Length; i++)
                        {
                            amountSpan[i] = scanline[i] * this.Options.BlendPercentage;

                            int offsetX = x + i;

                            // No doubt this one can be optimized further but I can't imagine its
                            // actually being used and can probably be removed/internalized for now
                            overlaySpan[i] = this[offsetX, y];
                        }

                        Span <TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
                        this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan);
                    }
            }
Example #5
0
        /// <summary>
        /// Bulk operation that copies the <paramref name="sourcePixels"/> to <paramref name="destinationPixels"/> in
        /// <typeparamref name="TSourcePixel"/> format.
        /// </summary>
        /// <typeparam name="TSourcePixel">The destination pixel type.</typeparam>
        /// <param name="configuration">A <see cref="Configuration"/> to configure internal operations.</param>
        /// <param name="sourcePixels">The <see cref="ReadOnlySpan{TSourcePixel}"/> to the source pixels.</param>
        /// <param name="destinationPixels">The <see cref="Span{TPixel}"/> to the destination pixels.</param>
        public virtual void From <TSourcePixel>(
            Configuration configuration,
            ReadOnlySpan <TSourcePixel> sourcePixels,
            Span <TPixel> destinationPixels)
            where TSourcePixel : struct, IPixel <TSourcePixel>
        {
            const int SliceLength    = 1024;
            int       numberOfSlices = sourcePixels.Length / SliceLength;

            using (IMemoryOwner <Vector4> tempVectors = configuration.MemoryAllocator.Allocate <Vector4>(SliceLength))
            {
                Span <Vector4> vectorSpan = tempVectors.GetSpan();
                for (int i = 0; i < numberOfSlices; i++)
                {
                    int start = i * SliceLength;
                    ReadOnlySpan <TSourcePixel> s = sourcePixels.Slice(start, SliceLength);
                    Span <TPixel> d = destinationPixels.Slice(start, SliceLength);
                    PixelOperations <TSourcePixel> .Instance.ToVector4(configuration, s, vectorSpan);

                    this.FromVector4Destructive(configuration, vectorSpan, d);
                }

                int endOfCompleteSlices = numberOfSlices * SliceLength;
                int remainder           = sourcePixels.Length - endOfCompleteSlices;
                if (remainder > 0)
                {
                    ReadOnlySpan <TSourcePixel> s = sourcePixels.Slice(endOfCompleteSlices);
                    Span <TPixel> d = destinationPixels.Slice(endOfCompleteSlices);
                    vectorSpan = vectorSpan.Slice(0, remainder);
                    PixelOperations <TSourcePixel> .Instance.ToVector4(configuration, s, vectorSpan);

                    this.FromVector4Destructive(configuration, vectorSpan, d);
                }
            }
        }
Example #6
0
            /// <inheritdoc />
            internal override void Apply(Span <float> scanline, int x, int y)
            {
                // Create a span for colors
                using (IMemoryOwner <float> amountBuffer = this.Target.MemoryAllocator.Allocate <float>(scanline.Length))
                    using (IMemoryOwner <TPixel> overlay = this.Target.MemoryAllocator.Allocate <TPixel>(scanline.Length))
                    {
                        Span <float>  amountSpan  = amountBuffer.GetSpan();
                        Span <TPixel> overlaySpan = overlay.GetSpan();

                        int           sourceY   = (y - this.offsetY) % this.yLength;
                        int           offsetX   = x - this.offsetX;
                        Span <TPixel> sourceRow = this.sourceFrame.GetPixelRowSpan(sourceY);

                        for (int i = 0; i < scanline.Length; i++)
                        {
                            amountSpan[i] = scanline[i] * this.Options.BlendPercentage;

                            int    sourceX = (i + offsetX) % this.xLength;
                            TPixel pixel   = sourceRow[sourceX];
                            overlaySpan[i] = pixel;
                        }

                        Span <TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
                        this.Blender.Blend(
                            this.sourceFrame.Configuration,
                            destinationRow,
                            destinationRow,
                            overlaySpan,
                            amountSpan);
                    }
            }
        public Size BulkVectorConvert()
        {
            using var image = new Image <Rgba32>(800, 800);
            using IMemoryOwner <float> amounts = Configuration.Default.MemoryAllocator.Allocate <float>(image.Width);
            amounts.GetSpan().Fill(1);

            Buffer2D <Rgba32> pixels = image.GetRootFramePixelBuffer();

            for (int y = 0; y < image.Height; y++)
            {
                Span <Rgba32> span = pixels.DangerousGetRowSpan(y);
                this.BulkVectorConvert(span, span, span, amounts.GetSpan());
            }

            return(new Size(image.Width, image.Height));
        }
Example #8
0
        /// <summary>
        /// Reads the gif comments.
        /// </summary>
        private void ReadComments()
        {
            int length;

            var stringBuilder = new StringBuilder();

            while ((length = this.stream.ReadByte()) != 0)
            {
                if (length > GifConstants.MaxCommentSubBlockLength)
                {
                    GifThrowHelper.ThrowInvalidImageContentException($"Gif comment length '{length}' exceeds max '{GifConstants.MaxCommentSubBlockLength}' of a comment data block");
                }

                if (this.IgnoreMetadata)
                {
                    this.stream.Seek(length, SeekOrigin.Current);
                    continue;
                }

                using IMemoryOwner <byte> commentsBuffer = this.MemoryAllocator.Allocate <byte>(length);
                Span <byte> commentsSpan = commentsBuffer.GetSpan();

                this.stream.Read(commentsSpan);
                string commentPart = GifConstants.Encoding.GetString(commentsSpan);
                stringBuilder.Append(commentPart);
            }

            if (stringBuilder.Length > 0)
            {
                this.gifMetadata.Comments.Add(stringBuilder.ToString());
            }
        }
Example #9
0
        /// <summary>
        /// Applies the opacity weighting for each pixel in a scanline to the target based on the pattern contained in the brush.
        /// </summary>
        /// <param name="scanline">The a collection of opacity values between 0 and 1 to be merged with the brushed color value before being applied to the target.</param>
        /// <param name="x">The x position in the target pixel space that the start of the scanline data corresponds to.</param>
        /// <param name="y">The y position in  the target pixel space that whole scanline corresponds to.</param>
        /// <remarks>scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs.</remarks>
        internal virtual void Apply(Span <float> scanline, int x, int y)
        {
            MemoryAllocator memoryAllocator = this.Target.MemoryAllocator;

            using (IMemoryOwner <float> amountBuffer = memoryAllocator.Allocate <float>(scanline.Length))
                using (IMemoryOwner <TPixel> overlay = memoryAllocator.Allocate <TPixel>(scanline.Length))
                {
                    Span <float>  amountSpan  = amountBuffer.GetSpan();
                    Span <TPixel> overlaySpan = overlay.GetSpan();

                    for (int i = 0; i < scanline.Length; i++)
                    {
                        if (this.Options.BlendPercentage < 1)
                        {
                            amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
                        }
                        else
                        {
                            amountSpan[i] = scanline[i];
                        }

                        overlaySpan[i] = this[x + i, y];
                    }

                    Span <TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
                    this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan);
                }
        }
Example #10
0
            /// <inheritdoc />
            internal override void Apply(Span <float> scanline, int x, int y)
            {
                Span <TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);

                MemoryAllocator memoryAllocator = this.Target.MemoryAllocator;

                if (this.Options.BlendPercentage == 1f)
                {
                    this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, this.Colors.GetSpan(), scanline);
                }
                else
                {
                    using (IMemoryOwner <float> amountBuffer = memoryAllocator.Allocate <float>(scanline.Length))
                    {
                        Span <float> amountSpan = amountBuffer.GetSpan();

                        for (int i = 0; i < scanline.Length; i++)
                        {
                            amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
                        }

                        this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, this.Colors.GetSpan(), amountSpan);
                    }
                }
            }
        protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor)
        {
            if (this.rowBuffer == null)
            {
                this.rowBuffer = this.MemoryAllocator.Allocate <byte>(this.BytesPerRow * height);
            }

            this.rowBuffer.Clear();

            Span <byte> outputRowSpan = this.rowBuffer.GetSpan().Slice(0, this.BytesPerRow * height);

            int width = this.Image.Width;

            using IMemoryOwner <TPixel> stripPixelBuffer = this.MemoryAllocator.Allocate <TPixel>(height * width);
            Span <TPixel> stripPixels       = stripPixelBuffer.GetSpan();
            int           lastRow           = y + height;
            int           stripPixelsRowIdx = 0;

            for (int row = y; row < lastRow; row++)
            {
                Span <TPixel> stripPixelsRow = this.Image.PixelBuffer.DangerousGetRowSpan(row);
                stripPixelsRow.CopyTo(stripPixels.Slice(stripPixelsRowIdx * width, width));
                stripPixelsRowIdx++;
            }

            this.EncodePixels(stripPixels, outputRowSpan);
            compressor.CompressStrip(outputRowSpan, height);
        }
        internal static unsafe Bitmap To32bppArgbSystemDrawingBitmap <TPixel>(Image <TPixel> image)
            where TPixel : struct, IPixel <TPixel>
        {
            int w = image.Width;
            int h = image.Height;

            var        resultBitmap = new Bitmap(w, h, PixelFormat.Format32bppArgb);
            var        fullRect     = new Rectangle(0, 0, w, h);
            BitmapData data         = resultBitmap.LockBits(fullRect, ImageLockMode.ReadWrite, resultBitmap.PixelFormat);
            byte *     destPtrBase  = (byte *)data.Scan0;

            long destRowByteCount   = data.Stride;
            long sourceRowByteCount = w * sizeof(Bgra32);

            using (IMemoryOwner <Bgra32> workBuffer = image.GetConfiguration().MemoryAllocator.Allocate <Bgra32>(w))
            {
                fixed(Bgra32 *sourcePtr = &workBuffer.GetReference())
                {
                    for (int y = 0; y < h; y++)
                    {
                        Span <TPixel> row = image.Frames.RootFrame.GetPixelRowSpan(y);
                        PixelOperations <TPixel> .Instance.ToBgra32(row, workBuffer.GetSpan(), row.Length);

                        byte *destPtr = destPtrBase + (data.Stride * y);

                        Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount);
                    }
                }
            }

            resultBitmap.UnlockBits(data);

            return(resultBitmap);
        }
Example #13
0
        public ReadOnlyMemory <TPixel> BuildPalette(ImageFrame <TPixel> source, Rectangle bounds)
        {
            using IMemoryOwner <Rgba32> buffer = this.Configuration.MemoryAllocator.Allocate <Rgba32>(bounds.Width);
            Span <Rgba32> bufferSpan           = buffer.GetSpan();

            // Loop through each row
            for (int y = bounds.Top; y < bounds.Bottom; y++)
            {
                Span <TPixel> row = source.GetPixelRowSpan(y).Slice(bounds.Left, bounds.Width);
                PixelOperations <TPixel> .Instance.ToRgba32(this.Configuration, row, bufferSpan);

                for (int x = 0; x < bufferSpan.Length; x++)
                {
                    Rgba32 rgba = bufferSpan[x];

                    // Add the color to the Octree
                    this.octree.AddColor(rgba);
                }
            }

            TPixel[] palette = this.octree.Palletize(this.colors);
            this.pixelMap = new EuclideanPixelMap <TPixel>(palette);

            return(palette);
        }
Example #14
0
        /// <summary>
        /// Reads a uncompressed TGA image where each pixels has 16 bit.
        /// </summary>
        /// <typeparam name="TPixel">The pixel type.</typeparam>
        /// <param name="width">The width of the image.</param>
        /// <param name="height">The height of the image.</param>
        /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
        /// <param name="origin">The image origin.</param>
        private void ReadBgra16 <TPixel>(int width, int height, Buffer2D <TPixel> pixels, TgaImageOrigin origin)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            TPixel color   = default;
            bool   invertX = InvertX(origin);

            using IMemoryOwner <byte> row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 2, 0);
            Span <byte> rowSpan           = row.GetSpan();

            for (int y = 0; y < height; y++)
            {
                int           newY      = InvertY(y, height, origin);
                Span <TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);

                if (invertX)
                {
                    for (int x = width - 1; x >= 0; x--)
                    {
                        this.currentStream.Read(this.scratchBuffer, 0, 2);
                        if (!this.hasAlpha)
                        {
                            this.scratchBuffer[1] |= 1 << 7;
                        }

                        if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite)
                        {
                            color.FromLa16(Unsafe.As <byte, La16>(ref this.scratchBuffer[0]));
                        }
                        else
                        {
                            color.FromBgra5551(Unsafe.As <byte, Bgra5551>(ref this.scratchBuffer[0]));
                        }

                        pixelSpan[x] = color;
                    }
                }
                else
                {
                    this.currentStream.Read(rowSpan);

                    if (!this.hasAlpha)
                    {
                        // We need to set the alpha component value to fully opaque.
                        for (int x = 1; x < rowSpan.Length; x += 2)
                        {
                            rowSpan[x] |= 1 << 7;
                        }
                    }

                    if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite)
                    {
                        PixelOperations <TPixel> .Instance.FromLa16Bytes(this.Configuration, rowSpan, pixelSpan, width);
                    }
                    else
                    {
                        PixelOperations <TPixel> .Instance.FromBgra5551Bytes(this.Configuration, rowSpan, pixelSpan, width);
                    }
                }
            }
        }
Example #15
0
        /// <inheritdoc/>
        public override void WriteByte(byte value)
        {
            this.EnsureNotDisposed();

            if (this.memoryChunk is null)
            {
                this.memoryChunk = this.AllocateMemoryChunk();
                this.writeChunk  = this.memoryChunk;
                this.writeOffset = 0;
            }

            IMemoryOwner <byte> chunkBuffer = this.writeChunk.Buffer;
            int chunkSize = this.writeChunk.Length;

            if (this.writeOffset == chunkSize)
            {
                // Allocate a new chunk if the current one is full
                this.writeChunk.Next = this.AllocateMemoryChunk();
                this.writeChunk      = this.writeChunk.Next;
                this.writeOffset     = 0;
                chunkBuffer          = this.writeChunk.Buffer;
            }

            chunkBuffer.GetSpan()[this.writeOffset++] = value;
        }
Example #16
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source)
        {
            TPixel vignetteColor = this.definition.VignetteColor.ToPixel <TPixel>();
            float  blendPercent  = this.definition.GraphicsOptions.BlendPercentage;

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

            Vector2 center       = Rectangle.Center(interest);
            float   finalRadiusX = this.definition.RadiusX.Calculate(interest.Size);
            float   finalRadiusY = this.definition.RadiusY.Calculate(interest.Size);

            float rX = finalRadiusX > 0
                ? MathF.Min(finalRadiusX, interest.Width * .5F)
                : interest.Width * .5F;

            float rY = finalRadiusY > 0
                ? MathF.Min(finalRadiusY, interest.Height * .5F)
                : interest.Height * .5F;

            float maxDistance = MathF.Sqrt((rX * rX) + (rY * rY));

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

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

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

            ParallelRowIterator.IterateRows <RowOperation, float>(
                configuration,
                interest,
                in operation);
        }
Example #17
0
        public void AddPaletteColors(Buffer2DRegion <TPixel> pixelRegion)
        {
            Rectangle         bounds = pixelRegion.Rectangle;
            Buffer2D <TPixel> source = pixelRegion.Buffer;

            using IMemoryOwner <Rgba32> buffer = this.Configuration.MemoryAllocator.Allocate <Rgba32>(bounds.Width);
            Span <Rgba32> bufferSpan           = buffer.GetSpan();

            // Loop through each row
            for (int y = bounds.Top; y < bounds.Bottom; y++)
            {
                Span <TPixel> row = source.GetRowSpan(y).Slice(bounds.Left, bounds.Width);
                PixelOperations <TPixel> .Instance.ToRgba32(this.Configuration, row, bufferSpan);

                for (int x = 0; x < bufferSpan.Length; x++)
                {
                    Rgba32 rgba = bufferSpan[x];

                    // Add the color to the Octree
                    this.octree.AddColor(rgba);
                }
            }

            Span <TPixel> paletteSpan  = this.paletteOwner.GetSpan();
            int           paletteIndex = 0;

            this.octree.Palletize(paletteSpan, this.maxColors, ref paletteIndex);

            // Length of reduced palette + transparency.
            ReadOnlyMemory <TPixel> result = this.paletteOwner.Memory.Slice(0, Math.Min(paletteIndex + 2, QuantizerConstants.MaxColors));

            this.pixelMap = new EuclideanPixelMap <TPixel>(this.Configuration, result);

            this.palette = result;
        }
Example #18
0
        /// <summary>
        /// Reads an individual gif frame.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="image">The image to decode the information to.</param>
        /// <param name="previousFrame">The previous frame.</param>
        private void ReadFrame <TPixel>(ref Image <TPixel> image, ref ImageFrame <TPixel> previousFrame)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            this.ReadImageDescriptor();

            IMemoryOwner <byte> localColorTable = null;
            Buffer2D <byte>     indices         = null;

            try
            {
                // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table.
                if (this.imageDescriptor.LocalColorTableFlag)
                {
                    int length = this.imageDescriptor.LocalColorTableSize * 3;
                    localColorTable = this.Configuration.MemoryAllocator.Allocate <byte>(length, AllocationOptions.Clean);
                    this.stream.Read(localColorTable.GetSpan());
                }

                indices = this.Configuration.MemoryAllocator.Allocate2D <byte>(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean);

                this.ReadFrameIndices(indices);
                ReadOnlySpan <Rgb24> colorTable = MemoryMarshal.Cast <byte, Rgb24>((localColorTable ?? this.globalColorTable).GetSpan());
                this.ReadFrameColors(ref image, ref previousFrame, indices, colorTable, this.imageDescriptor);

                // Skip any remaining blocks
                this.SkipBlock();
            }
            finally
            {
                localColorTable?.Dispose();
                indices?.Dispose();
            }
        }
Example #19
0
 private void TestSpanPropertyIsAlwaysTheSame <T>(int desiredLength, bool testManagedByteBuffer = false)
     where T : struct
 {
     using (IMemoryOwner <T> buffer = this.Allocate <T>(desiredLength, AllocationOptions.None, testManagedByteBuffer))
     {
         ref T a = ref MemoryMarshal.GetReference(buffer.GetSpan());
         ref T b = ref MemoryMarshal.GetReference(buffer.GetSpan());
Example #20
0
        /// <summary>
        /// Initializes a new instance of the <see cref="HuffmanTable"/> struct.
        /// </summary>
        /// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations.</param>
        /// <param name="codeLengths">The code lengths</param>
        /// <param name="values">The huffman values</param>
        public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan <byte> codeLengths, ReadOnlySpan <byte> values)
        {
            const int Length = 257;

            using (IMemoryOwner <short> huffcode = memoryAllocator.Allocate <short>(Length))
            {
                ref short huffcodeRef    = ref MemoryMarshal.GetReference(huffcode.GetSpan());
                ref byte  codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths);
Example #21
0
 private void TestHasCorrectLength <T>(int desiredLength)
     where T : struct
 {
     using (IMemoryOwner <T> buffer = this.MemoryAllocator.Allocate <T>(desiredLength))
     {
         Assert.Equal(desiredLength, buffer.GetSpan().Length);
     }
 }
Example #22
0
        /// <summary>
        /// Reads a run length encoded TGA image.
        /// </summary>
        /// <typeparam name="TPixel">The pixel type.</typeparam>
        /// <param name="width">The width of the image.</param>
        /// <param name="height">The height of the image.</param>
        /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
        /// <param name="bytesPerPixel">The bytes per pixel.</param>
        /// <param name="inverted">Indicates, if the origin of the image is top left rather the bottom left (the default).</param>
        private void ReadRle <TPixel>(int width, int height, Buffer2D <TPixel> pixels, int bytesPerPixel, bool inverted)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            TPixel color     = default;
            var    alphaBits = this.tgaMetadata.AlphaChannelBits;

            using (IMemoryOwner <byte> buffer = this.memoryAllocator.Allocate <byte>(width * height * bytesPerPixel, AllocationOptions.Clean))
            {
                Span <byte> bufferSpan = buffer.GetSpan();
                this.UncompressRle(width, height, bufferSpan, bytesPerPixel);
                for (int y = 0; y < height; y++)
                {
                    int           newY        = Invert(y, height, inverted);
                    Span <TPixel> pixelRow    = pixels.GetRowSpan(newY);
                    int           rowStartIdx = y * width * bytesPerPixel;
                    for (int x = 0; x < width; x++)
                    {
                        int idx = rowStartIdx + (x * bytesPerPixel);
                        switch (bytesPerPixel)
                        {
                        case 1:
                            color.FromL8(Unsafe.As <byte, L8>(ref bufferSpan[idx]));
                            break;

                        case 2:
                            if (!this.hasAlpha)
                            {
                                // Set alpha value to 1, to treat it as opaque for Bgra5551.
                                bufferSpan[idx + 1] = (byte)(bufferSpan[idx + 1] | 128);
                            }

                            color.FromBgra5551(Unsafe.As <byte, Bgra5551>(ref bufferSpan[idx]));
                            break;

                        case 3:
                            color.FromBgr24(Unsafe.As <byte, Bgr24>(ref bufferSpan[idx]));
                            break;

                        case 4:
                            if (this.hasAlpha)
                            {
                                color.FromBgra32(Unsafe.As <byte, Bgra32>(ref bufferSpan[idx]));
                            }
                            else
                            {
                                var alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3];
                                color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], (byte)alpha));
                            }

                            break;
                        }

                        pixelRow[x] = color;
                    }
                }
            }
        }
        /// <inheritdoc/>
        public void AddPaletteColors(Buffer2DRegion <TPixel> pixelRegion)
        {
            Rectangle         bounds = pixelRegion.Rectangle;
            Buffer2D <TPixel> source = pixelRegion.Buffer;

            using (IMemoryOwner <Rgba32> buffer = this.Configuration.MemoryAllocator.Allocate <Rgba32>(bounds.Width))
            {
                Span <Rgba32> bufferSpan = buffer.GetSpan();

                // Loop through each row
                for (int y = bounds.Top; y < bounds.Bottom; y++)
                {
                    Span <TPixel> row = source.DangerousGetRowSpan(y).Slice(bounds.Left, bounds.Width);
                    PixelOperations <TPixel> .Instance.ToRgba32(this.Configuration, row, bufferSpan);

                    for (int x = 0; x < bufferSpan.Length; x++)
                    {
                        Rgba32 rgba = bufferSpan[x];

                        // Add the color to the Octree
                        this.octree.AddColor(rgba);
                    }
                }
            }

            int           paletteIndex = 0;
            Span <TPixel> paletteSpan  = this.paletteOwner.GetSpan();

            // On very rare occasions, (blur.png), the quantizer does not preserve a
            // transparent entry when palletizing the captured colors.
            // To workaround this we ensure the palette ends with the default color
            // for higher bit depths. Lower bit depths will correctly reduce the palette.
            // TODO: Investigate more evenly reduced palette reduction.
            int max = this.maxColors;

            if (this.bitDepth == 8)
            {
                max--;
            }

            this.octree.Palletize(paletteSpan, max, ref paletteIndex);
            ReadOnlyMemory <TPixel> result = this.paletteOwner.Memory.Slice(0, paletteSpan.Length);

            // When called multiple times by QuantizerUtilities.BuildPalette
            // this prevents memory churn caused by reallocation.
            if (this.pixelMap is null)
            {
                this.pixelMap = new EuclideanPixelMap <TPixel>(this.Configuration, result);
            }
            else
            {
                this.pixelMap.Clear(result);
            }

            this.palette = result;
        }
Example #24
0
        /// <summary>
        /// Initializes a new instance of the <see cref="HuffmanTable"/> struct.
        /// </summary>
        /// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations.</param>
        /// <param name="codeLengths">The code lengths</param>
        /// <param name="values">The huffman values</param>
        public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan <byte> codeLengths, ReadOnlySpan <byte> values)
        {
            // We do some bounds checks in the code here to protect against AccessViolationExceptions
            const int HuffCodeLength = 257;
            const int MaxSizeLength  = HuffCodeLength - 1;

            using (IMemoryOwner <short> huffcode = memoryAllocator.Allocate <short>(HuffCodeLength))
            {
                ref short huffcodeRef    = ref MemoryMarshal.GetReference(huffcode.GetSpan());
                ref byte  codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths);
Example #25
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            int startY = sourceRectangle.Y;
            int endY   = sourceRectangle.Bottom;
            int startX = sourceRectangle.X;
            int endX   = sourceRectangle.Right;

            // Align start/end positions.
            int minX = Math.Max(0, startX);
            int maxX = Math.Min(source.Width, endX);
            int minY = Math.Max(0, startY);
            int maxY = Math.Min(source.Height, endY);

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

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

            int width = maxX - minX;

            using (IMemoryOwner <TPixel> colors = source.MemoryAllocator.Allocate <TPixel>(width))
                using (IMemoryOwner <float> amount = source.MemoryAllocator.Allocate <float>(width))
                {
                    // Be careful! Do not capture colorSpan & amountSpan in the lambda below!
                    Span <TPixel> colorSpan  = colors.GetSpan();
                    Span <float>  amountSpan = amount.GetSpan();

                    // TODO: Use Span.Fill?
                    for (int i = 0; i < width; i++)
                    {
                        colorSpan[i]  = this.Color;
                        amountSpan[i] = this.GraphicsOptions.BlendPercentage;
                    }

                    PixelBlender <TPixel> blender = PixelOperations <TPixel> .Instance.GetPixelBlender(this.GraphicsOptions);

                    ParallelFor.WithConfiguration(
                        minY,
                        maxY,
                        configuration,
                        y =>
                    {
                        Span <TPixel> destination = source.GetPixelRowSpan(y - startY).Slice(minX - startX, width);

                        // This switched color & destination in the 2nd and 3rd places because we are applying the target color under the current one
                        blender.Blend(source.MemoryAllocator, destination, colors.GetSpan(), destination, amount.GetSpan());
                    });
                }
        }
Example #26
0
            /// <inheritdoc/>
            protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
            {
                int     startY      = sourceRectangle.Y;
                int     endY        = sourceRectangle.Bottom;
                int     startX      = sourceRectangle.X;
                int     endX        = sourceRectangle.Right;
                TPixel  glowColor   = this.GlowColor;
                Vector2 centre      = Rectangle.Center(sourceRectangle);
                float   maxDistance = this.Radius > 0 ? Math.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F;

                // 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 (IMemoryOwner <TPixel> rowColors = Configuration.Default.MemoryAllocator.Allocate <TPixel>(width))
                {
                    Buffer2D <TPixel> sourcePixels = source.PixelBuffer;
                    rowColors.GetSpan().Fill(glowColor);

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

                        for (int x = minX; x < maxX; x++)
                        {
                            int offsetX         = x - startX;
                            float distance      = Vector2.Distance(centre, new Vector2(offsetX, offsetY));
                            Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4();
                            TPixel packed       = default(TPixel);
                            packed.PackFromVector4(PremultipliedLerp(sourceColor, glowColor.ToVector4(), 1 - (.95F * (distance / maxDistance))));
                            sourcePixels[offsetX, offsetY] = packed;
                        }
                    });
                }
            }
        /// <summary>
        /// Apply an image integral. <See href="https://en.wikipedia.org/wiki/Summed-area_table"/>
        /// </summary>
        /// <param name="source">The image on which to apply the integral.</param>
        /// <typeparam name="TPixel">The type of the pixel.</typeparam>
        /// <returns>The <see cref="Buffer2D{T}"/> containing all the sums.</returns>
        public static Buffer2D <ulong> CalculateIntegralImage <TPixel>(this Image <TPixel> source)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            Configuration configuration = source.GetConfiguration();

            int endY = source.Height;
            int endX = source.Width;

            Buffer2D <ulong>  intImage     = configuration.MemoryAllocator.Allocate2D <ulong>(source.Width, source.Height);
            ulong             sumX0        = 0;
            Buffer2D <TPixel> sourceBuffer = source.Frames.RootFrame.PixelBuffer;

            using (IMemoryOwner <L8> tempRow = configuration.MemoryAllocator.Allocate <L8>(source.Width))
            {
                Span <L8>     tempSpan  = tempRow.GetSpan();
                Span <TPixel> sourceRow = sourceBuffer.DangerousGetRowSpan(0);
                Span <ulong>  destRow   = intImage.DangerousGetRowSpan(0);

                PixelOperations <TPixel> .Instance.ToL8(configuration, sourceRow, tempSpan);

                // First row
                for (int x = 0; x < endX; x++)
                {
                    sumX0     += tempSpan[x].PackedValue;
                    destRow[x] = sumX0;
                }

                Span <ulong> previousDestRow = destRow;

                // All other rows
                for (int y = 1; y < endY; y++)
                {
                    sourceRow = sourceBuffer.DangerousGetRowSpan(y);
                    destRow   = intImage.DangerousGetRowSpan(y);

                    PixelOperations <TPixel> .Instance.ToL8(configuration, sourceRow, tempSpan);

                    // Process first column
                    sumX0      = tempSpan[0].PackedValue;
                    destRow[0] = sumX0 + previousDestRow[0];

                    // Process all other colmns
                    for (int x = 1; x < endX; x++)
                    {
                        sumX0     += tempSpan[x].PackedValue;
                        destRow[x] = sumX0 + previousDestRow[x];
                    }

                    previousDestRow = destRow;
                }
            }

            return(intImage);
        }
Example #28
0
        /// <summary>
        /// Reads the transformations, if any are present.
        /// </summary>
        /// <param name="xSize">The width of the image.</param>
        /// <param name="ySize">The height of the image.</param>
        /// <param name="decoder">Vp8LDecoder where the transformations will be stored.</param>
        private void ReadTransformation(int xSize, int ySize, Vp8LDecoder decoder)
        {
            var transformType = (Vp8LTransformType)this.bitReader.ReadValue(2);
            var transform     = new Vp8LTransform(transformType, xSize, ySize);

            // Each transform is allowed to be used only once.
            foreach (Vp8LTransform decoderTransform in decoder.Transforms)
            {
                if (decoderTransform.TransformType == transform.TransformType)
                {
                    WebpThrowHelper.ThrowImageFormatException("Each transform can only be present once");
                }
            }

            switch (transformType)
            {
            case Vp8LTransformType.SubtractGreen:
                // There is no data associated with this transform.
                break;

            case Vp8LTransformType.ColorIndexingTransform:
                // The transform data contains color table size and the entries in the color table.
                // 8 bit value for color table size.
                uint numColors = this.bitReader.ReadValue(8) + 1;
                int  bits      = numColors > 16 ? 0
                                     : numColors > 4 ? 1
                                     : numColors > 2 ? 2
                                     : 3;
                transform.Bits = bits;
                using (IMemoryOwner <uint> colorMap = this.DecodeImageStream(decoder, (int)numColors, 1, false))
                {
                    int finalNumColors = 1 << (8 >> transform.Bits);
                    IMemoryOwner <uint> newColorMap = this.memoryAllocator.Allocate <uint>(finalNumColors, AllocationOptions.Clean);
                    LosslessUtils.ExpandColorMap((int)numColors, colorMap.GetSpan(), newColorMap.GetSpan());
                    transform.Data = newColorMap;
                }

                break;

            case Vp8LTransformType.PredictorTransform:
            case Vp8LTransformType.CrossColorTransform:
            {
                // The first 3 bits of prediction data define the block width and height in number of bits.
                transform.Bits = (int)this.bitReader.ReadValue(3) + 2;
                int blockWidth  = LosslessUtils.SubSampleSize(transform.XSize, transform.Bits);
                int blockHeight = LosslessUtils.SubSampleSize(transform.YSize, transform.Bits);
                IMemoryOwner <uint> transformData = this.DecodeImageStream(decoder, blockWidth, blockHeight, false);
                transform.Data = transformData;
                break;
            }
            }

            decoder.Transforms.Add(transform);
        }
Example #29
0
        /// <summary>
        /// Returns an image from the given System.Drawing bitmap.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="bmp">The input bitmap.</param>
        /// <exception cref="ArgumentException">Thrown if the image pixel format is not of type <see cref="PixelFormat.Format32bppArgb"/></exception>
        internal static unsafe Image <TPixel> From32bppArgbSystemDrawingBitmap <TPixel>(Bitmap bmp)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            int w = bmp.Width;
            int h = bmp.Height;

            var fullRect = new System.Drawing.Rectangle(0, 0, w, h);

            if (bmp.PixelFormat != PixelFormat.Format32bppArgb)
            {
                throw new ArgumentException(
                          $"{nameof(From32bppArgbSystemDrawingBitmap)} : pixel format should be {PixelFormat.Format32bppArgb}!",
                          nameof(bmp));
            }

            BitmapData data  = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat);
            var        image = new Image <TPixel>(w, h);

            try
            {
                byte *sourcePtrBase = (byte *)data.Scan0;

                long sourceRowByteCount = data.Stride;
                long destRowByteCount   = w * sizeof(Bgra32);

                Configuration configuration = image.GetConfiguration();

                using (IMemoryOwner <Bgra32> workBuffer = Configuration.Default.MemoryAllocator.Allocate <Bgra32>(w))
                {
                    fixed(Bgra32 *destPtr = &workBuffer.GetReference())
                    {
                        for (int y = 0; y < h; y++)
                        {
                            Span <TPixel> row = image.Frames.RootFrame.GetPixelRowSpan(y);

                            byte *sourcePtr = sourcePtrBase + (data.Stride * y);

                            Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount);
                            PixelOperations <TPixel> .Instance.FromBgra32(
                                configuration,
                                workBuffer.GetSpan().Slice(0, w),
                                row);
                        }
                    }
                }
            }
            finally
            {
                bmp.UnlockBits(data);
            }

            return(image);
        }
Example #30
0
        /// <summary>
        /// Write remainder of this stream to another stream.
        /// </summary>
        /// <param name="stream">The stream to write to.</param>
        public void WriteTo(Stream stream)
        {
            this.EnsureNotDisposed();

            Guard.NotNull(stream, nameof(stream));

            if (this.readChunk is null)
            {
                if (this.memoryChunk is null)
                {
                    return;
                }

                this.readChunk  = this.memoryChunk;
                this.readOffset = 0;
            }

            IMemoryOwner <byte> chunkBuffer = this.readChunk.Buffer;
            int chunkSize = this.readChunk.Length;

            if (this.readChunk.Next is null)
            {
                chunkSize = this.writeOffset;
            }

            // Following code mirrors Read() logic (readChunk/readOffset should
            // point just past last byte of last chunk when done)
            // loop until end of chunks is found
            while (true)
            {
                if (this.readOffset == chunkSize)
                {
                    // Exit if no more chunks are currently available
                    if (this.readChunk.Next is null)
                    {
                        break;
                    }

                    this.readChunk  = this.readChunk.Next;
                    this.readOffset = 0;
                    chunkBuffer     = this.readChunk.Buffer;
                    chunkSize       = this.readChunk.Length;
                    if (this.readChunk.Next is null)
                    {
                        chunkSize = this.writeOffset;
                    }
                }

                int writeCount = chunkSize - this.readOffset;
                stream.Write(chunkBuffer.GetSpan(), this.readOffset, writeCount);
                this.readOffset = chunkSize;
            }
        }