Ejemplo n.º 1
0
        /// <summary>
        ///     Applies all PNG filters to the given scanline and returns the filtered scanline that is deemed
        ///     to be most compressible, using lowest total variation as proxy for compressibility.
        /// </summary>
        /// <param name="rawScanline"></param>
        /// <param name="previousScanline"></param>
        /// <param name="bytesPerPixel"></param>
        /// <returns></returns>
        private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, int bytesPerPixel)
        {
            var tupleList = new List <Tuple <byte[], int> >();
            var input1    = SubFilter.Encode(rawScanline, bytesPerPixel);

            tupleList.Add(new Tuple <byte[], int>(input1, CalculateTotalVariation(input1)));
            var input2 = UpFilter.Encode(rawScanline, previousScanline);

            tupleList.Add(new Tuple <byte[], int>(input2, CalculateTotalVariation(input2)));
            var input3 = AverageFilter.Encode(rawScanline, previousScanline, bytesPerPixel);

            tupleList.Add(new Tuple <byte[], int>(input3, CalculateTotalVariation(input3)));
            var input4 = PaethFilter.Encode(rawScanline, previousScanline, bytesPerPixel);

            tupleList.Add(new Tuple <byte[], int>(input4, CalculateTotalVariation(input4)));
            var maxValue = int.MaxValue;
            var index1   = 0;

            for (var index2 = 0; index2 < tupleList.Count; ++index2)
            {
                if (tupleList[index2].Item2 < maxValue)
                {
                    index1   = index2;
                    maxValue = tupleList[index2].Item2;
                }
            }

            return(tupleList[index1].Item1);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Encodes the pixel data line by line.
        /// Each scanline is encoded in the most optimal manner to improve compression.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="rowSpan">The row span.</param>
        /// <param name="quantizedPixelsSpan">The span of quantized pixels. Can be null.</param>
        /// <param name="row">The row.</param>
        /// <returns>The <see cref="IManagedByteBuffer"/></returns>
        private IManagedByteBuffer EncodePixelRow <TPixel>(ReadOnlySpan <TPixel> rowSpan, ReadOnlySpan <byte> quantizedPixelsSpan, int row)
            where TPixel : struct, IPixel <TPixel>
        {
            switch (this.pngColorType)
            {
            case PngColorType.Palette:

                int stride = this.rawScanline.Length();
                quantizedPixelsSpan.Slice(row * stride, stride).CopyTo(this.rawScanline.GetSpan());

                break;

            case PngColorType.Grayscale:
            case PngColorType.GrayscaleWithAlpha:
                this.CollectGrayscaleBytes(rowSpan);
                break;

            default:
                this.CollectTPixelBytes(rowSpan);
                break;
            }

            switch (this.pngFilterMethod)
            {
            case PngFilterMethod.None:
                NoneFilter.Encode(this.rawScanline.GetSpan(), this.result.GetSpan());
                return(this.result);

            case PngFilterMethod.Sub:
                SubFilter.Encode(this.rawScanline.GetSpan(), this.sub.GetSpan(), this.bytesPerPixel, out int _);
                return(this.sub);

            case PngFilterMethod.Up:
                UpFilter.Encode(this.rawScanline.GetSpan(), this.previousScanline.GetSpan(), this.up.GetSpan(), out int _);
                return(this.up);

            case PngFilterMethod.Average:
                AverageFilter.Encode(this.rawScanline.GetSpan(), this.previousScanline.GetSpan(), this.average.GetSpan(), this.bytesPerPixel, out int _);
                return(this.average);

            case PngFilterMethod.Paeth:
                PaethFilter.Encode(this.rawScanline.GetSpan(), this.previousScanline.GetSpan(), this.paeth.GetSpan(), this.bytesPerPixel, out int _);
                return(this.paeth);

            default:
                return(this.GetOptimalFilteredScanline());
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Applies all PNG filters to the given scanline and returns the filtered scanline that is deemed
        /// to be most compressible, using lowest total variation as proxy for compressibility.
        /// </summary>
        /// <returns>The <see cref="T:byte[]"/></returns>
        private Buffer <byte> GetOptimalFilteredScanline()
        {
            Span <byte> scanSpan = this.rawScanline.Span;
            Span <byte> prevSpan = this.previousScanline.Span;

            // Palette images don't compress well with adaptive filtering.
            if (this.pngColorType == PngColorType.Palette || this.bitDepth < 8)
            {
                NoneFilter.Encode(this.rawScanline, this.result);
                return(this.result);
            }

            // This order, while different to the enumerated order is more likely to produce a smaller sum
            // early on which shaves a couple of milliseconds off the processing time.
            UpFilter.Encode(scanSpan, prevSpan, this.up);

            int           currentSum   = this.CalculateTotalVariation(this.up, int.MaxValue);
            int           lowestSum    = currentSum;
            Buffer <byte> actualResult = this.up;

            PaethFilter.Encode(scanSpan, prevSpan, this.paeth, this.bytesPerPixel);
            currentSum = this.CalculateTotalVariation(this.paeth, currentSum);

            if (currentSum < lowestSum)
            {
                lowestSum    = currentSum;
                actualResult = this.paeth;
            }

            SubFilter.Encode(scanSpan, this.sub, this.bytesPerPixel);
            currentSum = this.CalculateTotalVariation(this.sub, int.MaxValue);

            if (currentSum < lowestSum)
            {
                lowestSum    = currentSum;
                actualResult = this.sub;
            }

            AverageFilter.Encode(scanSpan, prevSpan, this.average, this.bytesPerPixel);
            currentSum = this.CalculateTotalVariation(this.average, currentSum);

            if (currentSum < lowestSum)
            {
                actualResult = this.average;
            }

            return(actualResult);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Applies all PNG filters to the given scanline and returns the filtered scanline that is deemed
        /// to be most compressible, using lowest total variation as proxy for compressibility.
        /// </summary>
        /// <returns>The <see cref="T:byte[]"/></returns>
        private IManagedByteBuffer GetOptimalFilteredScanline()
        {
            // Palette images don't compress well with adaptive filtering.
            if (this.pngColorType == PngColorType.Palette || this.bitDepth < 8)
            {
                NoneFilter.Encode(this.rawScanline.GetSpan(), this.result.GetSpan());
                return(this.result);
            }

            Span <byte> scanSpan = this.rawScanline.GetSpan();
            Span <byte> prevSpan = this.previousScanline.GetSpan();

            // This order, while different to the enumerated order is more likely to produce a smaller sum
            // early on which shaves a couple of milliseconds off the processing time.
            UpFilter.Encode(scanSpan, prevSpan, this.up.GetSpan(), out int currentSum);

            // TODO: PERF.. We should be breaking out of the encoding for each line as soon as we hit the sum.
            // That way the above comment would actually be true. It used to be anyway...
            // If we could use SIMD for none branching filters we could really speed it up.
            int lowestSum = currentSum;
            IManagedByteBuffer actualResult = this.up;

            PaethFilter.Encode(scanSpan, prevSpan, this.paeth.GetSpan(), this.bytesPerPixel, out currentSum);

            if (currentSum < lowestSum)
            {
                lowestSum    = currentSum;
                actualResult = this.paeth;
            }

            SubFilter.Encode(scanSpan, this.sub.GetSpan(), this.bytesPerPixel, out currentSum);

            if (currentSum < lowestSum)
            {
                lowestSum    = currentSum;
                actualResult = this.sub;
            }

            AverageFilter.Encode(scanSpan, prevSpan, this.average.GetSpan(), this.bytesPerPixel, out currentSum);

            if (currentSum < lowestSum)
            {
                actualResult = this.average;
            }

            return(actualResult);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Encodes the pixel data line by line.
        /// Each scanline is encoded in the most optimal manner to improve compression.
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="rowSpan">The row span.</param>
        /// <param name="row">The row.</param>
        /// <returns>The <see cref="IManagedByteBuffer"/></returns>
        private IManagedByteBuffer EncodePixelRow <TPixel>(ReadOnlySpan <TPixel> rowSpan, int row)
            where TPixel : struct, IPixel <TPixel>
        {
            switch (this.pngColorType)
            {
            case PngColorType.Palette:
                // TODO: Use Span copy!
                Buffer.BlockCopy(this.palettePixelData, row * this.rawScanline.Length(), this.rawScanline.Array, 0, this.rawScanline.Length());
                break;

            case PngColorType.Grayscale:
            case PngColorType.GrayscaleWithAlpha:
                this.CollectGrayscaleBytes(rowSpan);
                break;

            default:
                this.CollectTPixelBytes(rowSpan);
                break;
            }

            switch (this.pngFilterMethod)
            {
            case PngFilterMethod.None:
                NoneFilter.Encode(this.rawScanline.Span, this.result.Span);
                return(this.result);

            case PngFilterMethod.Sub:
                SubFilter.Encode(this.rawScanline.Span, this.sub.Span, this.bytesPerPixel, out int _);
                return(this.sub);

            case PngFilterMethod.Up:
                UpFilter.Encode(this.rawScanline.Span, this.previousScanline.Span, this.up.Span, out int _);
                return(this.up);

            case PngFilterMethod.Average:
                AverageFilter.Encode(this.rawScanline.Span, this.previousScanline.Span, this.average.Span, this.bytesPerPixel, out int _);
                return(this.average);

            case PngFilterMethod.Paeth:
                PaethFilter.Encode(this.rawScanline.Span, this.previousScanline.Span, this.paeth.Span, this.bytesPerPixel, out int _);
                return(this.paeth);

            default:
                return(this.GetOptimalFilteredScanline());
            }
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Applies all PNG filters to the given scanline and returns the filtered scanline that is deemed
        /// to be most compressible, using lowest total variation as proxy for compressibility.
        /// </summary>
        /// <returns>The <see cref="T:byte[]"/></returns>
        private IManagedByteBuffer GetOptimalFilteredScanline()
        {
            // Palette images don't compress well with adaptive filtering.
            if (this.pngColorType == PngColorType.Palette || this.bitDepth < 8)
            {
                NoneFilter.Encode(this.rawScanline.GetSpan(), this.result.GetSpan());
                return(this.result);
            }

            Span <byte> scanSpan = this.rawScanline.GetSpan();
            Span <byte> prevSpan = this.previousScanline.GetSpan();

            // This order, while different to the enumerated order is more likely to produce a smaller sum
            // early on which shaves a couple of milliseconds off the processing time.
            UpFilter.Encode(scanSpan, prevSpan, this.up.GetSpan(), out int currentSum);

            int lowestSum = currentSum;
            IManagedByteBuffer actualResult = this.up;

            PaethFilter.Encode(scanSpan, prevSpan, this.paeth.GetSpan(), this.bytesPerPixel, out currentSum);

            if (currentSum < lowestSum)
            {
                lowestSum    = currentSum;
                actualResult = this.paeth;
            }

            SubFilter.Encode(scanSpan, this.sub.GetSpan(), this.bytesPerPixel, out currentSum);

            if (currentSum < lowestSum)
            {
                lowestSum    = currentSum;
                actualResult = this.sub;
            }

            AverageFilter.Encode(scanSpan, prevSpan, this.average.GetSpan(), this.bytesPerPixel, out currentSum);

            if (currentSum < lowestSum)
            {
                actualResult = this.average;
            }

            return(actualResult);
        }