Ejemplo n.º 1
0
        /// <summary>
        /// Reads the 32 bit color palette from the stream, checking the alpha component of each pixel.
        /// This is a special case only used for 32bpp WinBMPv3 files, which could be in either BGR0 or BGRA format.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param>
        /// <param name="width">The width of the bitmap.</param>
        /// <param name="height">The height of the bitmap.</param>
        /// <param name="inverted">Whether the bitmap is inverted.</param>
        private void ReadRgb32Slow <TPixel>(Buffer2D <TPixel> pixels, int width, int height, bool inverted)
            where TPixel : struct, IPixel <TPixel>
        {
            int padding = CalculatePadding(width, 4);

            using (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, padding))
                using (IMemoryOwner <Bgra32> bgraRow = this.memoryAllocator.Allocate <Bgra32>(width))
                {
                    Span <Bgra32> bgraRowSpan     = bgraRow.GetSpan();
                    long          currentPosition = this.stream.Position;
                    bool          hasAlpha        = false;

                    // Loop though the rows checking each pixel. We start by assuming it's
                    // an BGR0 image. If we hit a non-zero alpha value, then we know it's
                    // actually a BGRA image, and change tactics accordingly.
                    for (int y = 0; y < height; y++)
                    {
                        this.stream.Read(row);

                        PixelOperations <Bgra32> .Instance.FromBgra32Bytes(
                            this.configuration,
                            row.GetSpan(),
                            bgraRowSpan,
                            width);

                        // Check each pixel in the row to see if it has an alpha value.
                        for (int x = 0; x < width; x++)
                        {
                            Bgra32 bgra = bgraRowSpan[x];
                            if (bgra.A > 0)
                            {
                                hasAlpha = true;
                                break;
                            }
                        }

                        if (hasAlpha)
                        {
                            break;
                        }
                    }

                    // Reset our stream for a second pass.
                    this.stream.Position = currentPosition;

                    // Process the pixels in bulk taking the raw alpha component value.
                    if (hasAlpha)
                    {
                        for (int y = 0; y < height; y++)
                        {
                            this.stream.Read(row);

                            int           newY      = Invert(y, height, inverted);
                            Span <TPixel> pixelSpan = pixels.GetRowSpan(newY);

                            PixelOperations <TPixel> .Instance.FromBgra32Bytes(
                                this.configuration,
                                row.GetSpan(),
                                pixelSpan,
                                width);
                        }

                        return;
                    }

                    // Slow path. We need to set each alpha component value to fully opaque.
                    for (int y = 0; y < height; y++)
                    {
                        this.stream.Read(row);
                        PixelOperations <Bgra32> .Instance.FromBgra32Bytes(
                            this.configuration,
                            row.GetSpan(),
                            bgraRowSpan,
                            width);

                        int           newY      = Invert(y, height, inverted);
                        Span <TPixel> pixelSpan = pixels.GetRowSpan(newY);

                        for (int x = 0; x < width; x++)
                        {
                            Bgra32 bgra = bgraRowSpan[x];
                            bgra.A = byte.MaxValue;
                            ref TPixel pixel = ref pixelSpan[x];
                            pixel.FromBgra32(bgra);
                        }
                    }
                }
Ejemplo n.º 2
0
 /// <summary>
 /// Initializes a new instance of the <see cref="WeightsBuffer"/> class.
 /// </summary>
 /// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for allocations.</param>
 /// <param name="sourceSize">The size of the source window</param>
 /// <param name="destinationSize">The size of the destination window</param>
 public WeightsBuffer(MemoryAllocator memoryAllocator, int sourceSize, int destinationSize)
 {
     this.dataBuffer = memoryAllocator.Allocate2D <float>(sourceSize, destinationSize, true);
     this.Weights    = new WeightsWindow[destinationSize];
 }
