Beispiel #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">The raw scanline</param>
        /// <param name="previousScanline">The previous scanline</param>
        /// <param name="result">The filtered scanline result.</param>
        /// <returns>The <see cref="T:byte[]"/></returns>
        private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, byte[] result)
        {
            // Palette images don't compress well with adaptive filtering.
            if (this.PngColorType == PngColorType.Palette || this.bitDepth < 8)
            {
                NoneFilter.Encode(rawScanline, result);
                return(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(rawScanline, previousScanline, this.up);
            int currentSum = this.CalculateTotalVariation(this.up, int.MaxValue);
            int lowestSum  = currentSum;

            result = this.up;

            PaethFilter.Encode(rawScanline, previousScanline, this.paeth, this.bytesPerPixel);
            currentSum = this.CalculateTotalVariation(this.paeth, currentSum);

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

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

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

            AverageFilter.Encode(rawScanline, previousScanline, this.average, this.bytesPerPixel);
            currentSum = this.CalculateTotalVariation(this.average, currentSum);

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

            return(result);
        }
        /// <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">The raw scanline</param>
        /// <param name="previousScanline">The previous scanline</param>
        /// <param name="result">The filtered scanline result.</param>
        /// <returns>The <see cref="T:byte[]"/></returns>
        private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, byte[] result)
        {
            // Palette images don't compress well with adaptive filtering.
            if (this.PngColorType == PngColorType.Palette)
            {
                NoneFilter.Encode(rawScanline, result);
                return(result);
            }

            SubFilter.Encode(rawScanline, this.sub, this.bytesPerPixel);
            int currentTotalVariation = this.CalculateTotalVariation(this.sub);
            int lowestTotalVariation  = currentTotalVariation;

            result = this.sub;

            UpFilter.Encode(rawScanline, previousScanline, this.up);
            currentTotalVariation = this.CalculateTotalVariation(this.up);

            if (currentTotalVariation < lowestTotalVariation)
            {
                lowestTotalVariation = currentTotalVariation;
                result = this.up;
            }

            AverageFilter.Encode(rawScanline, previousScanline, this.average, this.bytesPerPixel);
            currentTotalVariation = this.CalculateTotalVariation(this.average);

            if (currentTotalVariation < lowestTotalVariation)
            {
                lowestTotalVariation = currentTotalVariation;
                result = this.average;
            }

            PaethFilter.Encode(rawScanline, previousScanline, this.paeth, this.bytesPerPixel);
            currentTotalVariation = this.CalculateTotalVariation(this.paeth);

            if (currentTotalVariation < lowestTotalVariation)
            {
                result = this.paeth;
            }

            return(result);
        }
Beispiel #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>
        /// <param name="rawScanline">The raw scanline</param>
        /// <param name="previousScanline">The previous scanline</param>
        /// <param name="result">The filtered scanline result</param>
        /// <param name="bytesPerScanline">The number of bytes per scanline</param>
        private void GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, byte[] result, int bytesPerScanline)
        {
            // Palette images don't compress well with adaptive filtering.
            if (this.PngColorType == PngColorType.Palette)
            {
                NoneFilter.Encode(rawScanline, result, bytesPerScanline);
                return;
            }

            Tuple <byte[], int>[] candidates = new Tuple <byte[], int> [4];

            byte[] sub = SubFilter.Encode(rawScanline, this.bytesPerPixel, bytesPerScanline);
            candidates[0] = new Tuple <byte[], int>(sub, this.CalculateTotalVariation(sub));

            byte[] up = UpFilter.Encode(rawScanline, previousScanline, result, bytesPerScanline);
            candidates[1] = new Tuple <byte[], int>(up, this.CalculateTotalVariation(up));

            byte[] average = AverageFilter.Encode(rawScanline, previousScanline, result, this.bytesPerPixel, bytesPerScanline);
            candidates[2] = new Tuple <byte[], int>(average, this.CalculateTotalVariation(average));

            byte[] paeth = PaethFilter.Encode(rawScanline, previousScanline, result, this.bytesPerPixel, bytesPerScanline);
            candidates[3] = new Tuple <byte[], int>(paeth, this.CalculateTotalVariation(paeth));

            int lowestTotalVariation      = int.MaxValue;
            int lowestTotalVariationIndex = 0;

            for (int i = 0; i < candidates.Length; i++)
            {
                if (candidates[i].Item2 < lowestTotalVariation)
                {
                    lowestTotalVariationIndex = i;
                    lowestTotalVariation      = candidates[i].Item2;
                }
            }

            // ReSharper disable once RedundantAssignment
            result = candidates[lowestTotalVariationIndex].Item1;
        }
