/// <summary> /// Reads the tga file header from the stream. /// </summary> /// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <returns>The image origin.</returns> private TgaImageOrigin ReadFileHeader(Stream stream) { this.currentStream = stream; Span <byte> buffer = stackalloc byte[TgaFileHeader.Size]; this.currentStream.Read(buffer, 0, TgaFileHeader.Size); this.fileHeader = TgaFileHeader.Parse(buffer); this.metadata = new ImageMetadata(); this.tgaMetadata = this.metadata.GetTgaMetadata(); this.tgaMetadata.BitsPerPixel = (TgaBitsPerPixel)this.fileHeader.PixelDepth; var alphaBits = this.fileHeader.ImageDescriptor & 0xf; if (alphaBits != 0 && alphaBits != 1 && alphaBits != 8) { TgaThrowHelper.ThrowImageFormatException("Invalid alpha channel bits"); } this.tgaMetadata.AlphaChannelBits = (byte)alphaBits; this.hasAlpha = alphaBits > 0; // Bits 4 and 5 describe the image origin. var origin = (TgaImageOrigin)((this.fileHeader.ImageDescriptor & 0x30) >> 4); return(origin); }
/// <summary> /// Reads the tga file header from the stream. /// </summary> /// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <returns>true, if the image origin is top left.</returns> private bool ReadFileHeader(Stream stream) { this.currentStream = stream; Span <byte> buffer = stackalloc byte[TgaFileHeader.Size]; this.currentStream.Read(buffer, 0, TgaFileHeader.Size); this.fileHeader = TgaFileHeader.Parse(buffer); this.metadata = new ImageMetadata(); this.tgaMetadata = this.metadata.GetTgaMetadata(); this.tgaMetadata.BitsPerPixel = (TgaBitsPerPixel)this.fileHeader.PixelDepth; var alphaBits = this.fileHeader.ImageDescriptor & 0xf; if (alphaBits != 0 && alphaBits != 1 && alphaBits != 8) { TgaThrowHelper.ThrowImageFormatException("Invalid alpha channel bits"); } this.tgaMetadata.AlphaChannelBits = (byte)alphaBits; this.hasAlpha = alphaBits > 0; // TODO: bits 4 and 5 describe the image origin. See spec page 9. bit 4 is currently ignored. // Theoretically the origin could also be top right and bottom right. // Bit at position 5 of the descriptor indicates, that the origin is top left instead of bottom left. if ((this.fileHeader.ImageDescriptor & (1 << 5)) != 0) { return(true); } return(false); }
/// <summary> /// Encodes the image to the specified stream from the <see cref="ImageFrame{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.configuration = image.GetConfiguration(); ImageMetadata metadata = image.Metadata; TgaMetadata tgaMetadata = metadata.GetFormatMetadata(TgaFormat.Instance); this.bitsPerPixel = this.bitsPerPixel ?? tgaMetadata.BitsPerPixel; TgaImageType imageType = this.compression is TgaCompression.RunLength ? TgaImageType.RleTrueColor : TgaImageType.TrueColor; if (this.bitsPerPixel == TgaBitsPerPixel.Pixel8) { imageType = this.compression is TgaCompression.RunLength ? TgaImageType.RleBlackAndWhite : TgaImageType.BlackAndWhite; } // If compression is used, set bit 5 of the image descriptor to indicate an left top origin. byte imageDescriptor = (byte)(this.compression is TgaCompression.RunLength ? 32 : 0); var fileHeader = new TgaFileHeader( idLength: 0, colorMapType: 0, imageType: imageType, cMapStart: 0, cMapLength: 0, cMapDepth: 0, xOffset: 0, yOffset: this.compression is TgaCompression.RunLength ? (short)image.Height : (short)0, // When run length encoding is used, the origin should be top left instead of the default bottom left. width: (short)image.Width, height: (short)image.Height, pixelDepth: (byte)this.bitsPerPixel.Value, imageDescriptor: imageDescriptor); #if NETCOREAPP2_1 Span <byte> buffer = stackalloc byte[TgaFileHeader.Size]; #else byte[] buffer = new byte[TgaFileHeader.Size]; #endif fileHeader.WriteTo(buffer); stream.Write(buffer, 0, TgaFileHeader.Size); if (this.compression is TgaCompression.RunLength) { this.WriteRunLengthEndcodedImage(stream, image.Frames.RootFrame); } else { this.WriteImage(stream, image.Frames.RootFrame); } stream.Flush(); }
/// <summary> /// Reads the tga file header from the stream. /// </summary> /// <param name="stream">The <see cref="Stream"/> containing image data.</param> /// <returns>true, if the image origin is top left.</returns> private bool ReadFileHeader(Stream stream) { this.currentStream = stream; Span <byte> buffer = stackalloc byte[TgaFileHeader.Size]; this.currentStream.Read(buffer, 0, TgaFileHeader.Size); this.fileHeader = TgaFileHeader.Parse(buffer); this.metadata = new ImageMetadata(); this.tgaMetadata = this.metadata.GetTgaMetadata(); this.tgaMetadata.BitsPerPixel = (TgaBitsPerPixel)this.fileHeader.PixelDepth; // Bit at position 5 of the descriptor indicates, that the origin is top left instead of bottom right. if ((this.fileHeader.ImageDescriptor & (1 << 5)) != 0) { return(true); } return(false); }
/// <summary> /// Encodes the image to the specified stream from the <see cref="ImageFrame{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 : unmanaged, IPixel <TPixel> { Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); this.configuration = image.GetConfiguration(); ImageMetadata metadata = image.Metadata; TgaMetadata tgaMetadata = metadata.GetTgaMetadata(); this.bitsPerPixel = this.bitsPerPixel ?? tgaMetadata.BitsPerPixel; TgaImageType imageType = this.compression is TgaCompression.RunLength ? TgaImageType.RleTrueColor : TgaImageType.TrueColor; if (this.bitsPerPixel == TgaBitsPerPixel.Pixel8) { imageType = this.compression is TgaCompression.RunLength ? TgaImageType.RleBlackAndWhite : TgaImageType.BlackAndWhite; } byte imageDescriptor = 0; if (this.compression is TgaCompression.RunLength) { // If compression is used, set bit 5 of the image descriptor to indicate a left top origin. imageDescriptor |= 0x20; } if (this.bitsPerPixel is TgaBitsPerPixel.Pixel32) { // Indicate, that 8 bit are used for the alpha channel. imageDescriptor |= 0x8; } if (this.bitsPerPixel is TgaBitsPerPixel.Pixel16) { // Indicate, that 1 bit is used for the alpha channel. imageDescriptor |= 0x1; } var fileHeader = new TgaFileHeader( idLength: 0, colorMapType: 0, imageType: imageType, cMapStart: 0, cMapLength: 0, cMapDepth: 0, xOffset: 0, yOffset: this.compression is TgaCompression.RunLength ? (short)image.Height : (short)0, // When run length encoding is used, the origin should be top left instead of the default bottom left. width: (short)image.Width, height: (short)image.Height, pixelDepth: (byte)this.bitsPerPixel.Value, imageDescriptor: imageDescriptor); Span <byte> buffer = stackalloc byte[TgaFileHeader.Size]; fileHeader.WriteTo(buffer); stream.Write(buffer, 0, TgaFileHeader.Size); if (this.compression is TgaCompression.RunLength) { this.WriteRunLengthEncodedImage(stream, image.Frames.RootFrame); } else { this.WriteImage(stream, image.Frames.RootFrame); } stream.Flush(); }