Ejemplo n.º 3
0
        /// <inheritdoc/>
        protected override void OnApply(
            ImageFrame <TPixel> source,
            ImageFrame <TPixel> destination,
            Rectangle sourceRectangle,
            Configuration configuration)
        {
            int height = this.TargetDimensions.Height;
            int width  = this.TargetDimensions.Width;

            Rectangle sourceBounds = source.Bounds();
            var       targetBounds = new Rectangle(0, 0, width, height);

            // Since could potentially be resizing the canvas we might need to re-calculate the matrix
            Matrix3x2 matrix = this.GetProcessingMatrix(sourceBounds, targetBounds);

            // Convert from screen to world space.
            Matrix3x2.Invert(matrix, out matrix);

            if (this.Sampler is NearestNeighborResampler)
            {
                Parallel.For(
                    0,
                    height,
                    configuration.ParallelOptions,
                    y =>
                {
                    Span <TPixel> destRow = destination.GetPixelRowSpan(y);

                    for (int x = 0; x < width; x++)
                    {
                        var point = Point.Transform(new Point(x, y), matrix);
                        if (sourceBounds.Contains(point.X, point.Y))
                        {
                            destRow[x] = source[point.X, point.Y];
                        }
                    }
                });

                return;
            }

            int maxSourceX = source.Width - 1;
            int maxSourceY = source.Height - 1;

            (float radius, float scale, float ratio)xRadiusScale = this.GetSamplingRadius(source.Width, destination.Width);
            (float radius, float scale, float ratio)yRadiusScale = this.GetSamplingRadius(source.Height, destination.Height);
            float      xScale    = xRadiusScale.scale;
            float      yScale    = yRadiusScale.scale;
            var        radius    = new Vector2(xRadiusScale.radius, yRadiusScale.radius);
            IResampler sampler   = this.Sampler;
            var        maxSource = new Vector4(maxSourceX, maxSourceY, maxSourceX, maxSourceY);
            int        xLength   = (int)MathF.Ceiling((radius.X * 2) + 2);
            int        yLength   = (int)MathF.Ceiling((radius.Y * 2) + 2);

            MemoryManager memoryManager = configuration.MemoryManager;

            using (Buffer2D <float> yBuffer = memoryManager.Allocate2D <float>(yLength, height))
                using (Buffer2D <float> xBuffer = memoryManager.Allocate2D <float>(xLength, height))
                {
                    Parallel.For(
                        0,
                        height,
                        configuration.ParallelOptions,
                        y =>
                    {
                        Span <TPixel> destRow = destination.GetPixelRowSpan(y);
                        Span <float> ySpan    = yBuffer.GetRowSpan(y);
                        Span <float> xSpan    = xBuffer.GetRowSpan(y);

                        for (int x = 0; x < width; x++)
                        {
                            // Use the single precision position to calculate correct bounding pixels
                            // otherwise we get rogue pixels outside of the bounds.
                            var point = Vector2.Transform(new Vector2(x, y), matrix);

                            // Clamp sampling pixel radial extents to the source image edges
                            Vector2 maxXY = point + radius;
                            Vector2 minXY = point - radius;

                            // max, maxY, minX, minY
                            var extents = new Vector4(
                                MathF.Floor(maxXY.X + .5F),
                                MathF.Floor(maxXY.Y + .5F),
                                MathF.Ceiling(minXY.X - .5F),
                                MathF.Ceiling(minXY.Y - .5F));

                            int right  = (int)extents.X;
                            int bottom = (int)extents.Y;
                            int left   = (int)extents.Z;
                            int top    = (int)extents.W;

                            extents = Vector4.Clamp(extents, Vector4.Zero, maxSource);

                            int maxX = (int)extents.X;
                            int maxY = (int)extents.Y;
                            int minX = (int)extents.Z;
                            int minY = (int)extents.W;

                            if (minX == maxX || minY == maxY)
                            {
                                continue;
                            }

                            // It appears these have to be calculated on-the-fly.
                            // Precalulating transformed weights would require prior knowledge of every transformed pixel location
                            // since they can be at sub-pixel positions on both axis.
                            // I've optimized where I can but am always open to suggestions.
                            if (yScale > 1 && xScale > 1)
                            {
                                CalculateWeightsDown(top, bottom, minY, maxY, point.Y, sampler, yScale, ySpan);
                                CalculateWeightsDown(left, right, minX, maxX, point.X, sampler, xScale, xSpan);
                            }
                            else
                            {
                                CalculateWeightsScaleUp(minY, maxY, point.Y, sampler, ySpan);
                                CalculateWeightsScaleUp(minX, maxX, point.X, sampler, xSpan);
                            }

                            // Now multiply the results against the offsets
                            Vector4 sum = Vector4.Zero;
                            for (int yy = 0, j = minY; j <= maxY; j++, yy++)
                            {
                                float yWeight = ySpan[yy];

                                for (int xx = 0, i = minX; i <= maxX; i++, xx++)
                                {
                                    float xWeight = xSpan[xx];
                                    var vector    = source[i, j].ToVector4();

                                    // Values are first premultiplied to prevent darkening of edge pixels
                                    Vector4 multiplied = vector.Premultiply();
                                    sum += multiplied * xWeight * yWeight;
                                }
                            }

                            ref TPixel dest = ref destRow[x];

                            // Reverse the premultiplication
                            dest.PackFromVector4(sum.UnPremultiply());
                        }
                    });
                }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Decodes the image from the specified this._stream and sets
        /// the data to image.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="stream">The stream, where the image should be
        /// decoded from. Cannot be null (Nothing in Visual Basic).</param>
        /// <exception cref="System.ArgumentNullException">
        ///    <para><paramref name="stream"/> is null.</para>
        /// </exception>
        /// <returns>The decoded image.</returns>
        public Image <TPixel> Decode <TPixel>(Stream stream)
            where TPixel : struct, IPixel <TPixel>
        {
            try
            {
                int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);

                var image = new Image <TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metaData);

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

                switch (this.infoHeader.Compression)
                {
                case BmpCompression.RGB:
                    if (this.infoHeader.BitsPerPixel == 32)
                    {
                        if (this.bmpMetaData.InfoHeaderType == BmpInfoHeaderType.WinVersion3)
                        {
                            this.ReadRgb32Slow(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
                        }
                        else
                        {
                            this.ReadRgb32Fast(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
                        }
                    }
                    else if (this.infoHeader.BitsPerPixel == 24)
                    {
                        this.ReadRgb24(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
                    }
                    else if (this.infoHeader.BitsPerPixel == 16)
                    {
                        this.ReadRgb16(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted);
                    }
                    else if (this.infoHeader.BitsPerPixel <= 8)
                    {
                        this.ReadRgbPalette(
                            pixels,
                            palette,
                            this.infoHeader.Width,
                            this.infoHeader.Height,
                            this.infoHeader.BitsPerPixel,
                            bytesPerColorMapEntry,
                            inverted);
                    }

                    break;

                case BmpCompression.RLE8:
                case BmpCompression.RLE4:
                    this.ReadRle(this.infoHeader.Compression, pixels, palette, this.infoHeader.Width, this.infoHeader.Height, inverted);

                    break;

                case BmpCompression.BitFields:
                    this.ReadBitFields(pixels, inverted);

                    break;

                default:
                    BmpThrowHelper.ThrowNotSupportedException("Does not support this kind of bitmap files.");

                    break;
                }

                return(image);
            }
            catch (IndexOutOfRangeException e)
            {
                throw new ImageFormatException("Bitmap does not have a valid format.", e);
            }
        }
Ejemplo n.º 5
0
 /// <summary>
 /// Gets the span to the backing buffer at the given row.
 /// </summary>
 /// <typeparam name="TPixel">The type of the pixel.</typeparam>
 /// <param name="source">The source.</param>
 /// <param name="row">The row.</param>
 /// <returns>
 /// The span retuned from Pixel source
 /// </returns>
 private static Span <TPixel> GetSpan <TPixel>(Buffer2D <TPixel> source, int row)
     where TPixel : struct, IPixel <TPixel>
 => source.GetSpan().Slice(row * source.Width, source.Width);
Ejemplo n.º 6
0
        /// <inheritdoc />
        protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration)
        {
            DenseMatrix <float>[] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast };

            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);

            // we need a clean copy for each pass to start from
            using (ImageFrame <TPixel> cleanCopy = source.Clone())
            {
                new ConvolutionProcessor <TPixel>(kernels[0]).Apply(source, sourceRectangle, configuration);

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

                int shiftY = startY;
                int shiftX = startX;

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

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

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

                // Additional runs.
                // ReSharper disable once ForCanBeConvertedToForeach
                for (int i = 1; i < kernels.Length; i++)
                {
                    using (ImageFrame <TPixel> pass = cleanCopy.Clone())
                    {
                        new ConvolutionProcessor <TPixel>(kernels[i]).Apply(pass, sourceRectangle, configuration);

                        Buffer2D <TPixel> passPixels   = pass.PixelBuffer;
                        Buffer2D <TPixel> targetPixels = source.PixelBuffer;

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

                                ref TPixel passPixelsBase =
                                    ref MemoryMarshal.GetReference(passPixels.GetRowSpan(offsetY));
                                ref TPixel targetPixelsBase =
                                    ref MemoryMarshal.GetReference(targetPixels.GetRowSpan(offsetY));

                                for (int x = minX; x < maxX; x++)
                                {
                                    int offsetX = x - shiftX;

                                    // Grab the max components of the two pixels
                                    ref TPixel currentPassPixel   = ref Unsafe.Add(ref passPixelsBase, offsetX);
                                    ref TPixel currentTargetPixel =
                                        ref Unsafe.Add(ref targetPixelsBase, offsetX);

                                    var pixelValue = Vector4.Max(
                                        currentPassPixel.ToVector4(),
                                        currentTargetPixel.ToVector4());

                                    currentTargetPixel.PackFromVector4(pixelValue);
                                }
                            }
                        });
