using System; using System.IO; namespace Oni.Imaging { internal class TgaHeader { #region Private data private bool hasColorMap; private TgaImageType imageType; private int colorMapIndex; private int colorMapLength; private int colorMapEntrySize; private int width; private int height; private int pixelDepth; private int imageDescriptor; private bool xFlip; private bool yFlip; private bool hasAlpha; #endregion public TgaImageType ImageType => imageType; public int Width => width; public int Height => height; public int PixelSize => pixelDepth / 8; public bool XFlip => xFlip; public bool YFlip => yFlip; public static TgaHeader Read(BinaryReader reader) { int idLength = reader.ReadByte(); var header = new TgaHeader(); header.hasColorMap = (reader.ReadByte() != 0); header.imageType = (TgaImageType)reader.ReadByte(); header.colorMapIndex = reader.ReadUInt16(); header.colorMapLength = reader.ReadUInt16(); header.colorMapEntrySize = reader.ReadByte(); reader.ReadUInt16(); // x origin reader.ReadUInt16(); // y origin header.width = reader.ReadUInt16(); header.height = reader.ReadUInt16(); header.pixelDepth = reader.ReadByte(); header.imageDescriptor = reader.ReadByte(); if (!Enum.IsDefined(typeof(TgaImageType), header.ImageType) || header.ImageType == TgaImageType.None) throw new NotSupportedException(string.Format("Unsupported TGA image type {0}", header.ImageType)); if (header.Width == 0 || header.Height == 0) throw new InvalidDataException("Invalid TGA file"); if (header.ImageType == TgaImageType.TrueColor && (header.pixelDepth != 16 && header.pixelDepth != 24 && header.pixelDepth != 32)) { throw new InvalidDataException(string.Format("Invalid true color pixel depth {0}", header.pixelDepth)); } if (header.hasColorMap) { if (header.colorMapEntrySize != 16 && header.colorMapEntrySize != 24 && header.colorMapEntrySize != 32) { throw new InvalidDataException(string.Format("Invalid color map entry size {0}", header.colorMapEntrySize)); } if (header.ImageType != TgaImageType.ColorMapped && header.ImageType != TgaImageType.RleColorMapped) { // // We have a color map but the image type does not use it so we'll just skip it. // reader.Position += header.colorMapLength * header.colorMapEntrySize / 8; } } // // Skip the identification field because we don't need it. // reader.Position += idLength; if (header.pixelDepth == 32) header.hasAlpha = ((header.imageDescriptor & 0x0f) == 8); else if (header.pixelDepth == 16) header.hasAlpha = ((header.imageDescriptor & 0x0f) == 1); else header.hasAlpha = false; header.xFlip = ((header.imageDescriptor & 16) == 16); header.yFlip = ((header.imageDescriptor & 32) == 32); return header; } public static TgaHeader Create(int width, int height, TgaImageType imageType) { return new TgaHeader { imageType = imageType, width = width, height = height, pixelDepth = 32, imageDescriptor = 8 }; } public void Write(BinaryWriter writer) { writer.Write((byte)0); writer.Write((byte)(hasColorMap ? 1 : 0)); writer.Write((byte)imageType); writer.Write((ushort)colorMapIndex); writer.Write((ushort)colorMapLength); writer.Write((byte)colorMapEntrySize); writer.Write((ushort)0); writer.Write((ushort)0); writer.Write((ushort)width); writer.Write((ushort)height); writer.Write((byte)pixelDepth); writer.Write((byte)imageDescriptor); } public SurfaceFormat GetSurfaceFormat() { switch (pixelDepth) { case 16: return hasAlpha ? SurfaceFormat.BGRA5551 : SurfaceFormat.BGRX5551; case 24: return SurfaceFormat.BGRX; default: case 32: return hasAlpha ? SurfaceFormat.BGRA : SurfaceFormat.BGRX; } } public Color GetPixel(byte[] src, int srcOffset) { switch (pixelDepth) { case 16: if (hasAlpha) return Color.ReadBgra5551(src, srcOffset); else return Color.ReadBgrx5551(src, srcOffset); case 24: return Color.ReadBgrx(src, srcOffset); default: case 32: if (hasAlpha) return Color.ReadBgra(src, srcOffset); else return Color.ReadBgrx(src, srcOffset); } } } }