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 | }
|
---|