Ejemplo n.º 7
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="origin">The image origin.</param>
        private void ReadRle <TPixel>(int width, int height, Buffer2D <TPixel> pixels, int bytesPerPixel, TgaImageOrigin origin)
            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        = InvertY(y, height, origin);
                    Span <TPixel> pixelRow    = pixels.DangerousGetRowSpan(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);
                            }

                            if (this.fileHeader.ImageType == TgaImageType.RleBlackAndWhite)
                            {
                                color.FromLa16(Unsafe.As <byte, La16>(ref bufferSpan[idx]));
                            }
                            else
                            {
                                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;
                        }

                        int newX = InvertX(x, width, origin);
                        pixelRow[newX] = color;
                    }
                }
            }
        }
Ejemplo n.º 8
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 (IManagedByteBuffer row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 2, 0))
            {
                for (int y = 0; y < height; y++)
                {
                    int           newY      = InvertY(y, height, origin);
                    Span <TPixel> pixelSpan = pixels.GetRowSpan(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(row);
                        Span <byte> rowSpan = row.GetSpan();

                        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);
                        }
                    }
                }
            }
        }
Ejemplo n.º 9
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source, ImageFrame <TPixel> destination, Rectangle sourceRectangle, Configuration configuration)
        {
            // Handle resize dimensions identical to the original
            if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.ResizeRectangle)
            {
                // The cloned will be blank here copy all the pixel data over
                source.GetPixelSpan().CopyTo(destination.GetPixelSpan());
                return;
            }

            int width   = this.Width;
            int height  = this.Height;
            int sourceX = sourceRectangle.X;
            int sourceY = sourceRectangle.Y;
            int startY  = this.ResizeRectangle.Y;
            int endY    = this.ResizeRectangle.Bottom;
            int startX  = this.ResizeRectangle.X;
            int endX    = this.ResizeRectangle.Right;

            int minX = Math.Max(0, startX);
            int maxX = Math.Min(width, endX);
            int minY = Math.Max(0, startY);
            int maxY = Math.Min(height, endY);

            if (this.Sampler is NearestNeighborResampler)
            {
                var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);

                // Scaling factors
                float widthFactor  = sourceRectangle.Width / (float)this.ResizeRectangle.Width;
                float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height;

                ParallelHelper.IterateRows(
                    workingRect,
                    configuration,
                    rows =>
                {
                    for (int y = rows.Min; y < rows.Max; y++)
                    {
                        // Y coordinates of source points
                        Span <TPixel> sourceRow =
                            source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY));
                        Span <TPixel> targetRow = destination.GetPixelRowSpan(y);

                        for (int x = minX; x < maxX; x++)
                        {
                            // X coordinates of source points
                            targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)];
                        }
                    }
                });

                return;
            }

            int sourceHeight = source.Height;

            // Interpolate the image using the calculated weights.
            // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm
            // First process the columns. Since we are not using multiple threads startY and endY
            // are the upper and lower bounds of the source rectangle.
            using (Buffer2D <Vector4> firstPassPixelsTransposed = source.MemoryAllocator.Allocate2D <Vector4>(sourceHeight, width))
            {
                firstPassPixelsTransposed.MemorySource.Clear();

                var processColsRect = new Rectangle(0, 0, source.Width, sourceRectangle.Bottom);

                ParallelHelper.IterateRowsWithTempBuffer <Vector4>(
                    processColsRect,
                    configuration,
                    (rows, tempRowBuffer) =>
                {
                    for (int y = rows.Min; y < rows.Max; y++)
                    {
                        Span <TPixel> sourceRow    = source.GetPixelRowSpan(y).Slice(sourceX);
                        Span <Vector4> tempRowSpan = tempRowBuffer.Span.Slice(sourceX);

                        PixelOperations <TPixel> .Instance.ToVector4(configuration, sourceRow, tempRowSpan);
                        Vector4Utils.Premultiply(tempRowSpan);

                        ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y];

                        if (this.Compand)
                        {
                            SRgbCompanding.Expand(tempRowSpan);
                        }

                        for (int x = minX; x < maxX; x++)
                        {
                            ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX);
                            Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) =
                                kernel.Convolve(tempRowSpan);
                        }
                    }
                });

                var processRowsRect = Rectangle.FromLTRB(0, minY, width, maxY);

                // Now process the rows.
                ParallelHelper.IterateRowsWithTempBuffer <Vector4>(
                    processRowsRect,
                    configuration,
                    (rows, tempRowBuffer) =>
                {
                    Span <Vector4> tempRowSpan = tempRowBuffer.Span;

                    for (int y = rows.Min; y < rows.Max; y++)
                    {
                        // Ensure offsets are normalized for cropping and padding.
                        ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY);

                        ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempRowSpan);

                        for (int x = 0; x < width; x++)
                        {
                            Span <Vector4> firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x).Slice(sourceY);

                            // Destination color components
                            Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn);
                        }

                        Vector4Utils.UnPremultiply(tempRowSpan);

                        if (this.Compand)
                        {
                            SRgbCompanding.Compress(tempRowSpan);
                        }

                        Span <TPixel> targetRowSpan = destination.GetPixelRowSpan(y);
                        PixelOperations <TPixel> .Instance.FromVector4(configuration, tempRowSpan, targetRowSpan);
                    }
                });
            }
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Reads a uncompressed TGA image with a palette.
        /// </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="palette">The color palette.</param>
        /// <param name="colorMapPixelSizeInBytes">Color map size of one entry in bytes.</param>
        /// <param name="origin">The image origin.</param>
        private void ReadPaletted <TPixel>(int width, int height, Buffer2D <TPixel> pixels, Span <byte> palette, int colorMapPixelSizeInBytes, TgaImageOrigin origin)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            TPixel color   = default;
            bool   invertX = InvertX(origin);

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

                switch (colorMapPixelSizeInBytes)
                {
                case 2:
                    if (invertX)
                    {
                        for (int x = width - 1; x >= 0; x--)
                        {
                            this.ReadPalettedBgra16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
                        }
                    }
                    else
                    {
                        for (int x = 0; x < width; x++)
                        {
                            this.ReadPalettedBgra16Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
                        }
                    }

                    break;

                case 3:
                    if (invertX)
                    {
                        for (int x = width - 1; x >= 0; x--)
                        {
                            this.ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
                        }
                    }
                    else
                    {
                        for (int x = 0; x < width; x++)
                        {
                            this.ReadPalettedBgr24Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
                        }
                    }

                    break;

                case 4:
                    if (invertX)
                    {
                        for (int x = width - 1; x >= 0; x--)
                        {
                            this.ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
                        }
                    }
                    else
                    {
                        for (int x = 0; x < width; x++)
                        {
                            this.ReadPalettedBgra32Pixel(palette, colorMapPixelSizeInBytes, x, color, pixelRow);
                        }
                    }

                    break;
                }
            }
        }
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source, ImageFrame <TPixel> destination, Rectangle sourceRectangle, Configuration configuration)
        {
            int height = this.TargetDimensions.Height;
            int width  = this.TargetDimensions.Width;

            Rectangle sourceBounds = source.Bounds();
            var       targetBounds = new Rectangle(0, 0, width, height);

            // Since could potentially be resizing the canvas we might need to re-calculate the matrix
            Matrix4x4 matrix = this.GetProcessingMatrix(sourceBounds, targetBounds);

            // Convert from screen to world space.
            Matrix4x4.Invert(matrix, out matrix);
            const float Epsilon = 0.0000001F;

            if (this.Sampler is NearestNeighborResampler)
            {
                Parallel.For(
                    0,
                    height,
                    configuration.ParallelOptions,
                    y =>
                {
                    Span <TPixel> destRow = destination.GetPixelRowSpan(y);

                    for (int x = 0; x < width; x++)
                    {
                        var v3 = Vector3.Transform(new Vector3(x, y, 1), matrix);

                        float z = MathF.Max(v3.Z, Epsilon);
                        int px  = (int)MathF.Round(v3.X / z);
                        int py  = (int)MathF.Round(v3.Y / z);

                        if (sourceBounds.Contains(px, py))
                        {
                            destRow[x] = source[px, py];
                        }
                    }
                });

                return;
            }

            int maxSourceX = source.Width - 1;
            int maxSourceY = source.Height - 1;

            (float radius, float scale, float ratio)xRadiusScale = this.GetSamplingRadius(source.Width, destination.Width);
            (float radius, float scale, float ratio)yRadiusScale = this.GetSamplingRadius(source.Height, destination.Height);
            float xScale = xRadiusScale.scale;
            float yScale = yRadiusScale.scale;

            // Using Vector4 with dummy 0-s, because Vector2 SIMD implementation is not reliable:
            var radius = new Vector4(xRadiusScale.radius, yRadiusScale.radius, 0, 0);

            IResampler sampler   = this.Sampler;
            var        maxSource = new Vector4(maxSourceX, maxSourceY, maxSourceX, maxSourceY);
            int        xLength   = (int)MathF.Ceiling((radius.X * 2) + 2);
            int        yLength   = (int)MathF.Ceiling((radius.Y * 2) + 2);

            MemoryAllocator memoryAllocator = configuration.MemoryAllocator;

            using (Buffer2D <float> yBuffer = memoryAllocator.Allocate2D <float>(yLength, height))
                using (Buffer2D <float> xBuffer = memoryAllocator.Allocate2D <float>(xLength, height))
                {
                    Parallel.For(
                        0,
                        height,
                        configuration.ParallelOptions,
                        y =>
                    {
                        ref TPixel destRowRef = ref MemoryMarshal.GetReference(destination.GetPixelRowSpan(y));
                        ref float ySpanRef    = ref MemoryMarshal.GetReference(yBuffer.GetRowSpan(y));
                        ref float xSpanRef    = ref MemoryMarshal.GetReference(xBuffer.GetRowSpan(y));
Ejemplo n.º 12
0
 public void Setup()
 {
     this.buffer     = Configuration.Default.MemoryAllocator.Allocate2D <float>(1000, 500);
     this.destRegion = this.buffer.GetRegion(200, 100, 128, 128);
 }
Ejemplo n.º 13
0
        /// <summary>
        /// Reads a run length encoded TGA image with a palette.
        /// </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="palette">The color palette.</param>
        /// <param name="colorMapPixelSizeInBytes">Color map size of one entry in bytes.</param>
        /// <param name="inverted">Indicates, if the origin of the image is top left rather the bottom left (the default).</param>
        private void ReadPalettedRle <TPixel>(int width, int height, Buffer2D <TPixel> pixels, byte[] palette, int colorMapPixelSizeInBytes, bool inverted)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            int bytesPerPixel = 1;

            using (IMemoryOwner <byte> buffer = this.memoryAllocator.Allocate <byte>(width * height * bytesPerPixel, AllocationOptions.Clean))
            {
                TPixel      color      = default;
                var         alphaBits  = this.tgaMetadata.AlphaChannelBits;
                Span <byte> bufferSpan = buffer.GetSpan();
                this.UncompressRle(width, height, bufferSpan, bytesPerPixel: 1);

                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;
                        switch (colorMapPixelSizeInBytes)
                        {
                        case 1:
                            color.FromL8(Unsafe.As <byte, L8>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
                            break;

                        case 2:

                            Bgra5551 bgra = Unsafe.As <byte, Bgra5551>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]);
                            if (!this.hasAlpha)
                            {
                                // Set alpha value to 1, to treat it as opaque for Bgra5551.
                                bgra.PackedValue = (ushort)(bgra.PackedValue | 0x8000);
                            }

                            color.FromBgra5551(bgra);
                            break;

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

                        case 4:
                            if (this.hasAlpha)
                            {
                                color.FromBgra32(Unsafe.As <byte, Bgra32>(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
                            }
                            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;
                    }
                }
            }
        }
Ejemplo n.º 14
0
        /// <inheritdoc/>
        protected override unsafe void OnApply(ImageFrame <TPixel> source, ImageFrame <TPixel> cloned, Rectangle sourceRectangle, Configuration configuration)
        {
            // Jump out, we'll deal with that later.
            if (source.Width == cloned.Width && source.Height == cloned.Height && sourceRectangle == this.ResizeRectangle)
            {
                // the cloned will be blank here copy all the pixel data over
                source.GetPixelSpan().CopyTo(cloned.GetPixelSpan());
                return;
            }

            int width   = this.Width;
            int height  = this.Height;
            int sourceX = sourceRectangle.X;
            int sourceY = sourceRectangle.Y;
            int startY  = this.ResizeRectangle.Y;
            int endY    = this.ResizeRectangle.Bottom;
            int startX  = this.ResizeRectangle.X;
            int endX    = this.ResizeRectangle.Right;

            int minX = Math.Max(0, startX);
            int maxX = Math.Min(width, endX);
            int minY = Math.Max(0, startY);
            int maxY = Math.Min(height, endY);

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

                Parallel.For(
                    minY,
                    maxY,
                    configuration.ParallelOptions,
                    y =>
                {
                    // Y coordinates of source points
                    Span <TPixel> sourceRow = source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY));
                    Span <TPixel> targetRow = cloned.GetPixelRowSpan(y);

                    for (int x = minX; x < maxX; x++)
                    {
                        // X coordinates of source points
                        targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)];
                    }
                });

                return;
            }

            // Interpolate the image using the calculated weights.
            // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm
            // First process the columns. Since we are not using multiple threads startY and endY
            // are the upper and lower bounds of the source rectangle.
            // TODO: Using a transposed variant of 'firstPassPixels' could eliminate the need for the WeightsWindow.ComputeWeightedColumnSum() method, and improve speed!
            using (var firstPassPixels = new Buffer2D <Vector4>(width, source.Height))
            {
                firstPassPixels.Clear();

                Parallel.For(
                    0,
                    sourceRectangle.Bottom,
                    configuration.ParallelOptions,
                    y =>
                {
                    // TODO: Without Parallel.For() this buffer object could be reused:
                    using (var tempRowBuffer = new Buffer <Vector4>(source.Width))
                    {
                        Span <Vector4> firstPassRow = firstPassPixels.GetRowSpan(y);
                        Span <TPixel> sourceRow     = source.GetPixelRowSpan(y);
                        PixelOperations <TPixel> .Instance.ToVector4(sourceRow, tempRowBuffer, sourceRow.Length);

                        if (this.Compand)
                        {
                            for (int x = minX; x < maxX; x++)
                            {
                                WeightsWindow window = this.HorizontalWeights.Weights[x - startX];
                                firstPassRow[x]      = window.ComputeExpandedWeightedRowSum(tempRowBuffer, sourceX);
                            }
                        }
                        else
                        {
                            for (int x = minX; x < maxX; x++)
                            {
                                WeightsWindow window = this.HorizontalWeights.Weights[x - startX];
                                firstPassRow[x]      = window.ComputeWeightedRowSum(tempRowBuffer, sourceX);
                            }
                        }
                    }
                });

                // Now process the rows.
                Parallel.For(
                    minY,
                    maxY,
                    configuration.ParallelOptions,
                    y =>
                {
                    // Ensure offsets are normalised for cropping and padding.
                    WeightsWindow window    = this.VerticalWeights.Weights[y - startY];
                    Span <TPixel> targetRow = cloned.GetPixelRowSpan(y);

                    if (this.Compand)
                    {
                        for (int x = 0; x < width; x++)
                        {
                            // Destination color components
                            Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY);
                            destination         = destination.Compress();

                            ref TPixel pixel = ref targetRow[x];
                            pixel.PackFromVector4(destination);
                        }
                    }
                    else
                    {
                        for (int x = 0; x < width; x++)
                        {
                            // Destination color components
                            Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY);

                            ref TPixel pixel = ref targetRow[x];
                            pixel.PackFromVector4(destination);
                        }
                    }
                });
