/// <inheritdoc/> protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span <byte> buffer) { long pos = stream.Position; using (var deframeStream = new ZlibInflateStream( stream, () => { int left = (int)(byteCount - (stream.Position - pos)); return(left > 0 ? left : 0); })) { deframeStream.AllocateNewBytes(byteCount, true); DeflateStream dataStream = deframeStream.CompressedStream; int totalRead = 0; while (totalRead < buffer.Length) { int bytesRead = dataStream.Read(buffer, totalRead, buffer.Length - totalRead); if (bytesRead <= 0) { break; } totalRead += bytesRead; } } if (this.Predictor == TiffPredictor.Horizontal) { HorizontalPredictor.Undo(buffer, this.Width, this.colorType, this.isBigEndian); } }
/// <summary> /// Decodes the stream to the image. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="stream">The stream containing image data. </param> /// <exception cref="ImageFormatException"> /// Thrown if the stream does not contain and end chunk. /// </exception> /// <exception cref="ArgumentOutOfRangeException"> /// Thrown if the image is larger than the maximum allowable size. /// </exception> /// <returns>The decoded image</returns> public Image <TPixel> Decode <TPixel>(Stream stream) where TPixel : struct, IPixel <TPixel> { var metaData = new ImageMetaData(); PngMetaData pngMetaData = metaData.GetFormatMetaData(PngFormat.Instance); this.currentStream = stream; this.currentStream.Skip(8); Image <TPixel> image = null; try { while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk)) { try { switch (chunk.Type) { case PngChunkType.Header: this.ReadHeaderChunk(pngMetaData, chunk.Data.Array); break; case PngChunkType.Physical: this.ReadPhysicalChunk(metaData, chunk.Data.GetSpan()); break; case PngChunkType.Gamma: this.ReadGammaChunk(pngMetaData, chunk.Data.GetSpan()); break; case PngChunkType.Data: if (image is null) { this.InitializeImage(metaData, out image); } using (var deframeStream = new ZlibInflateStream(this.currentStream, this.ReadNextDataChunk)) { deframeStream.AllocateNewBytes(chunk.Length); this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame, pngMetaData); } break; case PngChunkType.Palette: byte[] pal = new byte[chunk.Length]; Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length); this.palette = pal; break; case PngChunkType.Transparency: byte[] alpha = new byte[chunk.Length]; Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length); this.paletteAlpha = alpha; this.AssignTransparentMarkers(alpha, pngMetaData); break; case PngChunkType.Text: this.ReadTextChunk(metaData, chunk.Data.Array.AsSpan(0, chunk.Length)); break; case PngChunkType.Exif: if (!this.ignoreMetadata) { byte[] exifData = new byte[chunk.Length]; Buffer.BlockCopy(chunk.Data.Array, 0, exifData, 0, chunk.Length); metaData.ExifProfile = new ExifProfile(exifData); } break; case PngChunkType.End: this.isEndChunkReached = true; break; } } finally { chunk.Data?.Dispose(); // Data is rented in ReadChunkData() } } if (image is null) { throw new ImageFormatException("PNG Image does not contain a data chunk"); } return(image); } finally { this.scanline?.Dispose(); this.previousScanline?.Dispose(); } }
private void ReadScanlines(MemoryStream dataStream, byte[] pixels, IColorReader colorReader, PngColorTypeInformation colorTypeInformation) { dataStream.Position = 0; int scanlineLength = CalculateScanlineLength(colorTypeInformation); int scanlineStep = CalculateScanlineStep(colorTypeInformation); byte[] lastScanline = new byte[scanlineLength]; byte[] currScanline = new byte[scanlineLength]; byte a = 0; byte b = 0; byte c = 0; int row = 0, filter = 0, column = -1; using (var compressedStream = new ZlibInflateStream(dataStream)) { int readByte = 0; while ((readByte = compressedStream.ReadByte()) >= 0) { if (column == -1) { filter = readByte; column++; } else { currScanline[column] = (byte)readByte; if (column >= scanlineStep) { a = currScanline[column - scanlineStep]; c = lastScanline[column - scanlineStep]; } else { a = 0; c = 0; } b = lastScanline[column]; if (filter == 1) { currScanline[column] = (byte)(currScanline[column] + a); } else if (filter == 2) { currScanline[column] = (byte)(currScanline[column] + b); } else if (filter == 3) { currScanline[column] = (byte)(currScanline[column] + (byte)((a + b) / 2)); } else if (filter == 4) { currScanline[column] = (byte)(currScanline[column] + PaethPredicator(a, b, c)); } column++; if (column == scanlineLength) { colorReader.ReadScanline(currScanline, pixels, _header); column = -1; row++; var tmp = currScanline; currScanline = lastScanline; lastScanline = tmp; } } } } }
/// <summary> /// Decodes the stream to the image. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="stream">The stream containing image data. </param> /// <exception cref="ImageFormatException"> /// Thrown if the stream does not contain and end chunk. /// </exception> /// <exception cref="ArgumentOutOfRangeException"> /// Thrown if the image is larger than the maximum allowable size. /// </exception> /// <returns>The decoded image</returns> public Image <TPixel> Decode <TPixel>(Stream stream) where TPixel : struct, IPixel <TPixel> { var metadata = new ImageMetaData(); this.currentStream = stream; this.currentStream.Skip(8); Image <TPixel> image = null; try { using (var deframeStream = new ZlibInflateStream(this.currentStream)) { PngChunk currentChunk; while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null) { try { switch (currentChunk.Type) { case PngChunkTypes.Header: this.ReadHeaderChunk(currentChunk.Data); this.ValidateHeader(); break; case PngChunkTypes.Physical: this.ReadPhysicalChunk(metadata, currentChunk.Data); break; case PngChunkTypes.Data: if (image == null) { this.InitializeImage(metadata, out image); } deframeStream.AllocateNewBytes(currentChunk.Length); this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame); stream.Read(this.crcBuffer, 0, 4); break; case PngChunkTypes.Palette: byte[] pal = new byte[currentChunk.Length]; Buffer.BlockCopy(currentChunk.Data, 0, pal, 0, currentChunk.Length); this.palette = pal; break; case PngChunkTypes.PaletteAlpha: byte[] alpha = new byte[currentChunk.Length]; Buffer.BlockCopy(currentChunk.Data, 0, alpha, 0, currentChunk.Length); this.paletteAlpha = alpha; this.AssignTransparentMarkers(alpha); break; case PngChunkTypes.Text: this.ReadTextChunk(metadata, currentChunk.Data, currentChunk.Length); break; case PngChunkTypes.End: this.isEndChunkReached = true; break; } } finally { // Data is rented in ReadChunkData() if (currentChunk.Data != null) { ArrayPool <byte> .Shared.Return(currentChunk.Data); } } } } return(image); } finally { this.scanline?.Dispose(); this.previousScanline?.Dispose(); } }