/// <summary> /// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param> /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param> public void Encode <TPixel>(Image <TPixel> image, Stream stream) where TPixel : struct, IPixel <TPixel> { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); this.width = image.Width; this.height = image.Height; ImageMetadata metadata = image.Metadata; PngMetadata pngMetadata = metadata.GetPngMetadata(); PngEncoderOptionsHelpers.AdjustOptions <TPixel>(this.options, pngMetadata, out this.use16Bit, out this.bytesPerPixel); IQuantizedFrame <TPixel> quantized = PngEncoderOptionsHelpers.CreateQuantizedFrame(this.options, image); this.bitDepth = PngEncoderOptionsHelpers.CalculateBitDepth(this.options, image, quantized); stream.Write(PngConstants.HeaderBytes, 0, PngConstants.HeaderBytes.Length); this.WriteHeaderChunk(stream); this.WritePaletteChunk(stream, quantized); this.WriteTransparencyChunk(stream, pngMetadata); this.WritePhysicalChunk(stream, metadata); this.WriteGammaChunk(stream); this.WriteExifChunk(stream, metadata); this.WriteTextChunks(stream, pngMetadata); this.WriteDataChunks(image.Frames.RootFrame, quantized, stream); this.WriteEndChunk(stream); stream.Flush(); quantized?.Dispose(); }
/// <summary> /// Reads the raw image information from the specified stream. /// </summary> /// <param name="stream">The <see cref="Stream"/> containing image data.</param> public IImageInfo Identify(Stream stream) { var metadata = new ImageMetadata(); PngMetadata pngMetadata = metadata.GetPngMetadata(); this.currentStream = stream; this.currentStream.Skip(8); 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: this.SkipChunkDataAndCrc(chunk); break; case PngChunkType.Text: this.ReadTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length)); break; case PngChunkType.End: this.isEndChunkReached = true; break; } } finally { chunk.Data?.Dispose(); // Data is rented in ReadChunkData() } } } finally { this.scanline?.Dispose(); this.previousScanline?.Dispose(); } if (this.header.Width == 0 && this.header.Height == 0) { PngThrowHelper.ThrowNoHeader(); } return(new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata)); }
/// <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 : unmanaged, IPixel <TPixel> { var metadata = new ImageMetadata(); PngMetadata pngMetadata = metadata.GetPngMetadata(); 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); } this.ReadScanlines(chunk, image.Frames.RootFrame, pngMetadata); break; case PngChunkType.Palette: var pal = new byte[chunk.Length]; Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length); this.palette = pal; break; case PngChunkType.Transparency: var 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(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length)); break; case PngChunkType.CompressedText: this.ReadCompressedTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length)); break; case PngChunkType.InternationalText: this.ReadInternationalTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length)); break; case PngChunkType.Exif: if (!this.ignoreMetadata) { var 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) { PngThrowHelper.ThrowNoData(); } return(image); } finally { this.scanline?.Dispose(); this.previousScanline?.Dispose(); } }
/// <inheritdoc/> public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { var metadata = new ImageMetadata(); PngMetadata pngMetadata = metadata.GetPngMetadata(); this.currentStream = stream; this.currentStream.Skip(8); 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: this.SkipChunkDataAndCrc(chunk); break; case PngChunkType.Text: this.ReadTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length)); break; case PngChunkType.CompressedText: this.ReadCompressedTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length)); break; case PngChunkType.InternationalText: this.ReadInternationalTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length)); break; case PngChunkType.Exif: if (!this.ignoreMetadata) { var 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() } } } finally { this.scanline?.Dispose(); this.previousScanline?.Dispose(); } if (this.header.Width == 0 && this.header.Height == 0) { PngThrowHelper.ThrowNoHeader(); } return(new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), this.header.Width, this.header.Height, metadata)); }