Ejemplo n.º 15
0
        /// <summary>
        /// Decodes the image from the specified stream.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="stream">The stream, where the image should be decoded from. Cannot be null.</param>
        /// <exception cref="System.ArgumentNullException">
        ///    <para><paramref name="stream"/> is null.</para>
        /// </exception>
        /// <returns>The decoded image.</returns>
        public Image <TPixel> Decode <TPixel>(Stream stream)
            where TPixel : struct, IPixel <TPixel>
        {
            try
            {
                bool inverted = this.ReadFileHeader(stream);
                this.currentStream.Skip(this.fileHeader.IdLength);

                // Parse the color map, if present.
                if (this.fileHeader.ColorMapType != 0 && this.fileHeader.ColorMapType != 1)
                {
                    TgaThrowHelper.ThrowNotSupportedException($"Unknown tga colormap type {this.fileHeader.ColorMapType} found");
                }

                if (this.fileHeader.Width == 0 || this.fileHeader.Height == 0)
                {
                    throw new UnknownImageFormatException("Width or height cannot be 0");
                }

                var image = new Image <TPixel>(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata);
                Buffer2D <TPixel> pixels = image.GetRootFramePixelBuffer();

                if (this.fileHeader.ColorMapType is 1)
                {
                    if (this.fileHeader.CMapLength <= 0)
                    {
                        TgaThrowHelper.ThrowImageFormatException("Missing tga color map length");
                    }

                    if (this.fileHeader.CMapDepth <= 0)
                    {
                        TgaThrowHelper.ThrowImageFormatException("Missing tga color map depth");
                    }

                    int colorMapPixelSizeInBytes = this.fileHeader.CMapDepth / 8;
                    int colorMapSizeInBytes      = this.fileHeader.CMapLength * colorMapPixelSizeInBytes;
                    using (IManagedByteBuffer palette = this.memoryAllocator.AllocateManagedByteBuffer(colorMapSizeInBytes, AllocationOptions.Clean))
                    {
                        this.currentStream.Read(palette.Array, this.fileHeader.CMapStart, colorMapSizeInBytes);

                        if (this.fileHeader.ImageType is TgaImageType.RleColorMapped)
                        {
                            this.ReadPalettedRle(
                                this.fileHeader.Width,
                                this.fileHeader.Height,
                                pixels,
                                palette.Array,
                                colorMapPixelSizeInBytes,
                                inverted);
                        }
                        else
                        {
                            this.ReadPaletted(
                                this.fileHeader.Width,
                                this.fileHeader.Height,
                                pixels,
                                palette.Array,
                                colorMapPixelSizeInBytes,
                                inverted);
                        }
                    }

                    return(image);
                }

                // Even if the image type indicates it is not a paletted image, it can still contain a palette. Skip those bytes.
                if (this.fileHeader.CMapLength > 0)
                {
                    int colorMapPixelSizeInBytes = this.fileHeader.CMapDepth / 8;
                    this.currentStream.Skip(this.fileHeader.CMapLength * colorMapPixelSizeInBytes);
                }

                switch (this.fileHeader.PixelDepth)
                {
                case 8:
                    if (this.fileHeader.ImageType.IsRunLengthEncoded())
                    {
                        this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 1, inverted);
                    }
                    else
                    {
                        this.ReadMonoChrome(this.fileHeader.Width, this.fileHeader.Height, pixels, inverted);
                    }

                    break;

                case 15:
                case 16:
                    if (this.fileHeader.ImageType.IsRunLengthEncoded())
                    {
                        this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 2, inverted);
                    }
                    else
                    {
                        this.ReadBgra16(this.fileHeader.Width, this.fileHeader.Height, pixels, inverted);
                    }

                    break;

                case 24:
                    if (this.fileHeader.ImageType.IsRunLengthEncoded())
                    {
                        this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 3, inverted);
                    }
                    else
                    {
                        this.ReadBgr24(this.fileHeader.Width, this.fileHeader.Height, pixels, inverted);
                    }

                    break;

                case 32:
                    if (this.fileHeader.ImageType.IsRunLengthEncoded())
                    {
                        this.ReadRle(this.fileHeader.Width, this.fileHeader.Height, pixels, 4, inverted);
                    }
                    else
                    {
                        this.ReadBgra32(this.fileHeader.Width, this.fileHeader.Height, pixels, inverted);
                    }

                    break;

                default:
                    TgaThrowHelper.ThrowNotSupportedException("Does not support this kind of tga files.");
                    break;
                }

                return(image);
            }
            catch (IndexOutOfRangeException e)
            {
                throw new ImageFormatException("TGA image does not have a valid format.", e);
            }
        }
Ejemplo n.º 16
0
 /// <summary>
 /// Reads the frames colors, mapping indices to colors.
 /// </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>
 /// <param name="indices">The indexed pixels.</param>
 /// <param name="colorTable">The color table containing the available colors.</param>
 /// <param name="descriptor">The <see cref="GifImageDescriptor"/></param>
 private void ReadFrameColors <TPixel>(ref Image <TPixel> image, ref ImageFrame <TPixel> previousFrame, Buffer2D <byte> indices, ReadOnlySpan <Rgb24> colorTable, in GifImageDescriptor descriptor)
Ejemplo n.º 17
0
 public RowIntervalOperation(Configuration configuration, Rectangle bounds, Buffer2D <TPixel> source)
 {
     this.configuration = configuration;
     this.bounds        = bounds;
     this.source        = source;
 }