/[Sims3RigEditor]/trunk/ViewDDS/DdsFile.cs
ViewVC logotype

Contents of /trunk/ViewDDS/DdsFile.cs

Parent Directory Parent Directory | Revision Log Revision Log


Revision 64 - (show annotations) (download)
Fri Aug 13 01:33:17 2010 UTC (9 years, 10 months ago) by william
File size: 48293 byte(s)
Add DXT2 & DXT4 support to DDS Plugin

1 //------------------------------------------------------------------------------
2 /*
3 @brief DDS File Type Plugin for Paint.NET
4
5 @note Copyright (c) 2007 Dean Ashton http://www.dmashton.co.uk
6
7 Permission is hereby granted, free of charge, to any person obtaining
8 a copy of this software and associated documentation files (the
9 "Software"), to deal in the Software without restriction, including
10 without limitation the rights to use, copy, modify, merge, publish,
11 distribute, sublicense, and/or sell copies of the Software, and to
12 permit persons to whom the Software is furnished to do so, subject to
13 the following conditions:
14
15 The above copyright notice and this permission notice shall be included
16 in all copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 **/
26 //------------------------------------------------------------------------------
27
28 // If we want to do the alignment as per the (broken) DDS documentation, then we
29 // uncomment this define..
30 //#define APPLY_PITCH_ALIGNMENT
31
32 using System;
33 using System.Collections.Generic;
34 using System.IO;
35 using System.Text;
36 //using PaintDotNet;
37 using System.Drawing;
38 using PaintDotNet;
39
40 namespace DdsFileTypePlugin
41 {
42 public enum DdsFileFormat
43 {
44 DDS_FORMAT_DXT1,
45 DDS_FORMAT_DXT2,
46 DDS_FORMAT_DXT3,
47 DDS_FORMAT_DXT4,
48 DDS_FORMAT_DXT5,
49 DDS_FORMAT_A8R8G8B8,
50 DDS_FORMAT_X8R8G8B8,
51 DDS_FORMAT_A8B8G8R8,
52 DDS_FORMAT_X8B8G8R8,
53 DDS_FORMAT_A1R5G5B5,
54 DDS_FORMAT_A4R4G4B4,
55 DDS_FORMAT_R8G8B8,
56 DDS_FORMAT_R5G6B5,
57
58 DDS_FORMAT_INVALID,
59 };
60
61 public class DdsPixelFormat
62 {
63 public enum PixelFormatFlags
64 {
65 DDS_FOURCC = 0x00000004,
66 DDS_RGB = 0x00000040,
67 DDS_RGBA = 0x00000041,
68 }
69
70 public uint m_size;
71 public uint m_flags;
72 public uint m_fourCC;
73 public uint m_rgbBitCount;
74 public uint m_rBitMask;
75 public uint m_gBitMask;
76 public uint m_bBitMask;
77 public uint m_aBitMask;
78
79 public uint Size()
80 {
81 return 8 * 4;
82 }
83
84 public void Initialise(DdsFileFormat fileFormat)
85 {
86 m_size = Size();
87 switch (fileFormat)
88 {
89 case DdsFileFormat.DDS_FORMAT_DXT1:
90 case DdsFileFormat.DDS_FORMAT_DXT2:
91 case DdsFileFormat.DDS_FORMAT_DXT3:
92 case DdsFileFormat.DDS_FORMAT_DXT4:
93 case DdsFileFormat.DDS_FORMAT_DXT5:
94 {
95 // DXT1/DXT3/DXT5
96 m_flags = (int)PixelFormatFlags.DDS_FOURCC;
97 m_rgbBitCount = 0;
98 m_rBitMask = 0;
99 m_gBitMask = 0;
100 m_bBitMask = 0;
101 m_aBitMask = 0;
102 if (fileFormat == DdsFileFormat.DDS_FORMAT_DXT1) m_fourCC = 0x31545844; //"DXT1"
103 if (fileFormat == DdsFileFormat.DDS_FORMAT_DXT2) m_fourCC = 0x32545844; //"DXT3"
104 if (fileFormat == DdsFileFormat.DDS_FORMAT_DXT3) m_fourCC = 0x33545844; //"DXT3"
105 if (fileFormat == DdsFileFormat.DDS_FORMAT_DXT4) m_fourCC = 0x34545844; //"DXT3"
106 if (fileFormat == DdsFileFormat.DDS_FORMAT_DXT5) m_fourCC = 0x35545844; //"DXT5"
107 break;
108 }
109
110 case DdsFileFormat.DDS_FORMAT_A8R8G8B8:
111 {
112 m_flags = (int)PixelFormatFlags.DDS_RGBA;
113 m_rgbBitCount = 32;
114 m_fourCC = 0;
115 m_rBitMask = 0x00ff0000;
116 m_gBitMask = 0x0000ff00;
117 m_bBitMask = 0x000000ff;
118 m_aBitMask = 0xff000000;
119 break;
120 }
121
122 case DdsFileFormat.DDS_FORMAT_X8R8G8B8:
123 {
124 m_flags = (int)PixelFormatFlags.DDS_RGB;
125 m_rgbBitCount = 32;
126 m_fourCC = 0;
127 m_rBitMask = 0x00ff0000;
128 m_gBitMask = 0x0000ff00;
129 m_bBitMask = 0x000000ff;
130 m_aBitMask = 0x00000000;
131 break;
132 }
133
134 case DdsFileFormat.DDS_FORMAT_A8B8G8R8:
135 {
136 m_flags = (int)PixelFormatFlags.DDS_RGBA;
137 m_rgbBitCount = 32;
138 m_fourCC = 0;
139 m_rBitMask = 0x000000ff;
140 m_gBitMask = 0x0000ff00;
141 m_bBitMask = 0x00ff0000;
142 m_aBitMask = 0xff000000;
143 break;
144 }
145
146 case DdsFileFormat.DDS_FORMAT_X8B8G8R8:
147 {
148 m_flags = (int)PixelFormatFlags.DDS_RGB;
149 m_rgbBitCount = 32;
150 m_fourCC = 0;
151 m_rBitMask = 0x000000ff;
152 m_gBitMask = 0x0000ff00;
153 m_bBitMask = 0x00ff0000;
154 m_aBitMask = 0x00000000;
155 break;
156 }
157
158 case DdsFileFormat.DDS_FORMAT_A1R5G5B5:
159 {
160 m_flags = (int)PixelFormatFlags.DDS_RGBA;
161 m_rgbBitCount = 16;
162 m_fourCC = 0;
163 m_rBitMask = 0x00007c00;
164 m_gBitMask = 0x000003e0;
165 m_bBitMask = 0x0000001f;
166 m_aBitMask = 0x00008000;
167 break;
168 }
169
170 case DdsFileFormat.DDS_FORMAT_A4R4G4B4:
171 {
172 m_flags = (int)PixelFormatFlags.DDS_RGBA;
173 m_rgbBitCount = 16;
174 m_fourCC = 0;
175 m_rBitMask = 0x00000f00;
176 m_gBitMask = 0x000000f0;
177 m_bBitMask = 0x0000000f;
178 m_aBitMask = 0x0000f000;
179 break;
180 }
181
182 case DdsFileFormat.DDS_FORMAT_R8G8B8:
183 {
184 m_flags = (int)PixelFormatFlags.DDS_RGB;
185 m_fourCC = 0;
186 m_rgbBitCount = 24;
187 m_rBitMask = 0x00ff0000;
188 m_gBitMask = 0x0000ff00;
189 m_bBitMask = 0x000000ff;
190 m_aBitMask = 0x00000000;
191 break;
192 }
193
194 case DdsFileFormat.DDS_FORMAT_R5G6B5:
195 {
196 m_flags = (int)PixelFormatFlags.DDS_RGB;
197 m_fourCC = 0;
198 m_rgbBitCount = 16;
199 m_rBitMask = 0x0000f800;
200 m_gBitMask = 0x000007e0;
201 m_bBitMask = 0x0000001f;
202 m_aBitMask = 0x00000000;
203 break;
204 }
205
206 default:
207 break;
208 }
209 }
210
211 public void Read(BinaryReader input)
212 {
213 this.m_size = input.ReadUInt32();
214 this.m_flags = input.ReadUInt32();
215 this.m_fourCC = input.ReadUInt32();
216 this.m_rgbBitCount = input.ReadUInt32();
217 this.m_rBitMask = input.ReadUInt32();
218 this.m_gBitMask = input.ReadUInt32();
219 this.m_bBitMask = input.ReadUInt32();
220 this.m_aBitMask = input.ReadUInt32();
221 }
222
223 public void Write(BinaryWriter output)
224 {
225 output.Write(this.m_size);
226 output.Write(this.m_flags);
227 output.Write(this.m_fourCC);
228 output.Write(this.m_rgbBitCount);
229 output.Write(this.m_rBitMask);
230 output.Write(this.m_gBitMask);
231 output.Write(this.m_bBitMask);
232 output.Write(this.m_aBitMask);
233 }
234 }
235
236 public class DdsHeader
237 {
238 public enum HeaderFlags
239 {
240 DDS_HEADER_FLAGS_TEXTURE = 0x00001007, // DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT
241 DDS_HEADER_FLAGS_MIPMAP = 0x00020000, // DDSD_MIPMAPCOUNT
242 DDS_HEADER_FLAGS_VOLUME = 0x00800000, // DDSD_DEPTH
243 DDS_HEADER_FLAGS_PITCH = 0x00000008, // DDSD_PITCH
244 DDS_HEADER_FLAGS_LINEARSIZE = 0x00080000, // DDSD_LINEARSIZE
245 }
246
247 public enum SurfaceFlags
248 {
249 DDS_SURFACE_FLAGS_TEXTURE = 0x00001000, // DDSCAPS_TEXTURE
250 DDS_SURFACE_FLAGS_MIPMAP = 0x00400008, // DDSCAPS_COMPLEX | DDSCAPS_MIPMAP
251 DDS_SURFACE_FLAGS_CUBEMAP = 0x00000008, // DDSCAPS_COMPLEX
252 }
253
254 public enum CubemapFlags
255 {
256 DDS_CUBEMAP_POSITIVEX = 0x00000600, // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX
257 DDS_CUBEMAP_NEGATIVEX = 0x00000a00, // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX
258 DDS_CUBEMAP_POSITIVEY = 0x00001200, // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY
259 DDS_CUBEMAP_NEGATIVEY = 0x00002200, // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY
260 DDS_CUBEMAP_POSITIVEZ = 0x00004200, // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ
261 DDS_CUBEMAP_NEGATIVEZ = 0x00008200, // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ
262
263 DDS_CUBEMAP_ALLFACES = (DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX |
264 DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY |
265 DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ)
266 }
267
268 public enum VolumeFlags
269 {
270 DDS_FLAGS_VOLUME = 0x00200000, // DDSCAPS2_VOLUME
271 }
272
273 public DdsHeader()
274 {
275 m_pixelFormat = new DdsPixelFormat();
276 }
277
278 public uint Size()
279 {
280 return (18 * 4) + m_pixelFormat.Size() + (5 * 4);
281 }
282
283 public uint m_size;
284 public uint m_headerFlags;
285 public uint m_height;
286 public uint m_width;
287 public uint m_pitchOrLinearSize;
288 public uint m_depth;
289 public uint m_mipMapCount;
290 public uint m_reserved1_0;
291 public uint m_reserved1_1;
292 public uint m_reserved1_2;
293 public uint m_reserved1_3;
294 public uint m_reserved1_4;
295 public uint m_reserved1_5;
296 public uint m_reserved1_6;
297 public uint m_reserved1_7;
298 public uint m_reserved1_8;
299 public uint m_reserved1_9;
300 public uint m_reserved1_10;
301 public DdsPixelFormat m_pixelFormat;
302 public DdsFileFormat m_fileformat;
303 public uint m_surfaceFlags;
304 public uint m_cubemapFlags;
305 public uint m_reserved2_0;
306 public uint m_reserved2_1;
307 public uint m_reserved2_2;
308
309 public void Read(System.IO.Stream input)
310 {
311 BinaryReader Utility = new BinaryReader(input);
312 this.m_size = (uint)Utility.ReadUInt32();
313 this.m_headerFlags = (uint)Utility.ReadUInt32();
314 this.m_height = (uint)Utility.ReadUInt32();
315 this.m_width = (uint)Utility.ReadUInt32();
316 this.m_pitchOrLinearSize = (uint)Utility.ReadUInt32();
317 this.m_depth = (uint)Utility.ReadUInt32();
318 this.m_mipMapCount = (uint)Utility.ReadUInt32();
319 this.m_reserved1_0 = (uint)Utility.ReadUInt32();
320 this.m_reserved1_1 = (uint)Utility.ReadUInt32();
321 this.m_reserved1_2 = (uint)Utility.ReadUInt32();
322 this.m_reserved1_3 = (uint)Utility.ReadUInt32();
323 this.m_reserved1_4 = (uint)Utility.ReadUInt32();
324 this.m_reserved1_5 = (uint)Utility.ReadUInt32();
325 this.m_reserved1_6 = (uint)Utility.ReadUInt32();
326 this.m_reserved1_7 = (uint)Utility.ReadUInt32();
327 this.m_reserved1_8 = (uint)Utility.ReadUInt32();
328 this.m_reserved1_9 = (uint)Utility.ReadUInt32();
329 this.m_reserved1_10 = (uint)Utility.ReadUInt32();
330 this.m_pixelFormat.Read(Utility);
331 this.m_surfaceFlags = (uint)Utility.ReadUInt32();
332 this.m_cubemapFlags = (uint)Utility.ReadUInt32();
333 this.m_reserved2_0 = (uint)Utility.ReadUInt32();
334 this.m_reserved2_1 = (uint)Utility.ReadUInt32();
335 this.m_reserved2_2 = (uint)Utility.ReadUInt32();
336 }
337
338 public void Write(BinaryWriter output)
339 {
340
341
342 output.Write(this.m_size);
343 output.Write(this.m_headerFlags);
344 output.Write(this.m_height);
345 output.Write(this.m_width);
346 output.Write(this.m_pitchOrLinearSize);
347 output.Write(this.m_depth);
348 output.Write(this.m_mipMapCount);
349 output.Write(this.m_reserved1_0);
350 output.Write(this.m_reserved1_1);
351 output.Write(this.m_reserved1_2);
352 output.Write(this.m_reserved1_3);
353 output.Write(this.m_reserved1_4);
354 output.Write(this.m_reserved1_5);
355 output.Write(this.m_reserved1_6);
356 output.Write(this.m_reserved1_7);
357 output.Write(this.m_reserved1_8);
358 output.Write(this.m_reserved1_9);
359 output.Write(this.m_reserved1_10);
360 this.m_pixelFormat.Write(output);
361 output.Write(this.m_surfaceFlags);
362 output.Write(this.m_cubemapFlags);
363 output.Write(this.m_reserved2_0);
364 output.Write(this.m_reserved2_1);
365 output.Write(this.m_reserved2_2);
366 }
367
368 }
369
370 public class DdsFile
371 {
372 public DdsFile()
373 {
374 m_header = new DdsHeader();
375 }
376
377 // public void Save(System.IO.Stream output, Surface surface, DdsSaveConfigToken ddsToken, ProgressEventHandler progressCallback)
378 // {
379 // // For non-compressed textures, we need pixel width.
380 // int pixelWidth = 0;
381
382 // // Identify if we're a compressed image
383 // bool isCompressed = ((ddsToken.m_fileFormat == DdsFileFormat.DDS_FORMAT_DXT1) ||
384 // (ddsToken.m_fileFormat == DdsFileFormat.DDS_FORMAT_DXT3) ||
385 // (ddsToken.m_fileFormat == DdsFileFormat.DDS_FORMAT_DXT5));
386
387 // // Compute mip map count..
388 // int mipCount = 1;
389 // int mipWidth = surface.Width;
390 // int mipHeight = surface.Height;
391
392 // if (ddsToken.m_generateMipMaps)
393 // {
394 // // This breaks!
395
396 // while ((mipWidth > 1) || (mipHeight > 1))
397 // {
398 // mipCount++;
399 // mipWidth /= 2;
400 // mipHeight /= 2;
401 // }
402 // }
403
404 // // Populate bulk of our DdsHeader
405 // m_header.m_size = m_header.Size();
406 // m_header.m_headerFlags = (uint)(DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_TEXTURE);
407
408 // if (isCompressed)
409 // m_header.m_headerFlags |= (uint)(DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_LINEARSIZE);
410 // else
411 // m_header.m_headerFlags |= (uint)(DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_PITCH);
412
413 // if (mipCount > 1)
414 // m_header.m_headerFlags |= (uint)(DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_MIPMAP);
415
416 // m_header.m_height = (uint)surface.Height;
417 // m_header.m_width = (uint)surface.Width;
418
419 // if (isCompressed)
420 // {
421 // // Compresssed textures have the linear flag set.So pitchOrLinearSize
422 // // needs to contain the entire size of the DXT block.
423 // int blockCount = ((surface.Width + 3) / 4) * ((surface.Height + 3) / 4);
424 // int blockSize = (ddsToken.m_fileFormat == 0) ? 8 : 16;
425 // m_header.m_pitchOrLinearSize = (uint)(blockCount * blockSize);
426 // }
427 // else
428 // {
429 // // Non-compressed textures have the pitch flag set. So pitchOrLinearSize
430 // // needs to contain the row pitch of the main image. DWORD aligned too.
431 // switch (ddsToken.m_fileFormat)
432 // {
433 // case DdsFileFormat.DDS_FORMAT_A8R8G8B8:
434 // case DdsFileFormat.DDS_FORMAT_X8R8G8B8:
435 // case DdsFileFormat.DDS_FORMAT_A8B8G8R8:
436 // case DdsFileFormat.DDS_FORMAT_X8B8G8R8:
437 // pixelWidth = 4; // 32bpp
438 // break;
439
440 // case DdsFileFormat.DDS_FORMAT_A1R5G5B5:
441 // case DdsFileFormat.DDS_FORMAT_A4R4G4B4:
442 // case DdsFileFormat.DDS_FORMAT_R5G6B5:
443 // pixelWidth = 2; // 16bpp
444 // break;
445
446 // case DdsFileFormat.DDS_FORMAT_R8G8B8:
447 // pixelWidth = 3; // 24bpp
448 // break;
449 // }
450
451 // // Compute row pitch
452 // m_header.m_pitchOrLinearSize = (uint)((int)m_header.m_width * pixelWidth);
453
454 //#if APPLY_PITCH_ALIGNMENT
455 // // Align to DWORD, if we need to.. (see notes about pitch alignment all over this code)
456 // m_header.m_pitchOrLinearSize = ( uint )( ( ( int )m_header.m_pitchOrLinearSize + 3 ) & ( ~3 ) );
457 //#endif //APPLY_PITCH_ALIGNMENT
458 // }
459
460 // m_header.m_depth = 0;
461 // m_header.m_mipMapCount = (mipCount == 1) ? 0 : (uint)mipCount;
462 // m_header.m_reserved1_0 = 0;
463 // m_header.m_reserved1_1 = 0;
464 // m_header.m_reserved1_2 = 0;
465 // m_header.m_reserved1_3 = 0;
466 // m_header.m_reserved1_4 = 0;
467 // m_header.m_reserved1_5 = 0;
468 // m_header.m_reserved1_6 = 0;
469 // m_header.m_reserved1_7 = 0;
470 // m_header.m_reserved1_8 = 0;
471 // m_header.m_reserved1_9 = 0;
472 // m_header.m_reserved1_10 = 0;
473
474 // // Populate our DdsPixelFormat object
475 // m_header.m_pixelFormat.Initialise(ddsToken.m_fileFormat);
476
477 // // Populate miscellanous header flags
478 // m_header.m_surfaceFlags = (uint)DdsHeader.SurfaceFlags.DDS_SURFACE_FLAGS_TEXTURE;
479
480 // if (mipCount > 1)
481 // m_header.m_surfaceFlags |= (uint)DdsHeader.SurfaceFlags.DDS_SURFACE_FLAGS_MIPMAP;
482
483 // m_header.m_cubemapFlags = 0;
484 // m_header.m_reserved2_0 = 0;
485 // m_header.m_reserved2_1 = 0;
486 // m_header.m_reserved2_2 = 0;
487
488 // // Write out our DDS tag
489 // Utility.WriteUInt32(output, 0x20534444); // 'DDS '
490
491 // // Write out the header
492 // m_header.Write(output);
493
494 // int squishFlags = ddsToken.GetSquishFlags();
495
496 // // Our output data array will be sized as necessary
497 // byte[] outputData;
498
499 // // Reset our mip width & height variables...
500 // mipWidth = surface.Width;
501 // mipHeight = surface.Height;
502
503 // // Figure out how much total work each mip map is
504 // Size[] writeSizes = new Size[mipCount];
505 // int[] mipPixels = new int[mipCount];
506 // int[] pixelsCompleted = new int[mipCount]; // # pixels completed once we have reached this mip
507 // long totalPixels = 0;
508 // for (int mipLoop = 0; mipLoop < mipCount; mipLoop++)
509 // {
510 // Size writeSize = new Size((mipWidth > 0) ? mipWidth : 1, (mipHeight > 0) ? mipHeight : 1);
511 // writeSizes[mipLoop] = writeSize;
512
513 // int thisMipPixels = writeSize.Width * writeSize.Height;
514 // mipPixels[mipLoop] = thisMipPixels;
515
516 // if (mipLoop == 0)
517 // {
518 // pixelsCompleted[mipLoop] = 0;
519 // }
520 // else
521 // {
522 // pixelsCompleted[mipLoop] = pixelsCompleted[mipLoop - 1] + mipPixels[mipLoop - 1];
523 // }
524
525 // totalPixels += thisMipPixels;
526 // mipWidth /= 2;
527 // mipHeight /= 2;
528 // }
529
530 // mipWidth = surface.Width;
531 // mipHeight = surface.Height;
532
533 // for (int mipLoop = 0; mipLoop < mipCount; mipLoop++)
534 // {
535 // Size writeSize = writeSizes[mipLoop];
536 // Surface writeSurface = new Surface(writeSize);
537
538 // if (mipLoop == 0)
539 // {
540 // // No point resampling the first level.. it's got exactly what we want.
541 // writeSurface = surface;
542 // }
543 // else
544 // {
545 // // I'd love to have a UI component to select what kind of resampling, but
546 // // there's hardly any space for custom UI stuff in the Save Dialog. And I'm
547 // // not having any scrollbars in there..!
548 // // Also, note that each mip level is formed from the main level, to reduce
549 // // compounded errors when generating mips.
550 // writeSurface.SuperSamplingFitSurface(surface);
551 // }
552
553 // DdsSquish.ProgressFn progressFn =
554 // delegate(int workDone, int workTotal)
555 // {
556 // long thisMipPixelsDone = workDone * (long)mipWidth;
557 // long previousMipsPixelsDone = pixelsCompleted[mipLoop];
558 // double progress = (double)((double)thisMipPixelsDone + (double)previousMipsPixelsDone) / (double)totalPixels;
559 // progressCallback(this, new ProgressEventArgs(100.0 * progress));
560 // };
561
562 // if ((ddsToken.m_fileFormat >= DdsFileFormat.DDS_FORMAT_DXT1) && (ddsToken.m_fileFormat <= DdsFileFormat.DDS_FORMAT_DXT5))
563 // outputData = DdsSquish.CompressImage(writeSurface, squishFlags, (progressCallback == null) ? null : progressFn);
564 // else
565 // {
566 // int mipPitch = pixelWidth * writeSurface.Width;
567
568 // // From the DDS documents I read, I'd expected the pitch of each mip level to be
569 // // DWORD aligned. As it happens, that's not the case. Re-aligning the pitch of
570 // // each level results in later mips getting sheared as the pitch is incorrect.
571 // // So, the following line is intentionally optional. Maybe the documentation
572 // // is referring to the pitch when accessing the mip directly.. who knows.
573 // //
574 // // Infact, all the talk of non-compressed textures having DWORD alignment of pitch
575 // // seems to be bollocks.. If I apply alignment, then they fail to load in 3rd Party
576 // // or Microsoft DDS viewing applications.
577 // //
578
579 //#if APPLY_PITCH_ALIGNMENT
580 // mipPitch = ( mipPitch + 3 ) & ( ~3 );
581 //#endif // APPLY_PITCH_ALIGNMENT
582
583 // outputData = new byte[mipPitch * writeSurface.Height];
584 // outputData.Initialize();
585
586 // for (int y = 0; y < writeSurface.Height; y++)
587 // {
588 // for (int x = 0; x < writeSurface.Width; x++)
589 // {
590 // // Get colour from surface
591 // ColorBgra pixelColour = writeSurface.GetPoint(x, y);
592 // uint pixelData = 0;
593
594 // switch (ddsToken.m_fileFormat)
595 // {
596 // case DdsFileFormat.DDS_FORMAT_A8R8G8B8:
597 // {
598 // pixelData = ((uint)pixelColour.A << 24) |
599 // ((uint)pixelColour.R << 16) |
600 // ((uint)pixelColour.G << 8) |
601 // ((uint)pixelColour.B << 0);
602 // break;
603 // }
604
605 // case DdsFileFormat.DDS_FORMAT_X8R8G8B8:
606 // {
607 // pixelData = ((uint)pixelColour.R << 16) |
608 // ((uint)pixelColour.G << 8) |
609 // ((uint)pixelColour.B << 0);
610 // break;
611 // }
612
613 // case DdsFileFormat.DDS_FORMAT_A8B8G8R8:
614 // {
615 // pixelData = ((uint)pixelColour.A << 24) |
616 // ((uint)pixelColour.B << 16) |
617 // ((uint)pixelColour.G << 8) |
618 // ((uint)pixelColour.R << 0);
619 // break;
620 // }
621
622 // case DdsFileFormat.DDS_FORMAT_X8B8G8R8:
623 // {
624 // pixelData = ((uint)pixelColour.B << 16) |
625 // ((uint)pixelColour.G << 8) |
626 // ((uint)pixelColour.R << 0);
627 // break;
628 // }
629
630 // case DdsFileFormat.DDS_FORMAT_A1R5G5B5:
631 // {
632 // pixelData = ((uint)((pixelColour.A != 0) ? 1 : 0) << 15) |
633 // ((uint)(pixelColour.R >> 3) << 10) |
634 // ((uint)(pixelColour.G >> 3) << 5) |
635 // ((uint)(pixelColour.B >> 3) << 0);
636 // break;
637 // }
638
639 // case DdsFileFormat.DDS_FORMAT_A4R4G4B4:
640 // {
641 // pixelData = ((uint)(pixelColour.A >> 4) << 12) |
642 // ((uint)(pixelColour.R >> 4) << 8) |
643 // ((uint)(pixelColour.G >> 4) << 4) |
644 // ((uint)(pixelColour.B >> 4) << 0);
645 // break;
646 // }
647
648 // case DdsFileFormat.DDS_FORMAT_R8G8B8:
649 // {
650 // pixelData = ((uint)pixelColour.R << 16) |
651 // ((uint)pixelColour.G << 8) |
652 // ((uint)pixelColour.B << 0);
653 // break;
654 // }
655
656 // case DdsFileFormat.DDS_FORMAT_R5G6B5:
657 // {
658 // pixelData = ((uint)(pixelColour.R >> 3) << 11) |
659 // ((uint)(pixelColour.G >> 2) << 5) |
660 // ((uint)(pixelColour.B >> 3) << 0);
661 // break;
662 // }
663 // }
664
665 // // pixelData contains our target data.. so now set the pixel bytes
666 // int pixelOffset = (y * mipPitch) + (x * pixelWidth);
667 // for (int loop = 0; loop < pixelWidth; loop++)
668 // {
669 // outputData[pixelOffset + loop] = (byte)((pixelData >> (8 * loop)) & 0xff);
670 // }
671 // }
672
673 // if (progressCallback != null)
674 // {
675 // long thisMipPixelsDone = (y + 1) * (long)mipWidth;
676 // long previousMipsPixelsDone = pixelsCompleted[mipLoop];
677 // double progress = (double)((double)thisMipPixelsDone + (double)previousMipsPixelsDone) / (double)totalPixels;
678 // progressCallback(this, new ProgressEventArgs(100.0 * progress));
679 // }
680 // }
681 // }
682
683 // // Write the data for this mip level out..
684 // output.Write(outputData, 0, outputData.GetLength(0));
685
686 // mipWidth = mipWidth / 2;
687 // mipHeight = mipHeight / 2;
688 // }
689 // }
690
691 public void Load(System.IO.Stream input)
692 {
693 BinaryReader Utility = new BinaryReader(input);
694 // Read the DDS tag. If it's not right, then bail..
695 uint ddsTag = (uint)Utility.ReadUInt32();
696 if (ddsTag != 0x20534444)
697 throw new FormatException("File does not appear to be a DDS image");
698
699 // Read everything in.. for now assume it worked like a charm..
700 m_header.Read(input);
701
702 if ((m_header.m_pixelFormat.m_flags & (int)DdsPixelFormat.PixelFormatFlags.DDS_FOURCC) != 0)
703 {
704 int squishFlags = 0;
705
706 switch (m_header.m_pixelFormat.m_fourCC)
707 {
708 case 0x31545844:
709 squishFlags = (int)DdsSquish.SquishFlags.kDxt1;
710 this.m_header.m_fileformat = DdsFileFormat.DDS_FORMAT_DXT1;
711 break;
712 case 0x32545844:
713 squishFlags = (int)DdsSquish.SquishFlags.kDxt2;
714 this.m_header.m_fileformat = DdsFileFormat.DDS_FORMAT_DXT2;
715 break;
716 case 0x33545844:
717 squishFlags = (int)DdsSquish.SquishFlags.kDxt3;
718 this.m_header.m_fileformat = DdsFileFormat.DDS_FORMAT_DXT3;
719 break;
720 case 0x34545844:
721 squishFlags = (int)DdsSquish.SquishFlags.kDxt4;
722 this.m_header.m_fileformat = DdsFileFormat.DDS_FORMAT_DXT4;
723 break;
724 case 0x35545844:
725 squishFlags = (int)DdsSquish.SquishFlags.kDxt5;
726 this.m_header.m_fileformat = DdsFileFormat.DDS_FORMAT_DXT5;
727 break;
728
729 default:
730 throw new FormatException("File is not a supported DDS format");
731 }
732
733 // Compute size of compressed block area
734 int blockCount = ((GetWidth() + 3) / 4) * ((GetHeight() + 3) / 4);
735 int blockSize = ((squishFlags & (int)DdsSquish.SquishFlags.kDxt1) != 0) ? 8 : 16;
736
737 // Allocate room for compressed blocks, and read data into it.
738 byte[] compressedBlocks = new byte[blockCount * blockSize];
739 input.Read(compressedBlocks, 0, compressedBlocks.GetLength(0));
740
741 // Now decompress..
742 m_pixelData = DdsSquish.DecompressImage(compressedBlocks, GetWidth(), GetHeight(), squishFlags);
743 }
744 else
745 {
746 // We can only deal with the non-DXT formats we know about.. this is a bit of a mess..
747 // Sorry..
748 DdsFileFormat fileFormat = DdsFileFormat.DDS_FORMAT_INVALID;
749
750 if ((m_header.m_pixelFormat.m_flags == (int)DdsPixelFormat.PixelFormatFlags.DDS_RGBA) &&
751 (m_header.m_pixelFormat.m_rgbBitCount == 32) &&
752 (m_header.m_pixelFormat.m_rBitMask == 0x00ff0000) && (m_header.m_pixelFormat.m_gBitMask == 0x0000ff00) &&
753 (m_header.m_pixelFormat.m_bBitMask == 0x000000ff) && (m_header.m_pixelFormat.m_aBitMask == 0xff000000))
754 fileFormat = DdsFileFormat.DDS_FORMAT_A8R8G8B8;
755 else
756 if ((m_header.m_pixelFormat.m_flags == (int)DdsPixelFormat.PixelFormatFlags.DDS_RGB) &&
757 (m_header.m_pixelFormat.m_rgbBitCount == 32) &&
758 (m_header.m_pixelFormat.m_rBitMask == 0x00ff0000) && (m_header.m_pixelFormat.m_gBitMask == 0x0000ff00) &&
759 (m_header.m_pixelFormat.m_bBitMask == 0x000000ff) && (m_header.m_pixelFormat.m_aBitMask == 0x00000000))
760 fileFormat = DdsFileFormat.DDS_FORMAT_X8R8G8B8;
761 else
762 if ((m_header.m_pixelFormat.m_flags == (int)DdsPixelFormat.PixelFormatFlags.DDS_RGBA) &&
763 (m_header.m_pixelFormat.m_rgbBitCount == 32) &&
764 (m_header.m_pixelFormat.m_rBitMask == 0x000000ff) && (m_header.m_pixelFormat.m_gBitMask == 0x0000ff00) &&
765 (m_header.m_pixelFormat.m_bBitMask == 0x00ff0000) && (m_header.m_pixelFormat.m_aBitMask == 0xff000000))
766 fileFormat = DdsFileFormat.DDS_FORMAT_A8B8G8R8;
767 else
768 if ((m_header.m_pixelFormat.m_flags == (int)DdsPixelFormat.PixelFormatFlags.DDS_RGB) &&
769 (m_header.m_pixelFormat.m_rgbBitCount == 32) &&
770 (m_header.m_pixelFormat.m_rBitMask == 0x000000ff) && (m_header.m_pixelFormat.m_gBitMask == 0x0000ff00) &&
771 (m_header.m_pixelFormat.m_bBitMask == 0x00ff0000) && (m_header.m_pixelFormat.m_aBitMask == 0x00000000))
772 fileFormat = DdsFileFormat.DDS_FORMAT_X8B8G8R8;
773 else
774 if ((m_header.m_pixelFormat.m_flags == (int)DdsPixelFormat.PixelFormatFlags.DDS_RGBA) &&
775 (m_header.m_pixelFormat.m_rgbBitCount == 16) &&
776 (m_header.m_pixelFormat.m_rBitMask == 0x00007c00) && (m_header.m_pixelFormat.m_gBitMask == 0x000003e0) &&
777 (m_header.m_pixelFormat.m_bBitMask == 0x0000001f) && (m_header.m_pixelFormat.m_aBitMask == 0x00008000))
778 fileFormat = DdsFileFormat.DDS_FORMAT_A1R5G5B5;
779 else
780 if ((m_header.m_pixelFormat.m_flags == (int)DdsPixelFormat.PixelFormatFlags.DDS_RGBA) &&
781 (m_header.m_pixelFormat.m_rgbBitCount == 16) &&
782 (m_header.m_pixelFormat.m_rBitMask == 0x00000f00) && (m_header.m_pixelFormat.m_gBitMask == 0x000000f0) &&
783 (m_header.m_pixelFormat.m_bBitMask == 0x0000000f) && (m_header.m_pixelFormat.m_aBitMask == 0x0000f000))
784 fileFormat = DdsFileFormat.DDS_FORMAT_A4R4G4B4;
785 else
786 if ((m_header.m_pixelFormat.m_flags == (int)DdsPixelFormat.PixelFormatFlags.DDS_RGB) &&
787 (m_header.m_pixelFormat.m_rgbBitCount == 24) &&
788 (m_header.m_pixelFormat.m_rBitMask == 0x00ff0000) && (m_header.m_pixelFormat.m_gBitMask == 0x0000ff00) &&
789 (m_header.m_pixelFormat.m_bBitMask == 0x000000ff) && (m_header.m_pixelFormat.m_aBitMask == 0x00000000))
790 fileFormat = DdsFileFormat.DDS_FORMAT_R8G8B8;
791 else
792 if ((m_header.m_pixelFormat.m_flags == (int)DdsPixelFormat.PixelFormatFlags.DDS_RGB) &&
793 (m_header.m_pixelFormat.m_rgbBitCount == 16) &&
794 (m_header.m_pixelFormat.m_rBitMask == 0x0000f800) && (m_header.m_pixelFormat.m_gBitMask == 0x000007e0) &&
795 (m_header.m_pixelFormat.m_bBitMask == 0x0000001f) && (m_header.m_pixelFormat.m_aBitMask == 0x00000000))
796 fileFormat = DdsFileFormat.DDS_FORMAT_R5G6B5;
797
798 this.m_header.m_fileformat = fileFormat;
799 // If fileFormat is still invalid, then it's an unsupported format.
800 if (fileFormat == DdsFileFormat.DDS_FORMAT_INVALID)
801 throw new FormatException("File is not a supported DDS format");
802
803 // Size of a source pixel, in bytes
804 int srcPixelSize = ((int)m_header.m_pixelFormat.m_rgbBitCount / 8);
805
806 // We need the pitch for a row, so we can allocate enough memory for the load.
807 int rowPitch = 0;
808
809 if ((m_header.m_headerFlags & (int)DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_PITCH) != 0)
810 {
811 // Pitch specified.. so we can use directly
812 rowPitch = (int)m_header.m_pitchOrLinearSize;
813 }
814 else
815 if ((m_header.m_headerFlags & (int)DdsHeader.HeaderFlags.DDS_HEADER_FLAGS_LINEARSIZE) != 0)
816 {
817 // Linear size specified.. compute row pitch. Of course, this should never happen
818 // as linear size is *supposed* to be for compressed textures. But Microsoft don't
819 // always play by the rules when it comes to DDS output.
820 rowPitch = (int)m_header.m_pitchOrLinearSize / (int)m_header.m_height;
821 }
822 else
823 {
824 // Another case of Microsoft not obeying their standard is the 'Convert to..' shell extension
825 // that ships in the DirectX SDK. Seems to always leave flags empty..so no indication of pitch
826 // or linear size. And - to cap it all off - they leave pitchOrLinearSize as *zero*. Zero??? If
827 // we get this bizarre set of inputs, we just go 'screw it' and compute row pitch ourselves,
828 // making sure we DWORD align it (if that code path is enabled).
829 rowPitch = ((int)m_header.m_width * srcPixelSize);
830
831 #if APPLY_PITCH_ALIGNMENT
832 rowPitch = ( ( ( int )rowPitch + 3 ) & ( ~3 ) );
833 #endif // APPLY_PITCH_ALIGNMENT
834 }
835
836 // System.Diagnostics.Debug.WriteLine( "Image width : " + m_header.m_width + ", rowPitch = " + rowPitch );
837
838 // Ok.. now, we need to allocate room for the bytes to read in from.. it's rowPitch bytes * height
839 byte[] readPixelData = new byte[rowPitch * m_header.m_height];
840 input.Read(readPixelData, 0, readPixelData.GetLength(0));
841
842 // We now need space for the real pixel data.. that's width * height * 4..
843 m_pixelData = new byte[m_header.m_width * m_header.m_height * 4];
844
845 // And now we have the arduous task of filling that up with stuff..
846 for (int destY = 0; destY < (int)m_header.m_height; destY++)
847 {
848 for (int destX = 0; destX < (int)m_header.m_width; destX++)
849 {
850 // Compute source pixel offset
851 int srcPixelOffset = (destY * rowPitch) + (destX * srcPixelSize);
852
853 // Read our pixel
854 uint pixelColour = 0;
855 uint pixelRed = 0;
856 uint pixelGreen = 0;
857 uint pixelBlue = 0;
858 uint pixelAlpha = 0;
859
860 // Build our pixel colour as a DWORD
861 for (int loop = 0; loop < srcPixelSize; loop++)
862 {
863 pixelColour |= (uint)(readPixelData[srcPixelOffset + loop] << (8 * loop));
864 }
865
866 if (fileFormat == DdsFileFormat.DDS_FORMAT_A8R8G8B8)
867 {
868 pixelAlpha = (pixelColour >> 24) & 0xff;
869 pixelRed = (pixelColour >> 16) & 0xff;
870 pixelGreen = (pixelColour >> 8) & 0xff;
871 pixelBlue = (pixelColour >> 0) & 0xff;
872 }
873 else
874 if (fileFormat == DdsFileFormat.DDS_FORMAT_X8R8G8B8)
875 {
876 pixelAlpha = 0xff;
877 pixelRed = (pixelColour >> 16) & 0xff;
878 pixelGreen = (pixelColour >> 8) & 0xff;
879 pixelBlue = (pixelColour >> 0) & 0xff;
880 }
881 else
882 if (fileFormat == DdsFileFormat.DDS_FORMAT_A8B8G8R8)
883 {
884 pixelAlpha = (pixelColour >> 24) & 0xff;
885 pixelRed = (pixelColour >> 0) & 0xff;
886 pixelGreen = (pixelColour >> 8) & 0xff;
887 pixelBlue = (pixelColour >> 16) & 0xff;
888 }
889 else
890 if (fileFormat == DdsFileFormat.DDS_FORMAT_X8B8G8R8)
891 {
892 pixelAlpha = 0xff;
893 pixelRed = (pixelColour >> 0) & 0xff;
894 pixelGreen = (pixelColour >> 8) & 0xff;
895 pixelBlue = (pixelColour >> 16) & 0xff;
896 }
897 else
898 if (fileFormat == DdsFileFormat.DDS_FORMAT_A1R5G5B5)
899 {
900 pixelAlpha = (pixelColour >> 15) * 0xff;
901 pixelRed = (pixelColour >> 10) & 0x1f;
902 pixelGreen = (pixelColour >> 5) & 0x1f;
903 pixelBlue = (pixelColour >> 0) & 0x1f;
904
905 pixelRed = (pixelRed << 3) | (pixelRed >> 2);
906 pixelGreen = (pixelGreen << 3) | (pixelGreen >> 2);
907 pixelBlue = (pixelBlue << 3) | (pixelBlue >> 2);
908 }
909 else
910 if (fileFormat == DdsFileFormat.DDS_FORMAT_A4R4G4B4)
911 {
912 pixelAlpha = (pixelColour >> 12) & 0xff;
913 pixelRed = (pixelColour >> 8) & 0x0f;
914 pixelGreen = (pixelColour >> 4) & 0x0f;
915 pixelBlue = (pixelColour >> 0) & 0x0f;
916
917 pixelAlpha = (pixelAlpha << 4) | (pixelAlpha >> 0);
918 pixelRed = (pixelRed << 4) | (pixelRed >> 0);
919 pixelGreen = (pixelGreen << 4) | (pixelGreen >> 0);
920 pixelBlue = (pixelBlue << 4) | (pixelBlue >> 0);
921 }
922 else
923 if (fileFormat == DdsFileFormat.DDS_FORMAT_R8G8B8)
924 {
925 pixelAlpha = 0xff;
926 pixelRed = (pixelColour >> 16) & 0xff;
927 pixelGreen = (pixelColour >> 8) & 0xff;
928 pixelBlue = (pixelColour >> 0) & 0xff;
929 }
930 else
931 if (fileFormat == DdsFileFormat.DDS_FORMAT_R5G6B5)
932 {
933 pixelAlpha = 0xff;
934 pixelRed = (pixelColour >> 11) & 0x1f;
935 pixelGreen = (pixelColour >> 5) & 0x3f;
936 pixelBlue = (pixelColour >> 0) & 0x1f;
937
938 pixelRed = (pixelRed << 3) | (pixelRed >> 2);
939 pixelGreen = (pixelGreen << 2) | (pixelGreen >> 4);
940 pixelBlue = (pixelBlue << 3) | (pixelBlue >> 2);
941 }
942
943 // Write the colours away..
944 int destPixelOffset = (destY * (int)m_header.m_width * 4) + (destX * 4);
945 m_pixelData[destPixelOffset + 0] = (byte)pixelRed;
946 m_pixelData[destPixelOffset + 1] = (byte)pixelGreen;
947 m_pixelData[destPixelOffset + 2] = (byte)pixelBlue;
948 m_pixelData[destPixelOffset + 3] = (byte)pixelAlpha;
949 }
950 }
951 }
952 }
953
954 public int GetWidth()
955 {
956 return (int)m_header.m_width;
957 }
958
959 public int GetHeight()
960 {
961 return (int)m_header.m_height;
962 }
963
964 public byte[] GetPixelData()
965 {
966 return m_pixelData;
967 }
968
969 // Loaded DDS header (also uses storage for save)
970 public DdsHeader m_header;
971
972 // Pixel data
973 byte[] m_pixelData;
974
975 }
976 }

  ViewVC Help
Powered by ViewVC 1.1.22