Beispiel #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>
        /// <param name="rawScanline">The raw scanline</param>
        /// <param name="previousScanline">The previous scanline</param>
        /// <param name="byteCount">The number of bytes per pixel</param>
        /// <returns>The <see cref="T:byte[]"/></returns>
        private byte[] GetOptimalFilteredScanline(byte[] rawScanline, byte[] previousScanline, int byteCount)
        {
            List <Tuple <byte[], int> > candidates = new List <Tuple <byte[], int> >();

            if (this.PngColorType == PngColorType.Palette)
            {
                byte[] none = NoneFilter.Encode(rawScanline);
                candidates.Add(new Tuple <byte[], int>(none, this.CalculateTotalVariation(none)));
            }
            else
            {
                byte[] sub = SubFilter.Encode(rawScanline, byteCount);
                candidates.Add(new Tuple <byte[], int>(sub, this.CalculateTotalVariation(sub)));

                byte[] up = UpFilter.Encode(rawScanline, previousScanline);
                candidates.Add(new Tuple <byte[], int>(up, this.CalculateTotalVariation(up)));

                byte[] average = AverageFilter.Encode(rawScanline, previousScanline, byteCount);
                candidates.Add(new Tuple <byte[], int>(average, this.CalculateTotalVariation(average)));

                byte[] paeth = PaethFilter.Encode(rawScanline, previousScanline, byteCount);
                candidates.Add(new Tuple <byte[], int>(paeth, this.CalculateTotalVariation(paeth)));
            }

            int lowestTotalVariation      = int.MaxValue;
            int lowestTotalVariationIndex = 0;

            for (int i = 0; i < candidates.Count; i++)
            {
                if (candidates[i].Item2 < lowestTotalVariation)
                {
                    lowestTotalVariationIndex = i;
                    lowestTotalVariation      = candidates[i].Item2;
                }
            }

            return(candidates[lowestTotalVariationIndex].Item1);
        }
        /// <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="TColor">The pixel format.</typeparam>
        /// <param name="compressedStream">The compressed pixel data stream.</param>
        /// <param name="pixels">The image pixel accessor.</param>
        private void DecodeInterlacedPixelData <TColor>(Stream compressedStream, PixelAccessor <TColor> pixels)
            where TColor : struct, IPixel <TColor>
        {
            byte[] previousScanline = ArrayPool <byte> .Shared.Rent(this.bytesPerScanline);

            byte[] scanline = ArrayPool <byte> .Shared.Rent(this.bytesPerScanline);

            try
            {
                for (int pass = 0; pass < 7; pass++)
                {
                    // Zero out the scanlines, because the bytes that are rented from the arraypool may not be zero.
                    Array.Clear(scanline, 0, this.bytesPerScanline);
                    Array.Clear(previousScanline, 0, this.bytesPerScanline);

                    int y          = Adam7FirstRow[pass];
                    int numColumns = this.ComputeColumnsAdam7(pass);

                    if (numColumns == 0)
                    {
                        // This pass contains no data; skip to next pass
                        continue;
                    }

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

                    while (y < this.header.Height)
                    {
                        compressedStream.Read(scanline, 0, bytesPerInterlaceScanline);

                        FilterType filterType = (FilterType)scanline[0];

                        switch (filterType)
                        {
                        case FilterType.None:

                            NoneFilter.Decode(scanline);

                            break;

                        case FilterType.Sub:

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

                            break;

                        case FilterType.Up:

                            UpFilter.Decode(scanline, previousScanline, bytesPerInterlaceScanline);

                            break;

                        case FilterType.Average:

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

                            break;

                        case FilterType.Paeth:

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

                            break;

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

                        this.ProcessInterlacedDefilteredScanline(scanline, y, pixels, Adam7FirstColumn[pass], Adam7ColumnIncrement[pass]);

                        Swap(ref scanline, ref previousScanline);

                        y += Adam7RowIncrement[pass];
                    }
                }
            }
            finally
            {
                ArrayPool <byte> .Shared.Return(previousScanline);

                ArrayPool <byte> .Shared.Return(scanline);
            }
        }
        /// <summary>
        /// Decodes the raw pixel data row by row
        /// </summary>
        /// <typeparam name="TColor">The pixel format.</typeparam>
        /// <param name="compressedStream">The compressed pixel data stream.</param>
        /// <param name="pixels">The image pixel accessor.</param>
        private void DecodePixelData <TColor>(Stream compressedStream, PixelAccessor <TColor> pixels)
            where TColor : struct, IPixel <TColor>
        {
            byte[] previousScanline = ArrayPool <byte> .Shared.Rent(this.bytesPerScanline);

            byte[] scanline = ArrayPool <byte> .Shared.Rent(this.bytesPerScanline);

            // Zero out the scanlines, because the bytes that are rented from the arraypool may not be zero.
            Array.Clear(scanline, 0, this.bytesPerScanline);
            Array.Clear(previousScanline, 0, this.bytesPerScanline);

            try
            {
                for (int y = 0; y < this.header.Height; y++)
                {
                    compressedStream.Read(scanline, 0, this.bytesPerScanline);

                    FilterType filterType = (FilterType)scanline[0];

                    switch (filterType)
                    {
                    case FilterType.None:

                        NoneFilter.Decode(scanline);

                        break;

                    case FilterType.Sub:

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

                        break;

                    case FilterType.Up:

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

                        break;

                    case FilterType.Average:

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

                        break;

                    case FilterType.Paeth:

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

                        break;

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

                    this.ProcessDefilteredScanline(scanline, y, pixels);

                    Swap(ref scanline, ref previousScanline);
                }
            }
            finally
            {
                ArrayPool <byte> .Shared.Return(previousScanline);

                ArrayPool <byte> .Shared.Return(scanline);
            }
        }
Beispiel #7
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, Image <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.GetRowSpan(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;
                }
            }
        }