示例#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);
        }
示例#2
0
        /// <summary>
        /// Decodes the raw pixel data row by row
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="compressedStream">The compressed pixel data stream.</param>
        /// <param name="image">The image to decode to.</param>
        private void DecodePixelData <TPixel>(Stream compressedStream, ImageFrame <TPixel> image)
            where TPixel : struct, IPixel <TPixel>
        {
            while (this.currentRow < this.header.Height)
            {
                int bytesRead = compressedStream.Read(this.scanline.Array, this.currentRowBytesRead, this.bytesPerScanline - this.currentRowBytesRead);
                this.currentRowBytesRead += bytesRead;
                if (this.currentRowBytesRead < this.bytesPerScanline)
                {
                    return;
                }

                this.currentRowBytesRead = 0;
                var filterType = (FilterType)this.scanline[0];

                switch (filterType)
                {
                case FilterType.None:
                    break;

                case FilterType.Sub:

                    SubFilter.Decode(this.scanline, this.bytesPerPixel);
                    break;

                case FilterType.Up:

                    UpFilter.Decode(this.scanline, this.previousScanline);
                    break;

                case FilterType.Average:

                    AverageFilter.Decode(this.scanline, this.previousScanline, this.bytesPerPixel);
                    break;

                case FilterType.Paeth:

                    PaethFilter.Decode(this.scanline, this.previousScanline, this.bytesPerPixel);
                    break;

                default:
                    throw new ImageFormatException("Unknown filter type.");
                }

                this.ProcessDefilteredScanline(this.scanline.Array, image);

                Swap(ref this.scanline, ref this.previousScanline);
                this.currentRow++;
            }
        }
示例#3
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());
            }
        }
示例#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 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);
        }
示例#5
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);
        }
示例#6
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());
            }
        }
示例#7
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);
        }
示例#8
0
        private void DecodePixelData(byte[][] pixelData)
        {
//            data = new Color[_width * _height];
            _colors = new Color[width * height];
            pixels  = new int[width * height];

            var previousScanline = new byte[bytesPerScanline];

            for (var y = 0; y < height; ++y)
            {
                var    scanline = pixelData[y];
                byte[] defilteredScanline;
                switch (scanline[0])
                {
                case 0:
                    defilteredScanline = NoneFilter.Decode(scanline);
                    break;

                case 1:
                    defilteredScanline = SubFilter.Decode(scanline, bytesPerPixel);
                    break;

                case 2:
                    defilteredScanline = UpFilter.Decode(scanline, previousScanline);
                    break;

                case 3:
                    defilteredScanline = AverageFilter.Decode(scanline, previousScanline, bytesPerPixel);
                    break;

                case 4:
                    defilteredScanline = PaethFilter.Decode(scanline, previousScanline, bytesPerPixel);
                    break;

                default:
                    throw new Exception("Unknown filter type.");
                }

                previousScanline = defilteredScanline;
                ProcessDefilteredScanline(defilteredScanline, y);
            }
        }
示例#9
0
        /// <summary>
        /// Decodes the raw interlaced pixel data row by row
        /// <see href="https://github.com/juehv/DentalImageViewer/blob/8a1a4424b15d6cc453b5de3f273daf3ff5e3a90d/DentalImageViewer/lib/jiu-0.14.3/net/sourceforge/jiu/codecs/PNGCodec.java"/>
        /// </summary>
        /// <typeparam name="TPixel">The pixel format.</typeparam>
        /// <param name="compressedStream">The compressed pixel data stream.</param>
        /// <param name="image">The current image.</param>
        private void DecodeInterlacedPixelData <TPixel>(Stream compressedStream, ImageFrame <TPixel> image)
            where TPixel : struct, IPixel <TPixel>
        {
            while (true)
            {
                int numColumns = this.ComputeColumnsAdam7(this.pass);

                if (numColumns == 0)
                {
                    this.pass++;

                    // This pass contains no data; skip to next pass
                    continue;
                }

                int bytesPerInterlaceScanline = this.CalculateScanlineLength(numColumns) + 1;

                while (this.currentRow < this.header.Height)
                {
                    int bytesRead = compressedStream.Read(this.scanline.Array, this.currentRowBytesRead, bytesPerInterlaceScanline - this.currentRowBytesRead);
                    this.currentRowBytesRead += bytesRead;
                    if (this.currentRowBytesRead < bytesPerInterlaceScanline)
                    {
                        return;
                    }

                    this.currentRowBytesRead = 0;

                    Span <byte> scanSpan   = this.scanline.Slice(0, bytesPerInterlaceScanline);
                    Span <byte> prevSpan   = this.previousScanline.Slice(0, bytesPerInterlaceScanline);
                    var         filterType = (FilterType)scanSpan[0];

                    switch (filterType)
                    {
                    case FilterType.None:
                        break;

                    case FilterType.Sub:

                        SubFilter.Decode(scanSpan, this.bytesPerPixel);
                        break;

                    case FilterType.Up:

                        UpFilter.Decode(scanSpan, prevSpan);
                        break;

                    case FilterType.Average:

                        AverageFilter.Decode(scanSpan, prevSpan, this.bytesPerPixel);
                        break;

                    case FilterType.Paeth:

                        PaethFilter.Decode(scanSpan, prevSpan, this.bytesPerPixel);
                        break;

                    default:
                        throw new ImageFormatException("Unknown filter type.");
                    }

                    Span <TPixel> rowSpan = image.GetPixelRowSpan(this.currentRow);
                    this.ProcessInterlacedDefilteredScanline(this.scanline.Array, rowSpan, Adam7FirstColumn[this.pass], Adam7ColumnIncrement[this.pass]);

                    Swap(ref this.scanline, ref this.previousScanline);

                    this.currentRow += Adam7RowIncrement[this.pass];
                }

                this.pass++;
                this.previousScanline.Clear();

                if (this.pass < 7)
                {
                    this.currentRow = Adam7FirstRow[this.pass];
                }
                else
                {
                    this.pass = 0;
                    break;
                }
            }
        }