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