blob: 6e2115d688efa76b796f8da4747b2056b7467a28 [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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
cristy3ed852e2009-09-05 21:47:34 +0000501 PixelPacket
502 *q;
503
cristybb503372010-05-27 20:51:26 +0000504 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000505 i,
506 x;
507
cristyf9a6c462011-04-24 14:01:11 +0000508 size_t
509 bits;
510
511 ssize_t
512 j,
513 y;
514
cristy3ed852e2009-09-05 21:47:34 +0000515 unsigned char
516 code;
517
cristyc5de6992009-10-06 19:19:48 +0000518 unsigned short
cristy3ed852e2009-09-05 21:47:34 +0000519 c0,
520 c1;
cristyc5de6992009-10-06 19:19:48 +0000521
cristy3ed852e2009-09-05 21:47:34 +0000522 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),
cristyf9a6c462011-04-24 14:01:11 +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);
cristyf9a6c462011-04-24 14:01:11 +0000549 SetRedPixelComponent(q,ScaleCharToQuantum(colors.r[code]));
550 SetGreenPixelComponent(q,ScaleCharToQuantum(colors.g[code]));
551 SetBluePixelComponent(q,ScaleCharToQuantum(colors.b[code]));
552 SetOpacityPixelComponent(q,ScaleCharToQuantum(colors.a[code]));
cristy3ed852e2009-09-05 21:47:34 +0000553 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;
cristyf9a6c462011-04-24 14:01:11 +0000634 SetRedPixelComponent(q,ScaleCharToQuantum(colors.r[code]));
635 SetGreenPixelComponent(q,ScaleCharToQuantum(colors.g[code]));
636 SetBluePixelComponent(q,ScaleCharToQuantum(colors.b[code]));
cristy3ed852e2009-09-05 21:47:34 +0000637 /*
638 Extract alpha value: multiply 0..15 by 17 to get range 0..255
639 */
640 if (j < 2)
641 alpha = 17U * (unsigned char) ((a0 >> (4*(4*j+i))) & 0xf);
642 else
643 alpha = 17U * (unsigned char) ((a1 >> (4*(4*(j-2)+i))) & 0xf);
cristy34d83b22011-05-30 00:41:18 +0000644 SetAlphaPixelComponent(q,ScaleCharToQuantum((unsigned char)
645 alpha));
cristy3ed852e2009-09-05 21:47:34 +0000646 q++;
647 }
648 }
649 }
650
651 if (SyncAuthenticPixels(image,exception) == MagickFalse)
652 return MagickFalse;
653 }
654 }
655
656 SkipDXTMipmaps(image, dds_info, 16);
657
658 return MagickTrue;
659}
660
661static MagickBooleanType ReadDXT5(Image *image, DDSInfo *dds_info)
662{
663 DDSColors
664 colors;
665
666 ExceptionInfo
667 *exception;
668
cristybb503372010-05-27 20:51:26 +0000669 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000670 j,
671 y;
672
673 MagickSizeType
674 alpha_bits;
675
676 PixelPacket
677 *q;
678
cristybb503372010-05-27 20:51:26 +0000679 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000680 i,
681 x;
682
683 unsigned char
684 a0,
685 a1;
686
cristybb503372010-05-27 20:51:26 +0000687 size_t
cristy3ed852e2009-09-05 21:47:34 +0000688 alpha,
689 bits,
690 code,
691 alpha_code;
692
693 unsigned short
694 c0,
695 c1;
696
697 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +0000698 for (y = 0; y < (ssize_t) dds_info->height; y += 4)
cristy3ed852e2009-09-05 21:47:34 +0000699 {
cristybb503372010-05-27 20:51:26 +0000700 for (x = 0; x < (ssize_t) dds_info->width; x += 4)
cristy3ed852e2009-09-05 21:47:34 +0000701 {
702 /* Get 4x4 patch of pixels to write on */
703 q = QueueAuthenticPixels(image, x, y, Min(4, dds_info->width - x),
704 Min(4, dds_info->height - y),exception);
705
706 if (q == (PixelPacket *) NULL)
707 return MagickFalse;
708
709 /* Read alpha values (8 bytes) */
710 a0 = (unsigned char) ReadBlobByte(image);
711 a1 = (unsigned char) ReadBlobByte(image);
712
713 alpha_bits = (MagickSizeType)ReadBlobLSBLong(image)
714 | ((MagickSizeType)ReadBlobLSBShort(image) << 32);
715
716 /* Read 8 bytes of data from the image */
717 c0 = ReadBlobLSBShort(image);
718 c1 = ReadBlobLSBShort(image);
719 bits = ReadBlobLSBLong(image);
720
721 CalculateColors(c0, c1, &colors, MagickTrue);
722
723 /* Write the pixels */
724 for (j = 0; j < 4; j++)
725 {
726 for (i = 0; i < 4; i++)
727 {
cristybb503372010-05-27 20:51:26 +0000728 if ((x + i) < (ssize_t) dds_info->width && (y + j) < (ssize_t) dds_info->height)
cristy3ed852e2009-09-05 21:47:34 +0000729 {
730 code = (bits >> ((4*j+i)*2)) & 0x3;
cristyf9a6c462011-04-24 14:01:11 +0000731 SetRedPixelComponent(q,ScaleCharToQuantum(colors.r[code]));
732 SetGreenPixelComponent(q,ScaleCharToQuantum(colors.g[code]));
733 SetBluePixelComponent(q,ScaleCharToQuantum(colors.b[code]));
cristy3ed852e2009-09-05 21:47:34 +0000734 /* Extract alpha value */
cristybb503372010-05-27 20:51:26 +0000735 alpha_code = (size_t) (alpha_bits >> (3*(4*j+i))) & 0x7;
cristy3ed852e2009-09-05 21:47:34 +0000736 if (alpha_code == 0)
737 alpha = a0;
738 else if (alpha_code == 1)
739 alpha = a1;
740 else if (a0 > a1)
741 alpha = ((8-alpha_code) * a0 + (alpha_code-1) * a1) / 7;
742 else if (alpha_code == 6)
743 alpha = alpha_code;
744 else if (alpha_code == 7)
745 alpha = 255;
746 else
747 alpha = (((6-alpha_code) * a0 + (alpha_code-1) * a1) / 5);
cristy34d83b22011-05-30 00:41:18 +0000748 SetAlphaPixelComponent(q,ScaleCharToQuantum((unsigned char)
749 alpha));
cristy3ed852e2009-09-05 21:47:34 +0000750 q++;
751 }
752 }
753 }
754
755 if (SyncAuthenticPixels(image,exception) == MagickFalse)
756 return MagickFalse;
757 }
758 }
759
760 SkipDXTMipmaps(image, dds_info, 16);
761
762 return MagickTrue;
763}
764
765static MagickBooleanType ReadUncompressedRGB(Image *image, DDSInfo *dds_info)
766{
767 ExceptionInfo
768 *exception;
769
cristybb503372010-05-27 20:51:26 +0000770 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000771 x, y;
772
773 PixelPacket
774 *q;
775
776 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +0000777 for (y = 0; y < (ssize_t) dds_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000778 {
779 q = QueueAuthenticPixels(image, 0, y, dds_info->width, 1,exception);
780
781 if (q == (PixelPacket *) NULL)
782 return MagickFalse;
783
cristybb503372010-05-27 20:51:26 +0000784 for (x = 0; x < (ssize_t) dds_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000785 {
cristyf9a6c462011-04-24 14:01:11 +0000786 SetBluePixelComponent(q,ScaleCharToQuantum((unsigned char)
787 ReadBlobByte(image)));
788 SetGreenPixelComponent(q,ScaleCharToQuantum((unsigned char)
789 ReadBlobByte(image)));
790 SetRedPixelComponent(q,ScaleCharToQuantum((unsigned char)
791 ReadBlobByte(image)));
cristy6305d192010-06-23 01:07:24 +0000792 if (dds_info->pixelformat.rgb_bitcount == 32)
793 (void) ReadBlobByte(image);
cristy3ed852e2009-09-05 21:47:34 +0000794 q++;
795 }
796
797 if (SyncAuthenticPixels(image,exception) == MagickFalse)
798 return MagickFalse;
799 }
800
801 SkipRGBMipmaps(image, dds_info, 3);
802
803 return MagickTrue;
804}
805
806static MagickBooleanType ReadUncompressedRGBA(Image *image, DDSInfo *dds_info)
807{
808 ExceptionInfo
809 *exception;
810
cristybb503372010-05-27 20:51:26 +0000811 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000812 x, y;
813
814 PixelPacket
815 *q;
816
817 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +0000818 for (y = 0; y < (ssize_t) dds_info->height; y++)
cristy3ed852e2009-09-05 21:47:34 +0000819 {
820 q = QueueAuthenticPixels(image, 0, y, dds_info->width, 1,exception);
821
822 if (q == (PixelPacket *) NULL)
823 return MagickFalse;
824
cristybb503372010-05-27 20:51:26 +0000825 for (x = 0; x < (ssize_t) dds_info->width; x++)
cristy3ed852e2009-09-05 21:47:34 +0000826 {
cristyf9a6c462011-04-24 14:01:11 +0000827 SetBluePixelComponent(q,ScaleCharToQuantum((unsigned char)
828 ReadBlobByte(image)));
829 SetGreenPixelComponent(q,ScaleCharToQuantum((unsigned char)
830 ReadBlobByte(image)));
831 SetRedPixelComponent(q,ScaleCharToQuantum((unsigned char)
832 ReadBlobByte(image)));
cristy34d83b22011-05-30 00:41:18 +0000833 SetAlphaPixelComponent(q,ScaleCharToQuantum((unsigned char)
834 ReadBlobByte(image)));
cristy3ed852e2009-09-05 21:47:34 +0000835 q++;
836 }
837
838 if (SyncAuthenticPixels(image,exception) == MagickFalse)
839 return MagickFalse;
840 }
841
842 SkipRGBMipmaps(image, dds_info, 4);
843
844 return MagickTrue;
845}
846
847/*
848 Skip the mipmap images for compressed (DXTn) dds files
849*/
850static void SkipDXTMipmaps(Image *image, DDSInfo *dds_info, int texel_size)
851{
cristybb503372010-05-27 20:51:26 +0000852 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000853 i;
854
855 MagickOffsetType
856 offset;
857
cristybb503372010-05-27 20:51:26 +0000858 size_t
cristy3ed852e2009-09-05 21:47:34 +0000859 h,
860 w;
861
862 /*
863 Only skip mipmaps for textures and cube maps
864 */
865 if (dds_info->ddscaps1 & DDSCAPS_MIPMAP
866 && (dds_info->ddscaps1 & DDSCAPS_TEXTURE
867 || dds_info->ddscaps2 & DDSCAPS2_CUBEMAP))
868 {
869 w = DIV2(dds_info->width);
870 h = DIV2(dds_info->height);
871
872 /*
873 Mipmapcount includes the main image, so start from one
874 */
cristybb503372010-05-27 20:51:26 +0000875 for (i = 1; (i < (ssize_t) dds_info->mipmapcount) && w && h; i++)
cristy3ed852e2009-09-05 21:47:34 +0000876 {
877 offset = (MagickOffsetType) ((w + 3) / 4) * ((h + 3) / 4) * texel_size;
878 (void) SeekBlob(image, offset, SEEK_CUR);
879
880 w = DIV2(w);
881 h = DIV2(h);
882 }
883 }
884}
885
886/*
887 Skip the mipmap images for uncompressed (RGB or RGBA) dds files
888*/
889static void SkipRGBMipmaps(Image *image, DDSInfo *dds_info, int pixel_size)
890{
891 MagickOffsetType
892 offset;
893
cristybb503372010-05-27 20:51:26 +0000894 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000895 i;
896
cristybb503372010-05-27 20:51:26 +0000897 size_t
cristy3ed852e2009-09-05 21:47:34 +0000898 h,
899 w;
900
901 /*
902 Only skip mipmaps for textures and cube maps
903 */
904 if (dds_info->ddscaps1 & DDSCAPS_MIPMAP
905 && (dds_info->ddscaps1 & DDSCAPS_TEXTURE
906 || dds_info->ddscaps2 & DDSCAPS2_CUBEMAP))
907 {
908 w = DIV2(dds_info->width);
909 h = DIV2(dds_info->height);
910
911 /*
912 Mipmapcount includes the main image, so start from one
913 */
cristybb503372010-05-27 20:51:26 +0000914 for (i=1; (i < (ssize_t) dds_info->mipmapcount) && w && h; i++)
cristy3ed852e2009-09-05 21:47:34 +0000915 {
916 offset = (MagickOffsetType) w * h * pixel_size;
917 (void) SeekBlob(image, offset, SEEK_CUR);
918
919 w = DIV2(w);
920 h = DIV2(h);
921 }
922 }
923}
924
925/*
926%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
927% %
928% %
929% %
930% I s D D S %
931% %
932% %
933% %
934%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
935%
936% IsDDS() returns MagickTrue if the image format type, identified by the
937% magick string, is DDS.
938%
939% The format of the IsDDS method is:
940%
941% MagickBooleanType IsDDS(const unsigned char *magick,const size_t length)
942%
943% A description of each parameter follows:
944%
945% o magick: compare image format pattern against these bytes.
946%
947% o length: Specifies the length of the magick string.
948%
949*/
950static MagickBooleanType IsDDS(const unsigned char *magick,const size_t length)
951{
952 if (length < 4)
953 return(MagickFalse);
954 if (LocaleNCompare((char *) magick,"DDS ", 4) == 0)
955 return(MagickTrue);
956 return(MagickFalse);
957}
958
959/*
960%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
961% %
962% %
963% %
964% R e g i s t e r D D S I m a g e %
965% %
966% %
967% %
968%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
969%
970% RegisterDDSImage() adds attributes for the DDS image format to
971% the list of supported formats. The attributes include the image format
972% tag, a method to read and/or write the format, whether the format
973% supports the saving of more than one frame to the same file or blob,
974% whether the format supports native in-memory I/O, and a brief
975% description of the format.
976%
977% The format of the RegisterDDSImage method is:
978%
979% RegisterDDSImage(void)
980%
981*/
cristybb503372010-05-27 20:51:26 +0000982ModuleExport size_t RegisterDDSImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000983{
984 MagickInfo
985 *entry;
986
987 entry = SetMagickInfo("DDS");
988 entry->decoder = (DecodeImageHandler *) ReadDDSImage;
989 entry->magick = (IsImageFormatHandler *) IsDDS;
cristyffaf9782011-04-13 19:50:51 +0000990 entry->seekable_stream=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +0000991 entry->description = ConstantString("Microsoft DirectDraw Surface");
992 entry->module = ConstantString("DDS");
993 (void) RegisterMagickInfo(entry);
994 return(MagickImageCoderSignature);
995}
996
997
998/*
999%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1000% %
1001% %
1002% %
1003% U n r e g i s t e r D D S I m a g e %
1004% %
1005% %
1006% %
1007%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1008%
1009% UnregisterDDSImage() removes format registrations made by the
1010% DDS module from the list of supported formats.
1011%
1012% The format of the UnregisterDDSImage method is:
1013%
1014% UnregisterDDSImage(void)
1015%
1016*/
1017ModuleExport void UnregisterDDSImage(void)
1018{
1019 (void) UnregisterMagickInfo("DDS");
1020}
1021