/// <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="pixels">The image pixel accessor.</param> private void DecodePixelData <TPixel>(Stream compressedStream, PixelAccessor <TPixel> pixels) where TPixel : struct, IPixel <TPixel> { while (this.currentRow < this.header.Height) { int bytesRead = compressedStream.Read(this.scanline, this.currentRowBytesRead, this.bytesPerScanline - this.currentRowBytesRead); this.currentRowBytesRead += bytesRead; if (this.currentRowBytesRead < this.bytesPerScanline) { return; } this.currentRowBytesRead = 0; FilterType filterType = (FilterType)this.scanline[0]; switch (filterType) { case FilterType.None: NoneFilter.Decode(this.scanline); break; case FilterType.Sub: SubFilter.Decode(this.scanline, this.bytesPerScanline, this.bytesPerPixel); break; case FilterType.Up: UpFilter.Decode(this.scanline, this.previousScanline, this.bytesPerScanline); break; case FilterType.Average: AverageFilter.Decode(this.scanline, this.previousScanline, this.bytesPerScanline, this.bytesPerPixel); break; case FilterType.Paeth: PaethFilter.Decode(this.scanline, this.previousScanline, this.bytesPerScanline, this.bytesPerPixel); break; default: throw new ImageFormatException("Unknown filter type."); } this.ProcessDefilteredScanline(this.scanline, pixels); Swap(ref this.scanline, ref this.previousScanline); this.currentRow++; } }
/// <summary> /// Decodes the raw pixel data row by row /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <param name="compressedStream">The compressed pixel data stream.</param> /// <param name="pixels">The image pixel accessor.</param> private void DecodePixelData <TColor, TPacked>(Stream compressedStream, PixelAccessor <TColor, TPacked> pixels) where TColor : struct, IPackedPixel <TPacked> where TPacked : struct { // TODO: ArrayPool<byte>.Shared.Rent(this.bytesPerScanline) byte[] previousScanline = new byte[this.bytesPerScanline]; byte[] scanline = new byte[this.bytesPerScanline]; for (int y = 0; y < this.header.Height; y++) { compressedStream.Read(scanline, 0, this.bytesPerScanline); FilterType filterType = (FilterType)scanline[0]; byte[] defilteredScanline; // TODO: It would be good if we can reduce the memory usage here. Each filter is creating a new row. switch (filterType) { case FilterType.None: defilteredScanline = NoneFilter.Decode(scanline); break; case FilterType.Sub: defilteredScanline = SubFilter.Decode(scanline, this.bytesPerPixel); break; case FilterType.Up: defilteredScanline = UpFilter.Decode(scanline, previousScanline); break; case FilterType.Average: defilteredScanline = AverageFilter.Decode(scanline, previousScanline, this.bytesPerPixel); break; case FilterType.Paeth: defilteredScanline = PaethFilter.Decode(scanline, previousScanline, this.bytesPerPixel); break; default: throw new ImageFormatException("Unknown filter type."); } previousScanline = defilteredScanline; this.ProcessDefilteredScanline(defilteredScanline, y, pixels); } }
/// <summary> /// Decodes the raw pixel data row by row /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <param name="pixelData">The pixel data.</param> /// <param name="pixels">The image pixels.</param> private void DecodePixelData <TColor, TPacked>(byte[] pixelData, TColor[] pixels) where TColor : struct, IPackedPixel <TPacked> where TPacked : struct { byte[] previousScanline = new byte[this.bytesPerScanline]; for (int y = 0; y < this.header.Height; y++) { byte[] scanline = new byte[this.bytesPerScanline]; Array.Copy(pixelData, y * this.bytesPerScanline, scanline, 0, this.bytesPerScanline); FilterType filterType = (FilterType)scanline[0]; byte[] defilteredScanline; switch (filterType) { case FilterType.None: defilteredScanline = NoneFilter.Decode(scanline); break; case FilterType.Sub: defilteredScanline = SubFilter.Decode(scanline, this.bytesPerPixel); break; case FilterType.Up: defilteredScanline = UpFilter.Decode(scanline, previousScanline); break; case FilterType.Average: defilteredScanline = AverageFilter.Decode(scanline, previousScanline, this.bytesPerPixel); break; case FilterType.Paeth: defilteredScanline = PaethFilter.Decode(scanline, previousScanline, this.bytesPerPixel); break; default: throw new ImageFormatException("Unknown filter type."); } previousScanline = defilteredScanline; this.ProcessDefilteredScanline <TColor, TPacked>(defilteredScanline, y, pixels); } }
/// <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); } }
/// <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; } } }