blob: b5ea3c97ab0e55cb20d25d2fd79f222e82f08736 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% DDDD DDDD SSSSS %
7% D D D D SS %
8% D D D D SSS %
9% D D D D SS %
10% DDDD DDDD SSSSS %
11% %
12% %
13% Read Microsoft Direct Draw Surface Image Format %
14% %
15% Software Design %
16% Bianca van Schaik %
17% March 2008 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
42#include "magick/studio.h"
43#include "magick/blob.h"
44#include "magick/blob-private.h"
45#include "magick/cache.h"
46#include "magick/colorspace.h"
47#include "magick/exception.h"
48#include "magick/exception-private.h"
49#include "magick/image.h"
50#include "magick/image-private.h"
51#include "magick/list.h"
52#include "magick/log.h"
53#include "magick/magick.h"
54#include "magick/memory_.h"
55#include "magick/monitor.h"
56#include "magick/monitor-private.h"
57#include "magick/profile.h"
58#include "magick/quantum-private.h"
59#include "magick/static.h"
60#include "magick/string_.h"
61#include "magick/module.h"
62#include "magick/transform.h"
63#include "magick/studio.h"
64#include "magick/blob.h"
65#include "magick/blob-private.h"
66#include "magick/colorspace.h"
67#include "magick/exception.h"
68#include "magick/exception-private.h"
69#include "magick/compress.h"
70#include "magick/image.h"
71#include "magick/image-private.h"
72#include "magick/list.h"
73#include "magick/magick.h"
74#include "magick/memory_.h"
75#include "magick/monitor.h"
76#include "magick/monitor-private.h"
77#include "magick/quantum.h"
78#include "magick/static.h"
79#include "magick/string_.h"
80
81/*
82 Definitions
83*/
84#define DDSD_CAPS 0x00000001
85#define DDSD_HEIGHT 0x00000002
86#define DDSD_WIDTH 0x00000004
87#define DDSD_PITCH 0x00000008
88#define DDSD_PIXELFORMAT 0x00001000
89#define DDSD_MIPMAPCOUNT 0x00020000
90#define DDSD_LINEARSIZE 0x00080000
91#define DDSD_DEPTH 0x00800000
92
93#define DDPF_ALPHAPIXELS 0x00000001
94#define DDPF_FOURCC 0x00000004
95#define DDPF_RGB 0x00000040
96
97#define FOURCC_DXT1 0x31545844
98#define FOURCC_DXT3 0x33545844
99#define FOURCC_DXT5 0x35545844
100
101#define DDSCAPS_COMPLEX 0x00000008
102#define DDSCAPS_TEXTURE 0x00001000
103#define DDSCAPS_MIPMAP 0x00400000
104
105#define DDSCAPS2_CUBEMAP 0x00000200
106#define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400
107#define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800
108#define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000
109#define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000
110#define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000
111#define DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000
112#define DDSCAPS2_VOLUME 0x00200000
113
114/*
115 Structure declarations.
116*/
117typedef struct _DDSPixelFormat
118{
cristybb503372010-05-27 20:51:26 +0000119 size_t
cristy3ed852e2009-09-05 21:47:34 +0000120 flags,
121 fourcc,
122 rgb_bitcount,
123 r_bitmask,
124 g_bitmask,
125 b_bitmask,
126 alpha_bitmask;
127} DDSPixelFormat;
128
129typedef struct _DDSInfo
130{
cristybb503372010-05-27 20:51:26 +0000131 size_t
cristyc5de6992009-10-06 19:19:48 +0000132 flags,
133 height,
134 width,
135 pitchOrLinearSize,
136 depth,
137 mipmapcount,
cristy3ed852e2009-09-05 21:47:34 +0000138 ddscaps1,
139 ddscaps2;
140
141 DDSPixelFormat
142 pixelformat;
143} DDSInfo;
144
145typedef struct _DDSColors
146{
147 unsigned char
148 r[4],
149 g[4],
150 b[4],
151 a[4];
152} DDSColors;
153
154typedef MagickBooleanType
155 DDSDecoder(Image *,DDSInfo *);
156
157/*
158 Macros
159*/
160#define C565_r(x) (((x) & 0xF800) >> 11)
161#define C565_g(x) (((x) & 0x07E0) >> 5)
162#define C565_b(x) ((x) & 0x001F)
163
164#define C565_red(x) ( (C565_r(x) << 3 | C565_r(x) >> 2))
165#define C565_green(x) ( (C565_g(x) << 2 | C565_g(x) >> 4))
166#define C565_blue(x) ( (C565_b(x) << 3 | C565_b(x) >> 2))
167
168#define DIV2(x) ((x) > 1 ? ((x) >> 1) : 1)
169
170/*
171 Forward declarations
172*/
173static MagickBooleanType
174 ReadDDSInfo(Image *image, DDSInfo *dds_info);
175
176static void
177 CalculateColors(unsigned short c0, unsigned short c1,
178 DDSColors *c, MagickBooleanType ignoreAlpha);
179
180static MagickBooleanType
181 ReadDXT1(Image *image, DDSInfo *dds_info);
182
183static MagickBooleanType
184 ReadDXT3(Image *image, DDSInfo *dds_info);
185
186static MagickBooleanType
187 ReadDXT5(Image *image, DDSInfo *dds_info);
188
189static MagickBooleanType
190 ReadUncompressedRGB(Image *image, DDSInfo *dds_info);
191
192static MagickBooleanType
193 ReadUncompressedRGBA(Image *image, DDSInfo *dds_info);
194
195static void
196 SkipDXTMipmaps(Image *image, DDSInfo *dds_info, int texel_size);
197
198static void
199 SkipRGBMipmaps(Image *image, DDSInfo *dds_info, int pixel_size);
200
201/*
202%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
203% %
204% %
205% %
206% R e a d D D S I m a g e %
207% %
208% %
209% %
210%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
211%
212% ReadDDSImage() reads a DirectDraw Surface image file and returns it. It
213% allocates the memory necessary for the new Image structure and returns a
214% pointer to the new image.
215%
216% The format of the ReadDDSImage method is:
217%
218% Image *ReadDDSImage(const ImageInfo *image_info,ExceptionInfo *exception)
219%
220% A description of each parameter follows:
221%
222% o image_info: The image info.
223%
224% o exception: return any errors or warnings in this structure.
225%
226*/
227
cristybb503372010-05-27 20:51:26 +0000228static inline size_t Min(size_t one, size_t two)
cristy3ed852e2009-09-05 21:47:34 +0000229{
230 if (one < two)
231 return one;
232 return two;
233}
234
235static Image *ReadDDSImage(const ImageInfo *image_info,ExceptionInfo *exception)
236{
237 Image
238 *image;
239
240 MagickBooleanType
241 status,
242 cubemap = MagickFalse,
243 volume = MagickFalse,
244 matte;
245
246 CompressionType
247 compression;
248
249 DDSInfo
cristyc5de6992009-10-06 19:19:48 +0000250 dds_info;
cristy3ed852e2009-09-05 21:47:34 +0000251
252 DDSDecoder
253 *decoder;
254
cristybb503372010-05-27 20:51:26 +0000255 size_t
cristy3ed852e2009-09-05 21:47:34 +0000256 n, num_images;
257
258 /*
259 Open image file.
260 */
261 assert(image_info != (const ImageInfo *) NULL);
262 assert(image_info->signature == MagickSignature);
263 if (image_info->debug != MagickFalse)
264 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
265 image_info->filename);
266 assert(exception != (ExceptionInfo *) NULL);
267 assert(exception->signature == MagickSignature);
268 image=AcquireImage(image_info);
269 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
270 if (status == MagickFalse)
271 {
272 image=DestroyImageList(image);
273 return((Image *) NULL);
274 }
275
276 /*
277 Initialize image structure.
278 */
279 if (ReadDDSInfo(image, &dds_info) != MagickTrue) {
cristyc5de6992009-10-06 19:19:48 +0000280 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy3ed852e2009-09-05 21:47:34 +0000281 }
282
283 if (dds_info.ddscaps2 & DDSCAPS2_CUBEMAP)
284 cubemap = MagickTrue;
285
286 if (dds_info.ddscaps2 & DDSCAPS2_VOLUME && dds_info.depth > 0)
287 volume = MagickTrue;
288
289 (void) SeekBlob(image, 128, SEEK_SET);
290
291 /*
292 Determine pixel format
293 */
294 if (dds_info.pixelformat.flags & DDPF_RGB)
295 {
296 compression = NoCompression;
297 if (dds_info.pixelformat.flags & DDPF_ALPHAPIXELS)
298 {
299 matte = MagickTrue;
300 decoder = ReadUncompressedRGBA;
301 }
302 else
303 {
304 matte = MagickTrue;
305 decoder = ReadUncompressedRGB;
306 }
307 }
308 else if (dds_info.pixelformat.flags & DDPF_FOURCC)
309 {
310 switch (dds_info.pixelformat.fourcc)
311 {
312 case FOURCC_DXT1:
313 {
314 matte = MagickFalse;
315 compression = DXT1Compression;
316 decoder = ReadDXT1;
317 break;
318 }
319
320 case FOURCC_DXT3:
321 {
322 matte = MagickTrue;
323 compression = DXT3Compression;
324 decoder = ReadDXT3;
325 break;
326 }
327
328 case FOURCC_DXT5:
329 {
330 matte = MagickTrue;
331 compression = DXT5Compression;
332 decoder = ReadDXT5;
333 break;
334 }
335
336 default:
337 {
338 /* Unknown FOURCC */
339 ThrowReaderException(CorruptImageError, "ImageTypeNotSupported");
340 }
341 }
342 }
343 else
344 {
345 /* Neither compressed nor uncompressed... thus unsupported */
346 ThrowReaderException(CorruptImageError, "ImageTypeNotSupported");
347 }
348
349 num_images = 1;
350 if (cubemap)
351 {
352 /*
353 Determine number of faces defined in the cubemap
354 */
355 num_images = 0;
356 if (dds_info.ddscaps2 & DDSCAPS2_CUBEMAP_POSITIVEX) num_images++;
357 if (dds_info.ddscaps2 & DDSCAPS2_CUBEMAP_NEGATIVEX) num_images++;
358 if (dds_info.ddscaps2 & DDSCAPS2_CUBEMAP_POSITIVEY) num_images++;
359 if (dds_info.ddscaps2 & DDSCAPS2_CUBEMAP_NEGATIVEY) num_images++;
360 if (dds_info.ddscaps2 & DDSCAPS2_CUBEMAP_POSITIVEZ) num_images++;
361 if (dds_info.ddscaps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ) num_images++;
362 }
363
364 if (volume)
365 num_images = dds_info.depth;
366
367 for (n = 0; n < num_images; n++)
368 {
369 if (n != 0)
370 {
371 /* Start a new image */
372 AcquireNextImage(image_info,image);
373 if (GetNextImageInList(image) == (Image *) NULL)
374 {
375 image = DestroyImageList(image);
376 return((Image *) NULL);
377 }
378 image=SyncNextImageInList(image);
379 }
380
381 image->matte = matte;
382 image->compression = compression;
383 image->columns = dds_info.width;
384 image->rows = dds_info.height;
385 image->storage_class = DirectClass;
386 image->endian = LSBEndian;
387 image->depth = 8;
388 if (image_info->ping != MagickFalse)
389 {
390 (void) CloseBlob(image);
391 return(GetFirstImageInList(image));
392 }
393
394 if ((decoder)(image, &dds_info) != MagickTrue)
395 {
396 (void) CloseBlob(image);
397 return(GetFirstImageInList(image));
398 }
399 }
400
401 if (EOFBlob(image) != MagickFalse)
402 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
403 image->filename);
404
405 (void) CloseBlob(image);
406 return(GetFirstImageInList(image));
407}
408
409static MagickBooleanType ReadDDSInfo(Image *image, DDSInfo *dds_info)
410{
cristybb503372010-05-27 20:51:26 +0000411 size_t
cristy3ed852e2009-09-05 21:47:34 +0000412 hdr_size,
413 required;
cristy3ed852e2009-09-05 21:47:34 +0000414
cristyc5de6992009-10-06 19:19:48 +0000415 /* Seek to start of header */
416 (void) SeekBlob(image, 4, SEEK_SET);
417
418 /* Check header field */
419 hdr_size = ReadBlobLSBLong(image);
420 if (hdr_size != 124)
421 return MagickFalse;
422
423 /* Fill in DDS info struct */
424 dds_info->flags = ReadBlobLSBLong(image);
425
426 /* Check required flags */
cristybb503372010-05-27 20:51:26 +0000427 required=(size_t) (DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT);
cristy3ed852e2009-09-05 21:47:34 +0000428 if ((dds_info->flags & required) != required)
cristyc5de6992009-10-06 19:19:48 +0000429 return MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000430
cristyc5de6992009-10-06 19:19:48 +0000431 dds_info->height = ReadBlobLSBLong(image);
432 dds_info->width = ReadBlobLSBLong(image);
433 dds_info->pitchOrLinearSize = ReadBlobLSBLong(image);
434 dds_info->depth = ReadBlobLSBLong(image);
435 dds_info->mipmapcount = ReadBlobLSBLong(image);
cristy3ed852e2009-09-05 21:47:34 +0000436
cristyc5de6992009-10-06 19:19:48 +0000437 (void) SeekBlob(image, 44, SEEK_CUR); /* reserved region of 11 DWORDs */
cristy3ed852e2009-09-05 21:47:34 +0000438
439 /* Read pixel format structure */
440 hdr_size = ReadBlobLSBLong(image);
441 if (hdr_size != 32)
442 return MagickFalse;
443
444 dds_info->pixelformat.flags = ReadBlobLSBLong(image);
445 dds_info->pixelformat.fourcc = ReadBlobLSBLong(image);
446 dds_info->pixelformat.rgb_bitcount = ReadBlobLSBLong(image);
447 dds_info->pixelformat.r_bitmask = ReadBlobLSBLong(image);
448 dds_info->pixelformat.g_bitmask = ReadBlobLSBLong(image);
449 dds_info->pixelformat.b_bitmask = ReadBlobLSBLong(image);
450 dds_info->pixelformat.alpha_bitmask = ReadBlobLSBLong(image);
451
cristyc5de6992009-10-06 19:19:48 +0000452 dds_info->ddscaps1 = ReadBlobLSBLong(image);
453 dds_info->ddscaps2 = ReadBlobLSBLong(image);
454 (void) SeekBlob(image, 12, SEEK_CUR); /* 3 reserved DWORDs */
cristy3ed852e2009-09-05 21:47:34 +0000455
cristyc5de6992009-10-06 19:19:48 +0000456 return MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +0000457}
458
459static void CalculateColors(unsigned short c0, unsigned short c1,
460 DDSColors *c, MagickBooleanType ignoreAlpha)
461{
462 c->a[0] = c->a[1] = c->a[2] = c->a[3] = 0;
463
464 c->r[0] = (unsigned char) C565_red(c0);
465 c->g[0] = (unsigned char) C565_green(c0);
466 c->b[0] = (unsigned char) C565_blue(c0);
467
468 c->r[1] = (unsigned char) C565_red(c1);
469 c->g[1] = (unsigned char) C565_green(c1);
470 c->b[1] = (unsigned char) C565_blue(c1);
471
472 if (ignoreAlpha == MagickTrue || c0 > c1)
473 {
474 c->r[2] = (unsigned char) ((2 * c->r[0] + c->r[1]) / 3);
475 c->g[2] = (unsigned char) ((2 * c->g[0] + c->g[1]) / 3);
476 c->b[2] = (unsigned char) ((2 * c->b[0] + c->b[1]) / 3);
477
478 c->r[3] = (unsigned char) ((c->r[0] + 2 * c->r[1]) / 3);
479 c->g[3] = (unsigned char) ((c->g[0] + 2 * c->g[1]) / 3);
480 c->b[3] = (unsigned char) ((c->b[0] + 2 * c->b[1]) / 3);
481 }
482 else
483 {
484 c->r[2] = (unsigned char) ((c->r[0] + c->r[1]) / 2);
485 c->g[2] = (unsigned char) ((c->g[0] + c->g[1]) / 2);
486 c->b[2] = (unsigned char) ((c->b[0] + c->b[1]) / 2);
487
488 c->r[3] = c->g[3] = c->b[3] = 0;
489 c->a[3] = 255;
490 }
491}
492
493static MagickBooleanType ReadDXT1(Image *image, DDSInfo *dds_info)
494{
495 DDSColors
496 colors;
497
498 ExceptionInfo
499 *exception;
500
cristybb503372010-05-27 20:51:26 +0000501 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000502 j,
503 y;
cristyc5de6992009-10-06 19:19:48 +0000504
cristy3ed852e2009-09-05 21:47:34 +0000505 PixelPacket
506 *q;
507
cristybb503372010-05-27 20:51:26 +0000508 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000509 i,
510 x;
511
512 unsigned char
513 code;
514
cristyc5de6992009-10-06 19:19:48 +0000515 unsigned short
cristy3ed852e2009-09-05 21:47:34 +0000516 c0,
517 c1;
cristyc5de6992009-10-06 19:19:48 +0000518
cristybb503372010-05-27 20:51:26 +0000519 size_t
cristy3ed852e2009-09-05 21:47:34 +0000520 bits;
521
522 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +0000523 for (y = 0; y < (ssize_t) dds_info->height; y += 4)
cristy3ed852e2009-09-05 21:47:34 +0000524 {
cristybb503372010-05-27 20:51:26 +0000525 for (x = 0; x < (ssize_t) dds_info->width; x += 4)
cristy3ed852e2009-09-05 21:47:34 +0000526 {
527 /* Get 4x4 patch of pixels to write on */
528 q = QueueAuthenticPixels(image, x, y, Min(4, dds_info->width - x),
cristyc5de6992009-10-06 19:19:48 +0000529 Min(4, dds_info->height - y),exception);
cristy3ed852e2009-09-05 21:47:34 +0000530
531 if (q == (PixelPacket *) NULL)
532 return MagickFalse;
533
534 /* Read 8 bytes of data from the image */
535 c0 = ReadBlobLSBShort(image);
536 c1 = ReadBlobLSBShort(image);
537 bits = ReadBlobLSBLong(image);
538
539 CalculateColors(c0, c1, &colors, MagickFalse);
540
541 /* Write the pixels */
542 for (j = 0; j < 4; j++)
543 {
544 for (i = 0; i < 4; i++)
545 {
cristybb503372010-05-27 20:51:26 +0000546 if ((x + i) < (ssize_t) dds_info->width && (y + j) < (ssize_t) dds_info->height)
cristy3ed852e2009-09-05 21:47:34 +0000547 {
548 code = (unsigned char) ((bits >> ((j*4+i)*2)) & 0x3);
549 q->red = ScaleCharToQuantum( colors.r[code] );
550 q->green = ScaleCharToQuantum( colors.g[code] );
551 q->blue = ScaleCharToQuantum( colors.b[code] );
552 q->opacity = ScaleCharToQuantum( colors.a[code] );
553 if (colors.a[code] && image->matte == MagickFalse)
554 /* Correct matte */
555 image->matte = MagickTrue;
556 q++;
557 }
558 }
559 }
560
561 if (SyncAuthenticPixels(image,exception) == MagickFalse)
562 return MagickFalse;
563 }
564 }
565
566 SkipDXTMipmaps(image, dds_info, 8);
567
568 return MagickTrue;
569}
570
571static MagickBooleanType ReadDXT3(Image *image, DDSInfo *dds_info)
572{
573 DDSColors
574 colors;
575
576 ExceptionInfo
577 *exception;
578
cristybb503372010-05-27 20:51:26 +0000579 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000580 j,
581 y;
582
583 PixelPacket
584 *q;
585
cristybb503372010-05-27 20:51:26 +0000586 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000587 i,
588 x;
589
590 unsigned char
591 alpha;
592
cristybb503372010-05-27 20:51:26 +0000593 size_t
cristy3ed852e2009-09-05 21:47:34 +0000594 a0,
595 a1,
596 bits,
597 code;
598
599 unsigned short
600 c0,
601 c1;
602
603 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +0000604 for (y = 0; y < (ssize_t) dds_info->height; y += 4)
cristy3ed852e2009-09-05 21:47:34 +0000605 {
cristybb503372010-05-27 20:51:26 +0000606 for (x = 0; x < (ssize_t) dds_info->width; x += 4)
cristy3ed852e2009-09-05 21:47:34 +0000607 {
608 /* Get 4x4 patch of pixels to write on */
609 q = QueueAuthenticPixels(image, x, y, Min(4, dds_info->width - x),
610 Min(4, dds_info->height - y),exception);
611
612 if (q == (PixelPacket *) NULL)
613 return MagickFalse;
614
615 /* Read alpha values (8 bytes) */
616 a0 = ReadBlobLSBLong(image);
617 a1 = ReadBlobLSBLong(image);
618
619 /* Read 8 bytes of data from the image */
620 c0 = ReadBlobLSBShort(image);
621 c1 = ReadBlobLSBShort(image);
622 bits = ReadBlobLSBLong(image);
623
624 CalculateColors(c0, c1, &colors, MagickTrue);
625
626 /* Write the pixels */
627 for (j = 0; j < 4; j++)
628 {
629 for (i = 0; i < 4; i++)
630 {
cristybb503372010-05-27 20:51:26 +0000631 if ((x + i) < (ssize_t) dds_info->width && (y + j) < (ssize_t) dds_info->height)
cristy3ed852e2009-09-05 21:47:34 +0000632 {
633 code = (bits >> ((4*j+i)*2)) & 0x3;
634 q->red = ScaleCharToQuantum( colors.r[code] );
635 q->green = ScaleCharToQuantum( colors.g[code] );
636 q->blue = ScaleCharToQuantum( colors.b[code] );
637
638 /*
639 Extract alpha value: multiply 0..15 by 17 to get range 0..255
640 */
641 if (j < 2)
642 alpha = 17U * (unsigned char) ((a0 >> (4*(4*j+i))) & 0xf);
643 else
644 alpha = 17U * (unsigned char) ((a1 >> (4*(4*(j-2)+i))) & 0xf);
645
646 q->opacity = ScaleCharToQuantum( (unsigned char) (255 - alpha) );
647
648 q++;
649 }
650 }
651 }
652
653 if (SyncAuthenticPixels(image,exception) == MagickFalse)
654 return MagickFalse;
655 }
656 }
657
658 SkipDXTMipmaps(image, dds_info, 16);
659
660 return MagickTrue;
661}
662
663static MagickBooleanType ReadDXT5(Image *image, DDSInfo *dds_info)
664{
665 DDSColors
666 colors;
667
668 ExceptionInfo
669 *exception;
670
cristybb503372010-05-27 20:51:26 +0000671 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000672 j,
673 y;
674
675 MagickSizeType
676 alpha_bits;
677
678 PixelPacket
679 *q;
680
cristybb503372010-05-27 20:51:26 +0000681 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000682 i,
683 x;
684
685 unsigned char
686 a0,
687 a1;
688
cristybb503372010-05-27 20:51:26 +0000689 size_t
cristy3ed852e2009-09-05 21:47:34 +0000690 alpha,
691 bits,
692 code,
693 alpha_code;
694
695 unsigned short
696 c0,
697 c1;
698
699 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +0000700 for (y = 0; y < (ssize_t) dds_info->height; y += 4)
cristy3ed852e2009-09-05 21:47:34 +0000701 {
cristybb503372010-05-27 20:51:26 +0000702 for (x = 0; x < (ssize_t) dds_info->width; x += 4)
cristy3ed852e2009-09-05 21:47:34 +0000703 {
704 /* Get 4x4 patch of pixels to write on */
705 q = QueueAuthenticPixels(image, x, y, Min(4, dds_info->width - x),
706 Min(4, dds_info->height - y),exception);
707
708 if (q == (PixelPacket *) NULL)
709 return MagickFalse;
710
711 /* Read alpha values (8 bytes) */
712 a0 = (unsigned char) ReadBlobByte(image);
713 a1 = (unsigned char) ReadBlobByte(image);
714
715 alpha_bits = (MagickSizeType)ReadBlobLSBLong(image)
716 | ((MagickSizeType)ReadBlobLSBShort(image) << 32);
717
718 /* Read 8 bytes of data from the image */
719 c0 = ReadBlobLSBShort(image);
720 c1 = ReadBlobLSBShort(image);
721 bits = ReadBlobLSBLong(image);
722
723 CalculateColors(c0, c1, &colors, MagickTrue);
724
725 /* Write the pixels */
726 for (j = 0; j < 4; j++)
727 {
728 for (i = 0; i < 4; i++)
729 {
cristybb503372010-05-27 20:51:26 +0000730 if ((x + i) < (ssize_t) dds_info->width && (y + j) < (ssize_t) dds_info->height)
cristy3ed852e2009-09-05 21:47:34 +0000731 {
732 code = (bits >> ((4*j+i)*2)) & 0x3;
733 q->red = ScaleCharToQuantum( colors.r[code] );
734 q->green = ScaleCharToQuantum( colors.g[code] );
735 q->blue = ScaleCharToQuantum( colors.b[code] );
736
737 /* Extract alpha value */
cristybb503372010-05-27 20:51:26 +0000738 alpha_code = (size_t) (alpha_bits >> (3*(4*j+i))) & 0x7;
cristy3ed852e2009-09-05 21:47:34 +0000739 if (alpha_code == 0)
740 alpha = a0;
741 else if (alpha_code == 1)
742 alpha = a1;
743 else if (a0 > a1)
744 alpha = ((8-alpha_code) * a0 + (alpha_code-1) * a1) / 7;
745 else if (alpha_code == 6)
746 alpha = alpha_code;
747 else if (alpha_code == 7)
748 alpha = 255;
749 else
750 alpha = (((6-alpha_code) * a0 + (alpha_code-1) * a1) / 5);
751
752 q->opacity = ScaleCharToQuantum( (unsigned char) (255 - alpha) );
753
754 q++;
755 }
756 }
757 }
758
759 if (SyncAuthenticPixels(image,exception) == MagickFalse)
760 return MagickFalse;
761 }
762 }
763
764 SkipDXTMipmaps(image, dds_info, 16);
765
766 return MagickTrue;
767}
768
769static MagickBooleanType ReadUncompressedRGB(Image *image, DDSInfo *dds_info)
770{
771 ExceptionInfo
772 *exception;
773
cristybb503372010-05-27 20:51:26 +0000774 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000775 x, y;
776
777 PixelPacket
778 *q;
779
780 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +0000781 for (y = 0; y < (ssize_t) dds_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000782 {
783 q = QueueAuthenticPixels(image, 0, y, dds_info->width, 1,exception);
784
785 if (q == (PixelPacket *) NULL)
786 return MagickFalse;
787
cristybb503372010-05-27 20:51:26 +0000788 for (x = 0; x < (ssize_t) dds_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000789 {
790 q->blue = ScaleCharToQuantum( (unsigned char) ReadBlobByte(image) );
791 q->green = ScaleCharToQuantum( (unsigned char) ReadBlobByte(image) );
792 q->red = ScaleCharToQuantum( (unsigned char) ReadBlobByte(image) );
cristy6305d192010-06-23 01:07:24 +0000793 if (dds_info->pixelformat.rgb_bitcount == 32)
794 (void) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000795 q++;
796 }
797
798 if (SyncAuthenticPixels(image,exception) == MagickFalse)
799 return MagickFalse;
800 }
801
802 SkipRGBMipmaps(image, dds_info, 3);
803
804 return MagickTrue;
805}
806
807static MagickBooleanType ReadUncompressedRGBA(Image *image, DDSInfo *dds_info)
808{
809 ExceptionInfo
810 *exception;
811
cristybb503372010-05-27 20:51:26 +0000812 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000813 x, y;
814
815 PixelPacket
816 *q;
817
818 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +0000819 for (y = 0; y < (ssize_t) dds_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000820 {
821 q = QueueAuthenticPixels(image, 0, y, dds_info->width, 1,exception);
822
823 if (q == (PixelPacket *) NULL)
824 return MagickFalse;
825
cristybb503372010-05-27 20:51:26 +0000826 for (x = 0; x < (ssize_t) dds_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000827 {
828 q->blue = ScaleCharToQuantum( (unsigned char) ReadBlobByte(image) );
829 q->green = ScaleCharToQuantum( (unsigned char) ReadBlobByte(image) );
830 q->red = ScaleCharToQuantum( (unsigned char) ReadBlobByte(image) );
831 q->opacity = ScaleCharToQuantum( (unsigned char) (255 - ReadBlobByte(image)) );
832 q++;
833 }
834
835 if (SyncAuthenticPixels(image,exception) == MagickFalse)
836 return MagickFalse;
837 }
838
839 SkipRGBMipmaps(image, dds_info, 4);
840
841 return MagickTrue;
842}
843
844/*
845 Skip the mipmap images for compressed (DXTn) dds files
846*/
847static void SkipDXTMipmaps(Image *image, DDSInfo *dds_info, int texel_size)
848{
cristybb503372010-05-27 20:51:26 +0000849 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000850 i;
851
852 MagickOffsetType
853 offset;
854
cristybb503372010-05-27 20:51:26 +0000855 size_t
cristy3ed852e2009-09-05 21:47:34 +0000856 h,
857 w;
858
859 /*
860 Only skip mipmaps for textures and cube maps
861 */
862 if (dds_info->ddscaps1 & DDSCAPS_MIPMAP
863 && (dds_info->ddscaps1 & DDSCAPS_TEXTURE
864 || dds_info->ddscaps2 & DDSCAPS2_CUBEMAP))
865 {
866 w = DIV2(dds_info->width);
867 h = DIV2(dds_info->height);
868
869 /*
870 Mipmapcount includes the main image, so start from one
871 */
cristybb503372010-05-27 20:51:26 +0000872 for (i = 1; (i < (ssize_t) dds_info->mipmapcount) && w && h; i++)
cristy3ed852e2009-09-05 21:47:34 +0000873 {
874 offset = (MagickOffsetType) ((w + 3) / 4) * ((h + 3) / 4) * texel_size;
875 (void) SeekBlob(image, offset, SEEK_CUR);
876
877 w = DIV2(w);
878 h = DIV2(h);
879 }
880 }
881}
882
883/*
884 Skip the mipmap images for uncompressed (RGB or RGBA) dds files
885*/
886static void SkipRGBMipmaps(Image *image, DDSInfo *dds_info, int pixel_size)
887{
888 MagickOffsetType
889 offset;
890
cristybb503372010-05-27 20:51:26 +0000891 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000892 i;
893
cristybb503372010-05-27 20:51:26 +0000894 size_t
cristy3ed852e2009-09-05 21:47:34 +0000895 h,
896 w;
897
898 /*
899 Only skip mipmaps for textures and cube maps
900 */
901 if (dds_info->ddscaps1 & DDSCAPS_MIPMAP
902 && (dds_info->ddscaps1 & DDSCAPS_TEXTURE
903 || dds_info->ddscaps2 & DDSCAPS2_CUBEMAP))
904 {
905 w = DIV2(dds_info->width);
906 h = DIV2(dds_info->height);
907
908 /*
909 Mipmapcount includes the main image, so start from one
910 */
cristybb503372010-05-27 20:51:26 +0000911 for (i=1; (i < (ssize_t) dds_info->mipmapcount) && w && h; i++)
cristy3ed852e2009-09-05 21:47:34 +0000912 {
913 offset = (MagickOffsetType) w * h * pixel_size;
914 (void) SeekBlob(image, offset, SEEK_CUR);
915
916 w = DIV2(w);
917 h = DIV2(h);
918 }
919 }
920}
921
922/*
923%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
924% %
925% %
926% %
927% I s D D S %
928% %
929% %
930% %
931%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
932%
933% IsDDS() returns MagickTrue if the image format type, identified by the
934% magick string, is DDS.
935%
936% The format of the IsDDS method is:
937%
938% MagickBooleanType IsDDS(const unsigned char *magick,const size_t length)
939%
940% A description of each parameter follows:
941%
942% o magick: compare image format pattern against these bytes.
943%
944% o length: Specifies the length of the magick string.
945%
946*/
947static MagickBooleanType IsDDS(const unsigned char *magick,const size_t length)
948{
949 if (length < 4)
950 return(MagickFalse);
951 if (LocaleNCompare((char *) magick,"DDS ", 4) == 0)
952 return(MagickTrue);
953 return(MagickFalse);
954}
955
956/*
957%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
958% %
959% %
960% %
961% R e g i s t e r D D S I m a g e %
962% %
963% %
964% %
965%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
966%
967% RegisterDDSImage() adds attributes for the DDS image format to
968% the list of supported formats. The attributes include the image format
969% tag, a method to read and/or write the format, whether the format
970% supports the saving of more than one frame to the same file or blob,
971% whether the format supports native in-memory I/O, and a brief
972% description of the format.
973%
974% The format of the RegisterDDSImage method is:
975%
976% RegisterDDSImage(void)
977%
978*/
cristybb503372010-05-27 20:51:26 +0000979ModuleExport size_t RegisterDDSImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000980{
981 MagickInfo
982 *entry;
983
984 entry = SetMagickInfo("DDS");
985 entry->decoder = (DecodeImageHandler *) ReadDDSImage;
986 entry->magick = (IsImageFormatHandler *) IsDDS;
987 entry->description = ConstantString("Microsoft DirectDraw Surface");
988 entry->module = ConstantString("DDS");
989 (void) RegisterMagickInfo(entry);
990 return(MagickImageCoderSignature);
991}
992
993
994/*
995%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
996% %
997% %
998% %
999% U n r e g i s t e r D D S I m a g e %
1000% %
1001% %
1002% %
1003%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1004%
1005% UnregisterDDSImage() removes format registrations made by the
1006% DDS module from the list of supported formats.
1007%
1008% The format of the UnregisterDDSImage method is:
1009%
1010% UnregisterDDSImage(void)
1011%
1012*/
1013ModuleExport void UnregisterDDSImage(void)
1014{
1015 (void) UnregisterMagickInfo("DDS");
1016}
1017