source: OniSplit/Imaging/DdsHeader.cs@ 1174

Last change on this file since 1174 was 1114, checked in by iritscen, 5 years ago

Adding OniSplit source code (v0.9.99.0). Many thanks to Neo for all his work over the years.

File size: 11.1 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.IO;
4
5namespace 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}
Note: See TracBrowser for help on using the repository browser.