| 1 | using System; | 
 
 
 
 
 | 2 | using System.IO; | 
 
 
 
 
 | 3 |  | 
 
 
 
 
 | 4 | namespace Oni.Imaging | 
 
 
 
 
 | 5 | { | 
 
 
 
 
 | 6 | internal class TgaHeader | 
 
 
 
 
 | 7 | { | 
 
 
 
 
 | 8 | #region Private data | 
 
 
 
 
 | 9 | private bool hasColorMap; | 
 
 
 
 
 | 10 | private TgaImageType imageType; | 
 
 
 
 
 | 11 | private int colorMapIndex; | 
 
 
 
 
 | 12 | private int colorMapLength; | 
 
 
 
 
 | 13 | private int colorMapEntrySize; | 
 
 
 
 
 | 14 | private int width; | 
 
 
 
 
 | 15 | private int height; | 
 
 
 
 
 | 16 | private int pixelDepth; | 
 
 
 
 
 | 17 | private int imageDescriptor; | 
 
 
 
 
 | 18 | private bool xFlip; | 
 
 
 
 
 | 19 | private bool yFlip; | 
 
 
 
 
 | 20 | private bool hasAlpha; | 
 
 
 
 
 | 21 | #endregion | 
 
 
 
 
 | 22 |  | 
 
 
 
 
 | 23 | public TgaImageType ImageType => imageType; | 
 
 
 
 
 | 24 | public int Width => width; | 
 
 
 
 
 | 25 | public int Height => height; | 
 
 
 
 
 | 26 | public int PixelSize => pixelDepth / 8; | 
 
 
 
 
 | 27 | public bool XFlip => xFlip; | 
 
 
 
 
 | 28 | public bool YFlip => yFlip; | 
 
 
 
 
 | 29 |  | 
 
 
 
 
 | 30 | public static TgaHeader Read(BinaryReader reader) | 
 
 
 
 
 | 31 | { | 
 
 
 
 
 | 32 | int idLength = reader.ReadByte(); | 
 
 
 
 
 | 33 |  | 
 
 
 
 
 | 34 | var header = new TgaHeader(); | 
 
 
 
 
 | 35 | header.hasColorMap = (reader.ReadByte() != 0); | 
 
 
 
 
 | 36 | header.imageType = (TgaImageType)reader.ReadByte(); | 
 
 
 
 
 | 37 | header.colorMapIndex = reader.ReadUInt16(); | 
 
 
 
 
 | 38 | header.colorMapLength = reader.ReadUInt16(); | 
 
 
 
 
 | 39 | header.colorMapEntrySize = reader.ReadByte(); | 
 
 
 
 
 | 40 | reader.ReadUInt16(); // x origin | 
 
 
 
 
 | 41 | reader.ReadUInt16(); // y origin | 
 
 
 
 
 | 42 | header.width = reader.ReadUInt16(); | 
 
 
 
 
 | 43 | header.height = reader.ReadUInt16(); | 
 
 
 
 
 | 44 | header.pixelDepth = reader.ReadByte(); | 
 
 
 
 
 | 45 | header.imageDescriptor = reader.ReadByte(); | 
 
 
 
 
 | 46 |  | 
 
 
 
 
 | 47 | if (!Enum.IsDefined(typeof(TgaImageType), header.ImageType) || header.ImageType == TgaImageType.None) | 
 
 
 
 
 | 48 | throw new NotSupportedException(string.Format("Unsupported TGA image type {0}", header.ImageType)); | 
 
 
 
 
 | 49 |  | 
 
 
 
 
 | 50 | if (header.Width == 0 || header.Height == 0) | 
 
 
 
 
 | 51 | throw new InvalidDataException("Invalid TGA file"); | 
 
 
 
 
 | 52 |  | 
 
 
 
 
 | 53 | if (header.ImageType == TgaImageType.TrueColor | 
 
 
 
 
 | 54 | && (header.pixelDepth != 16 | 
 
 
 
 
 | 55 | && header.pixelDepth != 24 | 
 
 
 
 
 | 56 | && header.pixelDepth != 32)) | 
 
 
 
 
 | 57 | { | 
 
 
 
 
 | 58 | throw new InvalidDataException(string.Format("Invalid true color pixel depth {0}", header.pixelDepth)); | 
 
 
 
 
 | 59 | } | 
 
 
 
 
 | 60 |  | 
 
 
 
 
 | 61 | if (header.hasColorMap) | 
 
 
 
 
 | 62 | { | 
 
 
 
 
 | 63 | if (header.colorMapEntrySize != 16 | 
 
 
 
 
 | 64 | && header.colorMapEntrySize != 24 | 
 
 
 
 
 | 65 | && header.colorMapEntrySize != 32) | 
 
 
 
 
 | 66 | { | 
 
 
 
 
 | 67 | throw new InvalidDataException(string.Format("Invalid color map entry size {0}", header.colorMapEntrySize)); | 
 
 
 
 
 | 68 | } | 
 
 
 
 
 | 69 |  | 
 
 
 
 
 | 70 | if (header.ImageType != TgaImageType.ColorMapped | 
 
 
 
 
 | 71 | && header.ImageType != TgaImageType.RleColorMapped) | 
 
 
 
 
 | 72 | { | 
 
 
 
 
 | 73 | // | 
 
 
 
 
 | 74 | // We have a color map but the image type does not use it so we'll just skip it. | 
 
 
 
 
 | 75 | // | 
 
 
 
 
 | 76 |  | 
 
 
 
 
 | 77 | reader.Position += header.colorMapLength * header.colorMapEntrySize / 8; | 
 
 
 
 
 | 78 | } | 
 
 
 
 
 | 79 | } | 
 
 
 
 
 | 80 |  | 
 
 
 
 
 | 81 | // | 
 
 
 
 
 | 82 | // Skip the identification field because we don't need it. | 
 
 
 
 
 | 83 | // | 
 
 
 
 
 | 84 |  | 
 
 
 
 
 | 85 | reader.Position += idLength; | 
 
 
 
 
 | 86 |  | 
 
 
 
 
 | 87 | if (header.pixelDepth == 32) | 
 
 
 
 
 | 88 | header.hasAlpha = ((header.imageDescriptor & 0x0f) == 8); | 
 
 
 
 
 | 89 | else if (header.pixelDepth == 16) | 
 
 
 
 
 | 90 | header.hasAlpha = ((header.imageDescriptor & 0x0f) == 1); | 
 
 
 
 
 | 91 | else | 
 
 
 
 
 | 92 | header.hasAlpha = false; | 
 
 
 
 
 | 93 |  | 
 
 
 
 
 | 94 | header.xFlip = ((header.imageDescriptor & 16) == 16); | 
 
 
 
 
 | 95 | header.yFlip = ((header.imageDescriptor & 32) == 32); | 
 
 
 
 
 | 96 |  | 
 
 
 
 
 | 97 | return header; | 
 
 
 
 
 | 98 | } | 
 
 
 
 
 | 99 |  | 
 
 
 
 
 | 100 | public static TgaHeader Create(int width, int height, TgaImageType imageType) | 
 
 
 
 
 | 101 | { | 
 
 
 
 
 | 102 | return new TgaHeader { | 
 
 
 
 
 | 103 | imageType = imageType, | 
 
 
 
 
 | 104 | width = width, | 
 
 
 
 
 | 105 | height = height, | 
 
 
 
 
 | 106 | pixelDepth = 32, | 
 
 
 
 
 | 107 | imageDescriptor = 8 | 
 
 
 
 
 | 108 | }; | 
 
 
 
 
 | 109 | } | 
 
 
 
 
 | 110 |  | 
 
 
 
 
 | 111 | public void Write(BinaryWriter writer) | 
 
 
 
 
 | 112 | { | 
 
 
 
 
 | 113 | writer.Write((byte)0); | 
 
 
 
 
 | 114 | writer.Write((byte)(hasColorMap ? 1 : 0)); | 
 
 
 
 
 | 115 | writer.Write((byte)imageType); | 
 
 
 
 
 | 116 | writer.Write((ushort)colorMapIndex); | 
 
 
 
 
 | 117 | writer.Write((ushort)colorMapLength); | 
 
 
 
 
 | 118 | writer.Write((byte)colorMapEntrySize); | 
 
 
 
 
 | 119 | writer.Write((ushort)0); | 
 
 
 
 
 | 120 | writer.Write((ushort)0); | 
 
 
 
 
 | 121 | writer.Write((ushort)width); | 
 
 
 
 
 | 122 | writer.Write((ushort)height); | 
 
 
 
 
 | 123 | writer.Write((byte)pixelDepth); | 
 
 
 
 
 | 124 | writer.Write((byte)imageDescriptor); | 
 
 
 
 
 | 125 | } | 
 
 
 
 
 | 126 |  | 
 
 
 
 
 | 127 | public SurfaceFormat GetSurfaceFormat() | 
 
 
 
 
 | 128 | { | 
 
 
 
 
 | 129 | switch (pixelDepth) | 
 
 
 
 
 | 130 | { | 
 
 
 
 
 | 131 | case 16: | 
 
 
 
 
 | 132 | return hasAlpha ? SurfaceFormat.BGRA5551 : SurfaceFormat.BGRX5551; | 
 
 
 
 
 | 133 | case 24: | 
 
 
 
 
 | 134 | return SurfaceFormat.BGRX; | 
 
 
 
 
 | 135 | default: | 
 
 
 
 
 | 136 | case 32: | 
 
 
 
 
 | 137 | return hasAlpha ? SurfaceFormat.BGRA : SurfaceFormat.BGRX; | 
 
 
 
 
 | 138 | } | 
 
 
 
 
 | 139 | } | 
 
 
 
 
 | 140 |  | 
 
 
 
 
 | 141 | public Color GetPixel(byte[] src, int srcOffset) | 
 
 
 
 
 | 142 | { | 
 
 
 
 
 | 143 | switch (pixelDepth) | 
 
 
 
 
 | 144 | { | 
 
 
 
 
 | 145 | case 16: | 
 
 
 
 
 | 146 | if (hasAlpha) | 
 
 
 
 
 | 147 | return Color.ReadBgra5551(src, srcOffset); | 
 
 
 
 
 | 148 | else | 
 
 
 
 
 | 149 | return Color.ReadBgrx5551(src, srcOffset); | 
 
 
 
 
 | 150 |  | 
 
 
 
 
 | 151 | case 24: | 
 
 
 
 
 | 152 | return Color.ReadBgrx(src, srcOffset); | 
 
 
 
 
 | 153 |  | 
 
 
 
 
 | 154 | default: | 
 
 
 
 
 | 155 | case 32: | 
 
 
 
 
 | 156 | if (hasAlpha) | 
 
 
 
 
 | 157 | return Color.ReadBgra(src, srcOffset); | 
 
 
 
 
 | 158 | else | 
 
 
 
 
 | 159 | return Color.ReadBgrx(src, srcOffset); | 
 
 
 
 
 | 160 | } | 
 
 
 
 
 | 161 | } | 
 
 
 
 
 | 162 | } | 
 
 
 
 
 | 163 | } |