1 | using System;
|
---|
2 | using System.Collections.Generic;
|
---|
3 | using System.IO;
|
---|
4 |
|
---|
5 | namespace Oni.Imaging
|
---|
6 | {
|
---|
7 | internal class DdsHeader
|
---|
8 | {
|
---|
9 | #region Private data
|
---|
10 |
|
---|
11 | private enum FOURCC
|
---|
12 | {
|
---|
13 | FOURCC_NONE = 0,
|
---|
14 | FOURCC_DXT1 = 0x31545844
|
---|
15 | }
|
---|
16 |
|
---|
17 | [Flags]
|
---|
18 | private enum DDS_FLAGS
|
---|
19 | {
|
---|
20 | DDSD_CAPS = 0x00000001,
|
---|
21 | DDSD_HEIGHT = 0x00000002,
|
---|
22 | DDSD_WIDTH = 0x00000004,
|
---|
23 | DDSD_PITCH = 0x00000008,
|
---|
24 | DDSD_PIXELFORMAT = 0x00001000,
|
---|
25 | DDSD_MIPMAPCOUNT = 0x00020000,
|
---|
26 | DDSD_LINEARSIZE = 0x00080000,
|
---|
27 | DDSD_DEPTH = 0x00800000
|
---|
28 | }
|
---|
29 |
|
---|
30 | [Flags]
|
---|
31 | private enum DDP_FLAGS
|
---|
32 | {
|
---|
33 | DDPF_RGB = 0x00000040,
|
---|
34 | DDPF_FOURCC = 0x00000004,
|
---|
35 | DDPF_ALPHAPIXELS = 0x00000001
|
---|
36 | }
|
---|
37 |
|
---|
38 | [Flags]
|
---|
39 | private enum DDS_CAPS
|
---|
40 | {
|
---|
41 | DDSCAPS_TEXTURE = 0x00001000,
|
---|
42 | DDSCAPS_MIPMAP = 0x00400000,
|
---|
43 | DDSCAPS_COMPLEX = 0x00000008
|
---|
44 | }
|
---|
45 |
|
---|
46 | [Flags]
|
---|
47 | private enum DDS_CAPS2
|
---|
48 | {
|
---|
49 | DDSCAPS2_CUBEMAP = 0x00000200,
|
---|
50 | DDSCAPS2_VOLUME = 0x00200000
|
---|
51 | }
|
---|
52 |
|
---|
53 | private const int DDS_MAGIC = 0x20534444;
|
---|
54 |
|
---|
55 | private DDS_FLAGS flags;
|
---|
56 | private int height;
|
---|
57 | private int width;
|
---|
58 | private int linearSize;
|
---|
59 | private int depth;
|
---|
60 | private int mipmapCount;
|
---|
61 | private DDP_FLAGS formatFlags;
|
---|
62 | private FOURCC fourCC;
|
---|
63 | private int rgbBitCount;
|
---|
64 | private uint rBitMask;
|
---|
65 | private uint gBitMask;
|
---|
66 | private uint bBitMask;
|
---|
67 | private uint aBitMask;
|
---|
68 | private DDS_CAPS caps;
|
---|
69 | private DDS_CAPS2 caps2;
|
---|
70 | #endregion
|
---|
71 |
|
---|
72 | public int Width => width;
|
---|
73 | public int Height => height;
|
---|
74 | public int MipmapCount => mipmapCount;
|
---|
75 |
|
---|
76 | public SurfaceFormat GetSurfaceFormat()
|
---|
77 | {
|
---|
78 | if (fourCC == FOURCC.FOURCC_DXT1)
|
---|
79 | return SurfaceFormat.DXT1;
|
---|
80 |
|
---|
81 | if (rgbBitCount == 32)
|
---|
82 | {
|
---|
83 | if (rBitMask == 0x00ff0000 && gBitMask == 0x0000ff00 && bBitMask == 0x000000ff)
|
---|
84 | {
|
---|
85 | if ((formatFlags & DDP_FLAGS.DDPF_ALPHAPIXELS) == 0)
|
---|
86 | return SurfaceFormat.BGRX;
|
---|
87 |
|
---|
88 | if (aBitMask == 0xff000000)
|
---|
89 | return SurfaceFormat.BGRA;
|
---|
90 | }
|
---|
91 | }
|
---|
92 | else if (rgbBitCount == 16)
|
---|
93 | {
|
---|
94 | if (rBitMask == 0x7c00 && gBitMask == 0x03e0 && bBitMask == 0x001f)
|
---|
95 | {
|
---|
96 | if ((formatFlags & DDP_FLAGS.DDPF_ALPHAPIXELS) == 0)
|
---|
97 | return SurfaceFormat.BGRX5551;
|
---|
98 |
|
---|
99 | if (aBitMask == 0x8000)
|
---|
100 | return SurfaceFormat.BGRA5551;
|
---|
101 | }
|
---|
102 | else if (rBitMask == 0x0f00 && gBitMask == 0x00f0 && bBitMask == 0x000f)
|
---|
103 | {
|
---|
104 | if ((formatFlags & DDP_FLAGS.DDPF_ALPHAPIXELS) != 0)
|
---|
105 | return SurfaceFormat.BGRA4444;
|
---|
106 | }
|
---|
107 | }
|
---|
108 |
|
---|
109 | throw new NotSupportedException(string.Format("Unsupported pixel format {0} {1} {2} {3} {4} {5} {6}",
|
---|
110 | formatFlags,
|
---|
111 | fourCC, rgbBitCount,
|
---|
112 | rBitMask, gBitMask, bBitMask, aBitMask));
|
---|
113 | }
|
---|
114 |
|
---|
115 | public static DdsHeader Read(BinaryReader reader)
|
---|
116 | {
|
---|
117 | if (reader.ReadInt32() != DDS_MAGIC)
|
---|
118 | throw new InvalidDataException("Not a DDS file");
|
---|
119 |
|
---|
120 | var header = new DdsHeader();
|
---|
121 |
|
---|
122 | if (reader.ReadInt32() != 124)
|
---|
123 | throw new InvalidDataException("Invalid DDS header size");
|
---|
124 |
|
---|
125 | header.flags = (DDS_FLAGS)reader.ReadInt32();
|
---|
126 |
|
---|
127 | var requiredFlags = DDS_FLAGS.DDSD_CAPS | DDS_FLAGS.DDSD_HEIGHT | DDS_FLAGS.DDSD_WIDTH | DDS_FLAGS.DDSD_PIXELFORMAT;
|
---|
128 |
|
---|
129 | if ((header.flags & requiredFlags) != requiredFlags)
|
---|
130 | throw new InvalidDataException(string.Format("Invalid DDS header flags ({0})", header.flags));
|
---|
131 |
|
---|
132 | header.height = reader.ReadInt32();
|
---|
133 | header.width = reader.ReadInt32();
|
---|
134 |
|
---|
135 | if (header.width == 0 || header.height == 0)
|
---|
136 | throw new InvalidDataException("DDS file has 0 width or height");
|
---|
137 |
|
---|
138 | header.linearSize = reader.ReadInt32();
|
---|
139 | header.depth = reader.ReadInt32();
|
---|
140 |
|
---|
141 | if ((header.flags & DDS_FLAGS.DDSD_MIPMAPCOUNT) != 0)
|
---|
142 | {
|
---|
143 | header.mipmapCount = reader.ReadInt32();
|
---|
144 | }
|
---|
145 | else
|
---|
146 | {
|
---|
147 | reader.ReadInt32();
|
---|
148 | header.mipmapCount = 1;
|
---|
149 | }
|
---|
150 |
|
---|
151 | reader.Position += 44;
|
---|
152 |
|
---|
153 | if (reader.ReadInt32() != 32)
|
---|
154 | throw new InvalidDataException("Invalid DDS pixel format size");
|
---|
155 |
|
---|
156 | header.formatFlags = (DDP_FLAGS)reader.ReadInt32();
|
---|
157 |
|
---|
158 | if ((header.formatFlags & DDP_FLAGS.DDPF_FOURCC) != 0)
|
---|
159 | {
|
---|
160 | header.fourCC = (FOURCC)reader.ReadInt32();
|
---|
161 | }
|
---|
162 | else
|
---|
163 | {
|
---|
164 | reader.ReadInt32();
|
---|
165 | header.fourCC = FOURCC.FOURCC_NONE;
|
---|
166 | }
|
---|
167 |
|
---|
168 | header.rgbBitCount = reader.ReadInt32();
|
---|
169 | header.rBitMask = reader.ReadUInt32();
|
---|
170 | header.gBitMask = reader.ReadUInt32();
|
---|
171 | header.bBitMask = reader.ReadUInt32();
|
---|
172 | header.aBitMask = reader.ReadUInt32();
|
---|
173 |
|
---|
174 | header.caps = (DDS_CAPS)reader.ReadInt32();
|
---|
175 | header.caps2 = (DDS_CAPS2)reader.ReadInt32();
|
---|
176 |
|
---|
177 | reader.Position += 12;
|
---|
178 |
|
---|
179 | if (header.fourCC == FOURCC.FOURCC_NONE)
|
---|
180 | {
|
---|
181 | if (header.rgbBitCount != 16 && header.rgbBitCount != 32)
|
---|
182 | throw new NotSupportedException(string.Format("Unsupported RGB bit count {0}", header.rgbBitCount));
|
---|
183 | }
|
---|
184 | else if (header.fourCC != FOURCC.FOURCC_DXT1)
|
---|
185 | {
|
---|
186 | throw new NotSupportedException(string.Format("Unsupported FOURCC {0}", header.fourCC));
|
---|
187 | }
|
---|
188 |
|
---|
189 | return header;
|
---|
190 | }
|
---|
191 |
|
---|
192 | public static DdsHeader Create(IList<Surface> surfaces)
|
---|
193 | {
|
---|
194 | var header = new DdsHeader();
|
---|
195 |
|
---|
196 | int width = surfaces[0].Width;
|
---|
197 | int height = surfaces[0].Height;
|
---|
198 | var format = surfaces[0].Format;
|
---|
199 |
|
---|
200 | header.flags = DDS_FLAGS.DDSD_CAPS | DDS_FLAGS.DDSD_HEIGHT | DDS_FLAGS.DDSD_WIDTH | DDS_FLAGS.DDSD_PIXELFORMAT;
|
---|
201 | header.width = width;
|
---|
202 | header.height = height;
|
---|
203 | header.caps = DDS_CAPS.DDSCAPS_TEXTURE;
|
---|
204 |
|
---|
205 | switch (format)
|
---|
206 | {
|
---|
207 | case SurfaceFormat.BGRA4444:
|
---|
208 | header.formatFlags = DDP_FLAGS.DDPF_RGB | DDP_FLAGS.DDPF_ALPHAPIXELS;
|
---|
209 | header.rgbBitCount = 16;
|
---|
210 | header.aBitMask = 0xf000;
|
---|
211 | header.rBitMask = 0x0f00;
|
---|
212 | header.gBitMask = 0x00f0;
|
---|
213 | header.bBitMask = 0x000f;
|
---|
214 | break;
|
---|
215 | case SurfaceFormat.BGRX5551:
|
---|
216 | case SurfaceFormat.BGRA5551:
|
---|
217 | header.formatFlags = DDP_FLAGS.DDPF_RGB | DDP_FLAGS.DDPF_ALPHAPIXELS;
|
---|
218 | header.rgbBitCount = 16;
|
---|
219 | header.aBitMask = 0x8000;
|
---|
220 | header.rBitMask = 0x7c00;
|
---|
221 | header.gBitMask = 0x03e0;
|
---|
222 | header.bBitMask = 0x001f;
|
---|
223 | break;
|
---|
224 | case SurfaceFormat.BGRA:
|
---|
225 | header.formatFlags = DDP_FLAGS.DDPF_RGB | DDP_FLAGS.DDPF_ALPHAPIXELS;
|
---|
226 | header.rgbBitCount = 32;
|
---|
227 | header.aBitMask = 0xff000000;
|
---|
228 | header.rBitMask = 0x00ff0000;
|
---|
229 | header.gBitMask = 0x0000ff00;
|
---|
230 | header.bBitMask = 0x000000ff;
|
---|
231 | break;
|
---|
232 | case SurfaceFormat.BGRX:
|
---|
233 | header.formatFlags = DDP_FLAGS.DDPF_RGB;
|
---|
234 | header.rgbBitCount = 32;
|
---|
235 | header.rBitMask = 0x00ff0000;
|
---|
236 | header.gBitMask = 0x0000ff00;
|
---|
237 | header.bBitMask = 0x000000ff;
|
---|
238 | break;
|
---|
239 | case SurfaceFormat.RGBA:
|
---|
240 | header.formatFlags = DDP_FLAGS.DDPF_RGB | DDP_FLAGS.DDPF_ALPHAPIXELS;
|
---|
241 | header.rgbBitCount = 32;
|
---|
242 | header.aBitMask = 0x000000ff;
|
---|
243 | header.rBitMask = 0x0000ff00;
|
---|
244 | header.gBitMask = 0x00ff0000;
|
---|
245 | header.bBitMask = 0xff000000;
|
---|
246 | break;
|
---|
247 | case SurfaceFormat.RGBX:
|
---|
248 | header.formatFlags = DDP_FLAGS.DDPF_RGB;
|
---|
249 | header.rgbBitCount = 32;
|
---|
250 | header.rBitMask = 0x0000ff00;
|
---|
251 | header.gBitMask = 0x00ff0000;
|
---|
252 | header.bBitMask = 0xff000000;
|
---|
253 | break;
|
---|
254 | case SurfaceFormat.DXT1:
|
---|
255 | header.formatFlags = DDP_FLAGS.DDPF_FOURCC;
|
---|
256 | header.fourCC = FOURCC.FOURCC_DXT1;
|
---|
257 | break;
|
---|
258 | }
|
---|
259 |
|
---|
260 | switch (format)
|
---|
261 | {
|
---|
262 | case SurfaceFormat.BGRA4444:
|
---|
263 | case SurfaceFormat.BGRX5551:
|
---|
264 | case SurfaceFormat.BGRA5551:
|
---|
265 | header.flags |= DDS_FLAGS.DDSD_PITCH;
|
---|
266 | header.linearSize = width * 2;
|
---|
267 | break;
|
---|
268 | case SurfaceFormat.BGRX:
|
---|
269 | case SurfaceFormat.BGRA:
|
---|
270 | case SurfaceFormat.RGBA:
|
---|
271 | case SurfaceFormat.RGBX:
|
---|
272 | header.flags |= DDS_FLAGS.DDSD_PITCH;
|
---|
273 | header.linearSize = width * 4;
|
---|
274 | break;
|
---|
275 | case SurfaceFormat.DXT1:
|
---|
276 | header.flags |= DDS_FLAGS.DDSD_LINEARSIZE;
|
---|
277 | header.linearSize = Math.Max(1, width / 4) * Math.Max(1, height / 4) * 8;
|
---|
278 | break;
|
---|
279 | }
|
---|
280 |
|
---|
281 | if (surfaces.Count > 1)
|
---|
282 | {
|
---|
283 | header.flags |= DDS_FLAGS.DDSD_MIPMAPCOUNT;
|
---|
284 | header.mipmapCount = surfaces.Count;
|
---|
285 | header.caps |= DDS_CAPS.DDSCAPS_COMPLEX | DDS_CAPS.DDSCAPS_MIPMAP;
|
---|
286 | }
|
---|
287 |
|
---|
288 | return header;
|
---|
289 | }
|
---|
290 |
|
---|
291 | public void Write(BinaryWriter writer)
|
---|
292 | {
|
---|
293 | writer.Write(DDS_MAGIC);
|
---|
294 | writer.Write(124);
|
---|
295 | writer.Write((int)flags);
|
---|
296 | writer.Write(height);
|
---|
297 | writer.Write(width);
|
---|
298 | writer.Write(linearSize);
|
---|
299 | writer.Write(depth);
|
---|
300 | writer.Write(mipmapCount);
|
---|
301 | writer.BaseStream.Seek(44, SeekOrigin.Current);
|
---|
302 | writer.Write(32);
|
---|
303 | writer.Write((int)formatFlags);
|
---|
304 | writer.Write((int)fourCC);
|
---|
305 | writer.Write(rgbBitCount);
|
---|
306 | writer.Write(rBitMask);
|
---|
307 | writer.Write(gBitMask);
|
---|
308 | writer.Write(bBitMask);
|
---|
309 | writer.Write(aBitMask);
|
---|
310 | writer.Write((int)caps);
|
---|
311 | writer.Write((int)caps2);
|
---|
312 | writer.BaseStream.Seek(12, SeekOrigin.Current);
|
---|
313 | }
|
---|
314 | }
|
---|
315 | }
|
---|