blob: ba97743b67a72faa4452b8e845cb69a87e769bf2 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% TTTTT GGGG AAA %
7% T G A A %
8% T G GG AAAAA %
9% T G G A A %
10% T GGG A A %
11% %
12% %
13% Read/Write Truevision Targa Image Format %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
20% Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization %
21% 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/color-private.h"
47#include "magick/colorspace.h"
48#include "magick/exception.h"
49#include "magick/exception-private.h"
50#include "magick/image.h"
51#include "magick/image-private.h"
52#include "magick/list.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/property.h"
58#include "magick/quantum-private.h"
59#include "magick/static.h"
60#include "magick/string_.h"
61#include "magick/module.h"
62
63/*
64 Forward declarations.
65*/
66static MagickBooleanType
67 WriteTGAImage(const ImageInfo *,Image *);
68
69/*
70%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
71% %
72% %
73% %
74% R e a d T G A I m a g e %
75% %
76% %
77% %
78%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79%
80% ReadTGAImage() reads a Truevision TGA image file and returns it.
81% It allocates the memory necessary for the new Image structure and returns
82% a pointer to the new image.
83%
84% The format of the ReadTGAImage method is:
85%
86% Image *ReadTGAImage(const ImageInfo *image_info,ExceptionInfo *exception)
87%
88% A description of each parameter follows:
89%
90% o image_info: the image info.
91%
92% o exception: return any errors or warnings in this structure.
93%
94*/
95static Image *ReadTGAImage(const ImageInfo *image_info,ExceptionInfo *exception)
96{
97#define TGAColormap 1
98#define TGARGB 2
99#define TGAMonochrome 3
100#define TGARLEColormap 9
101#define TGARLERGB 10
102#define TGARLEMonochrome 11
103
104 typedef struct _TGAInfo
105 {
106 unsigned char
107 id_length,
108 colormap_type,
109 image_type;
110
111 unsigned short
112 colormap_index,
113 colormap_length;
114
115 unsigned char
116 colormap_size;
117
118 unsigned short
119 x_origin,
120 y_origin,
121 width,
122 height;
123
124 unsigned char
125 bits_per_pixel,
126 attributes;
127 } TGAInfo;
128
129 Image
130 *image;
131
132 IndexPacket
133 index;
134
135 long
136 y;
137
138 MagickBooleanType
139 status;
140
141 PixelPacket
142 pixel;
143
144 register IndexPacket
145 *indexes;
146
147 register long
148 i,
149 x;
150
151 register PixelPacket
152 *q;
153
154 ssize_t
155 count;
156
157 TGAInfo
158 tga_info;
159
160 unsigned char
161 j,
162 k,
163 runlength;
164
165 unsigned long
166 base,
167 flag,
168 offset,
169 real,
170 skip;
171
172 /*
173 Open image file.
174 */
175 assert(image_info != (const ImageInfo *) NULL);
176 assert(image_info->signature == MagickSignature);
177 if (image_info->debug != MagickFalse)
178 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
179 image_info->filename);
180 assert(exception != (ExceptionInfo *) NULL);
181 assert(exception->signature == MagickSignature);
182 image=AcquireImage(image_info);
183 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
184 if (status == MagickFalse)
185 {
186 image=DestroyImageList(image);
187 return((Image *) NULL);
188 }
189 /*
190 Read TGA header information.
191 */
192 count=ReadBlob(image,1,&tga_info.id_length);
193 tga_info.colormap_type=(unsigned char) ReadBlobByte(image);
194 tga_info.image_type=(unsigned char) ReadBlobByte(image);
195 do
196 {
197 if ((count != 1) ||
198 ((tga_info.image_type != TGAColormap) &&
199 (tga_info.image_type != TGARGB) &&
200 (tga_info.image_type != TGAMonochrome) &&
201 (tga_info.image_type != TGARLEColormap) &&
202 (tga_info.image_type != TGARLERGB) &&
203 (tga_info.image_type != TGARLEMonochrome)) ||
204 (((tga_info.image_type == TGAColormap) ||
205 (tga_info.image_type == TGARLEColormap)) &&
206 (tga_info.colormap_type == 0)))
207 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
208 tga_info.colormap_index=ReadBlobLSBShort(image);
209 tga_info.colormap_length=ReadBlobLSBShort(image);
210 tga_info.colormap_size=(unsigned char) ReadBlobByte(image);
211 tga_info.x_origin=ReadBlobLSBShort(image);
212 tga_info.y_origin=ReadBlobLSBShort(image);
213 tga_info.width=(unsigned short) ReadBlobLSBShort(image);
214 tga_info.height=(unsigned short) ReadBlobLSBShort(image);
215 tga_info.bits_per_pixel=(unsigned char) ReadBlobByte(image);
216 tga_info.attributes=(unsigned char) ReadBlobByte(image);
217 if (EOFBlob(image) != MagickFalse)
218 ThrowReaderException(CorruptImageError,"UnableToReadImageData");
219 /*
220 Initialize image structure.
221 */
222 image->columns=tga_info.width;
223 image->rows=tga_info.height;
224 image->matte=tga_info.bits_per_pixel == 32 ? MagickTrue : MagickFalse;
225 if ((tga_info.image_type != TGAColormap) &&
226 (tga_info.image_type != TGARLEColormap))
227 image->depth=(unsigned long) ((tga_info.bits_per_pixel <= 8) ? 8 :
228 (tga_info.bits_per_pixel <= 16) ? 5 :
229 (tga_info.bits_per_pixel == 24) ? 8 :
230 (tga_info.bits_per_pixel == 32) ? 8 : 8);
231 else
232 image->depth=(unsigned long) ((tga_info.colormap_size <= 8) ? 8 :
233 (tga_info.colormap_size <= 16) ? 5 :
234 (tga_info.colormap_size == 24) ? 8 :
235 (tga_info.colormap_size == 32) ? 8 : 8);
236 if ((tga_info.image_type == TGAColormap) ||
237 (tga_info.image_type == TGAMonochrome) ||
238 (tga_info.image_type == TGARLEColormap) ||
239 (tga_info.image_type == TGARLEMonochrome))
240 image->storage_class=PseudoClass;
241 image->compression=NoCompression;
242 if ((tga_info.image_type == TGARLEColormap) ||
243 (tga_info.image_type == TGARLEMonochrome))
244 image->compression=RLECompression;
245 if (image->storage_class == PseudoClass)
246 {
247 if (tga_info.colormap_type != 0)
248 image->colors=tga_info.colormap_length;
249 else
250 {
251 image->colors=0x01U << tga_info.bits_per_pixel;
252 if (AcquireImageColormap(image,image->colors) == MagickFalse)
253 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
254 }
255 }
256 if (tga_info.id_length != 0)
257 {
258 char
259 *comment;
260
261 size_t
262 length;
263
264 /*
265 TGA image comment.
266 */
267 length=(size_t) tga_info.id_length;
268 comment=(char *) NULL;
269 if (~length >= MaxTextExtent)
270 comment=(char *) AcquireQuantumMemory(length+MaxTextExtent,
271 sizeof(*comment));
272 if (comment == (char *) NULL)
273 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
274 count=ReadBlob(image,tga_info.id_length,(unsigned char *) comment);
275 comment[tga_info.id_length]='\0';
276 (void) SetImageProperty(image,"comment",comment);
277 comment=DestroyString(comment);
278 }
279 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
280 pixel.opacity=(Quantum) OpaqueOpacity;
281 if (tga_info.colormap_type != 0)
282 {
283 /*
284 Read TGA raster colormap.
285 */
286 if (AcquireImageColormap(image,image->colors) == MagickFalse)
287 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
288 for (i=0; i < (long) image->colors; i++)
289 {
290 switch (tga_info.colormap_size)
291 {
292 case 8:
293 default:
294 {
295 /*
296 Gray scale.
297 */
298 pixel.red=ScaleCharToQuantum((unsigned char) ReadBlobByte(image));
299 pixel.green=pixel.red;
300 pixel.blue=pixel.red;
301 break;
302 }
303 case 15:
304 case 16:
305 {
306 QuantumAny
307 range;
308
309 /*
310 5 bits each of red green and blue.
311 */
312 j=(unsigned char) ReadBlobByte(image);
313 k=(unsigned char) ReadBlobByte(image);
314 range=GetQuantumRange(5UL);
315 pixel.red=ScaleAnyToQuantum(1UL*(k & 0x7c) >> 2,range);
316 pixel.green=ScaleAnyToQuantum((1UL*(k & 0x03) << 3)+
317 (1UL*(j & 0xe0) >> 5),range);
318 pixel.blue=ScaleAnyToQuantum(1UL*(j & 0x1f),range);
319 break;
320 }
321 case 24:
322 case 32:
323 {
324 /*
325 8 bits each of blue, green and red.
326 */
327 pixel.blue=ScaleCharToQuantum((unsigned char)
328 ReadBlobByte(image));
329 pixel.green=ScaleCharToQuantum((unsigned char)
330 ReadBlobByte(image));
331 pixel.red=ScaleCharToQuantum((unsigned char)
332 ReadBlobByte(image));
333 break;
334 }
335 }
336 image->colormap[i]=pixel;
337 }
338 }
339 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
340 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
341 break;
342 /*
343 Convert TGA pixels to pixel packets.
344 */
345 base=0;
346 flag=0;
347 skip=MagickFalse;
348 real=0;
349 index=(IndexPacket) 0;
350 runlength=0;
351 offset=0;
352 for (y=0; y < (long) image->rows; y++)
353 {
354 real=offset;
355 if (((unsigned char) (tga_info.attributes & 0x20) >> 5) == 0)
356 real=image->rows-real-1;
357 q=QueueAuthenticPixels(image,0,(long) real,image->columns,1,exception);
358 if (q == (PixelPacket *) NULL)
359 break;
360 indexes=GetAuthenticIndexQueue(image);
361 for (x=0; x < (long) image->columns; x++)
362 {
363 if ((tga_info.image_type == TGARLEColormap) ||
364 (tga_info.image_type == TGARLERGB) ||
365 (tga_info.image_type == TGARLEMonochrome))
366 {
367 if (runlength != 0)
368 {
369 runlength--;
370 skip=flag != 0;
371 }
372 else
373 {
374 count=ReadBlob(image,1,&runlength);
375 if (count == 0)
376 ThrowReaderException(CorruptImageError,
377 "UnableToReadImageData");
378 flag=runlength & 0x80;
379 if (flag != 0)
380 runlength-=128;
381 skip=MagickFalse;
382 }
383 }
384 if (skip == MagickFalse)
385 switch (tga_info.bits_per_pixel)
386 {
387 case 8:
388 default:
389 {
390 /*
391 Gray scale.
392 */
393 index=(IndexPacket) ReadBlobByte(image);
394 if (tga_info.colormap_type != 0)
395 pixel=image->colormap[(long) ConstrainColormapIndex(image,
396 1UL*index)];
397 else
398 {
399 pixel.red=ScaleCharToQuantum((unsigned char) index);
400 pixel.green=ScaleCharToQuantum((unsigned char) index);
401 pixel.blue=ScaleCharToQuantum((unsigned char) index);
402 }
403 break;
404 }
405 case 15:
406 case 16:
407 {
408 QuantumAny
409 range;
410
411 /*
412 5 bits each of red green and blue.
413 */
414 j=(unsigned char) ReadBlobByte(image);
415 k=(unsigned char) ReadBlobByte(image);
416 range=GetQuantumRange(5UL);
417 pixel.red=ScaleAnyToQuantum(1UL*(k & 0x7c) >> 2,range);
418 pixel.green=ScaleAnyToQuantum((1UL*(k & 0x03) << 3)+
419 (1UL*(j & 0xe0) >> 5),range);
420 pixel.blue=ScaleAnyToQuantum(1UL*(j & 0x1f),range);
421 if (image->matte != MagickFalse)
422 pixel.opacity=(k & 0x80) != 0 ? (Quantum) OpaqueOpacity :
423 (Quantum) TransparentOpacity;
424 if (image->storage_class == PseudoClass)
425 index=ConstrainColormapIndex(image,((unsigned long) k << 8)+j);
426 break;
427 }
428 case 24:
429 case 32:
430 {
431 /*
432 8 bits each of blue green and red.
433 */
434 pixel.blue=ScaleCharToQuantum((unsigned char)
435 ReadBlobByte(image));
436 pixel.green=ScaleCharToQuantum((unsigned char)
437 ReadBlobByte(image));
438 pixel.red=ScaleCharToQuantum((unsigned char) ReadBlobByte(image));
439 if (tga_info.bits_per_pixel == 32)
440 pixel.opacity=(Quantum) (QuantumRange-ScaleCharToQuantum(
441 (unsigned char) ReadBlobByte(image)));
442 break;
443 }
444 }
445 if (status == MagickFalse)
446 ThrowReaderException(CorruptImageError,"UnableToReadImageData");
447 if (image->storage_class == PseudoClass)
448 indexes[x]=index;
449 q->red=pixel.red;
450 q->green=pixel.green;
451 q->blue=pixel.blue;
452 if (image->matte != MagickFalse)
453 q->opacity=pixel.opacity;
454 q++;
455 }
456 if (((unsigned char) (tga_info.attributes & 0xc0) >> 6) == 4)
457 offset+=4;
458 else
459 if (((unsigned char) (tga_info.attributes & 0xc0) >> 6) == 2)
460 offset+=2;
461 else
462 offset++;
463 if (offset >= image->rows)
464 {
465 base++;
466 offset=base;
467 }
468 if (SyncAuthenticPixels(image,exception) == MagickFalse)
469 break;
470 if (image->previous == (Image *) NULL)
471 {
472 status=SetImageProgress(image,LoadImageTag,y,image->rows);
473 if (status == MagickFalse)
474 break;
475 }
476 }
477 if (EOFBlob(image) != MagickFalse)
478 {
479 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
480 image->filename);
481 break;
482 }
483 /*
484 Proceed to next image.
485 */
486 if (image_info->number_scenes != 0)
487 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
488 break;
489 count=ReadBlob(image,1,&tga_info.id_length);
490 tga_info.colormap_type=(unsigned char) ReadBlobByte(image);
491 tga_info.image_type=(unsigned char) ReadBlobByte(image);
492 status=((tga_info.image_type == TGAColormap) ||
493 (tga_info.image_type == TGARGB) ||
494 (tga_info.image_type == TGAMonochrome) ||
495 (tga_info.image_type == TGARLEColormap) ||
496 (tga_info.image_type == TGARLERGB) ||
497 (tga_info.image_type == TGARLEMonochrome)) ? MagickTrue : MagickFalse;
498 if (status == MagickTrue)
499 {
500 /*
501 Allocate next image structure.
502 */
503 AcquireNextImage(image_info,image);
504 if (GetNextImageInList(image) == (Image *) NULL)
505 {
506 image=DestroyImageList(image);
507 return((Image *) NULL);
508 }
509 image=SyncNextImageInList(image);
510 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
511 GetBlobSize(image));
512 if (status == MagickFalse)
513 break;
514 }
515 } while (status == MagickTrue);
516 (void) CloseBlob(image);
517 return(GetFirstImageInList(image));
518}
519
520/*
521%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
522% %
523% %
524% %
525% R e g i s t e r T G A I m a g e %
526% %
527% %
528% %
529%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
530%
531% RegisterTGAImage() adds properties for the TGA image format to
532% the list of supported formats. The properties include the image format
533% tag, a method to read and/or write the format, whether the format
534% supports the saving of more than one frame to the same file or blob,
535% whether the format supports native in-memory I/O, and a brief
536% description of the format.
537%
538% The format of the RegisterTGAImage method is:
539%
540% unsigned long RegisterTGAImage(void)
541%
542*/
543ModuleExport unsigned long RegisterTGAImage(void)
544{
545 MagickInfo
546 *entry;
547
548 entry=SetMagickInfo("ICB");
549 entry->decoder=(DecodeImageHandler *) ReadTGAImage;
550 entry->encoder=(EncodeImageHandler *) WriteTGAImage;
551 entry->description=ConstantString("Truevision Targa image");
552 entry->module=ConstantString("TGA");
553 (void) RegisterMagickInfo(entry);
554 entry=SetMagickInfo("TGA");
555 entry->decoder=(DecodeImageHandler *) ReadTGAImage;
556 entry->encoder=(EncodeImageHandler *) WriteTGAImage;
557 entry->description=ConstantString("Truevision Targa image");
558 entry->module=ConstantString("TGA");
559 (void) RegisterMagickInfo(entry);
560 entry=SetMagickInfo("VDA");
561 entry->decoder=(DecodeImageHandler *) ReadTGAImage;
562 entry->encoder=(EncodeImageHandler *) WriteTGAImage;
563 entry->description=ConstantString("Truevision Targa image");
564 entry->module=ConstantString("TGA");
565 (void) RegisterMagickInfo(entry);
566 entry=SetMagickInfo("VST");
567 entry->decoder=(DecodeImageHandler *) ReadTGAImage;
568 entry->encoder=(EncodeImageHandler *) WriteTGAImage;
569 entry->description=ConstantString("Truevision Targa image");
570 entry->module=ConstantString("TGA");
571 (void) RegisterMagickInfo(entry);
572 return(MagickImageCoderSignature);
573}
574
575/*
576%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
577% %
578% %
579% %
580% U n r e g i s t e r T G A I m a g e %
581% %
582% %
583% %
584%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
585%
586% UnregisterTGAImage() removes format registrations made by the
587% TGA module from the list of supported formats.
588%
589% The format of the UnregisterTGAImage method is:
590%
591% UnregisterTGAImage(void)
592%
593*/
594ModuleExport void UnregisterTGAImage(void)
595{
596 (void) UnregisterMagickInfo("ICB");
597 (void) UnregisterMagickInfo("TGA");
598 (void) UnregisterMagickInfo("VDA");
599 (void) UnregisterMagickInfo("VST");
600}
601
602/*
603%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
604% %
605% %
606% %
607% W r i t e T G A I m a g e %
608% %
609% %
610% %
611%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
612%
613% WriteTGAImage() writes a image in the Truevision Targa rasterfile
614% format.
615%
616% The format of the WriteTGAImage method is:
617%
618% MagickBooleanType WriteTGAImage(const ImageInfo *image_info,Image *image)
619%
620% A description of each parameter follows.
621%
622% o image_info: the image info.
623%
624% o image: The image.
625%
626*/
627
628static inline size_t MagickMin(const size_t x,const size_t y)
629{
630 if (x < y)
631 return(x);
632 return(y);
633}
634
635static MagickBooleanType WriteTGAImage(const ImageInfo *image_info,Image *image)
636{
637#define TargaColormap 1
638#define TargaRGB 2
639#define TargaMonochrome 3
640#define TargaRLEColormap 9
641#define TargaRLERGB 10
642#define TargaRLEMonochrome 11
643
644 typedef struct _TargaInfo
645 {
646 unsigned char
647 id_length,
648 colormap_type,
649 image_type;
650
651 unsigned short
652 colormap_index,
653 colormap_length;
654
655 unsigned char
656 colormap_size;
657
658 unsigned short
659 x_origin,
660 y_origin,
661 width,
662 height;
663
664 unsigned char
665 bits_per_pixel,
666 attributes;
667 } TargaInfo;
668
669 const char
670 *value;
671
672 long
673 y;
674
675 MagickBooleanType
676 status;
677
678 MagickOffsetType
679 scene;
680
681 register const IndexPacket
682 *indexes;
683
684 register const PixelPacket
685 *p;
686
687 register long
688 x;
689
690 register long
691 i;
692
693 register unsigned char
694 *q;
695
696 ssize_t
697 count;
698
699 TargaInfo
700 targa_info;
701
702 unsigned char
703 *targa_pixels;
704
705 /*
706 Open output image file.
707 */
708 assert(image_info != (const ImageInfo *) NULL);
709 assert(image_info->signature == MagickSignature);
710 assert(image != (Image *) NULL);
711 assert(image->signature == MagickSignature);
712 if (image->debug != MagickFalse)
713 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
714 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
715 if (status == MagickFalse)
716 return(status);
717 scene=0;
718 do
719 {
720 /*
721 Initialize TGA raster file header.
722 */
723 if ((image->columns > 65535L) || (image->rows > 65535L))
724 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
725 if (image->colorspace != RGBColorspace)
726 (void) TransformImageColorspace(image,RGBColorspace);
727 targa_info.id_length=0;
728 value=GetImageProperty(image,"comment");
729 if (value != (const char *) NULL)
730 targa_info.id_length=(unsigned char) MagickMin(strlen(value),255);
731 targa_info.colormap_type=0;
732 targa_info.colormap_index=0;
733 targa_info.colormap_length=0;
734 targa_info.colormap_size=0;
735 targa_info.x_origin=0;
736 targa_info.y_origin=0;
737 targa_info.width=(unsigned short) image->columns;
738 targa_info.height=(unsigned short) image->rows;
739 targa_info.bits_per_pixel=8;
740 targa_info.attributes=0;
741 if ((image_info->type != TrueColorType) &&
742 (image_info->type != TrueColorMatteType) &&
743 (image_info->type != PaletteType) &&
744 (image->matte == MagickFalse) &&
745 (IsGrayImage(image,&image->exception) != MagickFalse))
746 targa_info.image_type=TargaMonochrome;
747 else
748 if ((image->storage_class == DirectClass) || (image->colors > 256))
749 {
750 /*
751 Full color TGA raster.
752 */
753 targa_info.image_type=TargaRGB;
754 targa_info.bits_per_pixel=24;
755 if (image->matte != MagickFalse)
756 {
757 targa_info.bits_per_pixel=32;
758 targa_info.attributes=8; /* # of alpha bits */
759 }
760 }
761 else
762 {
763 /*
764 Colormapped TGA raster.
765 */
766 targa_info.image_type=TargaColormap;
767 targa_info.colormap_type=1;
768 targa_info.colormap_length=(unsigned short) image->colors;
769 targa_info.colormap_size=24;
770 }
771 /*
772 Write TGA header.
773 */
774 (void) WriteBlobByte(image,targa_info.id_length);
775 (void) WriteBlobByte(image,targa_info.colormap_type);
776 (void) WriteBlobByte(image,targa_info.image_type);
777 (void) WriteBlobLSBShort(image,targa_info.colormap_index);
778 (void) WriteBlobLSBShort(image,targa_info.colormap_length);
779 (void) WriteBlobByte(image,targa_info.colormap_size);
780 (void) WriteBlobLSBShort(image,targa_info.x_origin);
781 (void) WriteBlobLSBShort(image,targa_info.y_origin);
782 (void) WriteBlobLSBShort(image,targa_info.width);
783 (void) WriteBlobLSBShort(image,targa_info.height);
784 (void) WriteBlobByte(image,targa_info.bits_per_pixel);
785 (void) WriteBlobByte(image,targa_info.attributes);
786 if (targa_info.id_length != 0)
787 (void) WriteBlob(image,targa_info.id_length,(unsigned char *)
788 value);
789 if (targa_info.image_type == TargaColormap)
790 {
791 unsigned char
792 *targa_colormap;
793
794 /*
795 Dump colormap to file (blue, green, red byte order).
796 */
797 targa_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
798 targa_info.colormap_length,3UL*sizeof(*targa_colormap));
799 if (targa_colormap == (unsigned char *) NULL)
800 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
801 q=targa_colormap;
802 for (i=0; i < (long) image->colors; i++)
803 {
804 *q++=ScaleQuantumToChar(image->colormap[i].blue);
805 *q++=ScaleQuantumToChar(image->colormap[i].green);
806 *q++=ScaleQuantumToChar(image->colormap[i].red);
807 }
808 (void) WriteBlob(image,(size_t) (3*targa_info.colormap_length),
809 targa_colormap);
810 targa_colormap=(unsigned char *) RelinquishMagickMemory(targa_colormap);
811 }
812 /*
813 Convert MIFF to TGA raster pixels.
814 */
815 count=(ssize_t) (targa_info.bits_per_pixel*targa_info.width)/8;
816 targa_pixels=(unsigned char *) AcquireQuantumMemory((size_t) count,
817 sizeof(*targa_pixels));
818 if (targa_pixels == (unsigned char *) NULL)
819 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
820 for (y=(long) (image->rows-1); y >= 0; y--)
821 {
822 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
823 if (p == (const PixelPacket *) NULL)
824 break;
825 q=targa_pixels;
826 indexes=GetVirtualIndexQueue(image);
827 for (x=0; x < (long) image->columns; x++)
828 {
829 if (targa_info.image_type == TargaColormap)
830 *q++=(unsigned char) indexes[x];
831 else
832 if (targa_info.image_type == TargaMonochrome)
833 *q++=(unsigned char) ScaleQuantumToChar(PixelIntensityToQuantum(p));
834 else
835 {
836 *q++=ScaleQuantumToChar(p->blue);
837 *q++=ScaleQuantumToChar(p->green);
838 *q++=ScaleQuantumToChar(p->red);
839 if (image->matte != MagickFalse)
840 *q++=(unsigned char) ScaleQuantumToChar((Quantum)
841 (QuantumRange-p->opacity));
842 if (image->colorspace == CMYKColorspace)
843 *q++=ScaleQuantumToChar(indexes[x]);
844 }
845 p++;
846 }
847 (void) WriteBlob(image,(size_t) (q-targa_pixels),targa_pixels);
848 if (image->previous == (Image *) NULL)
849 {
850 status=SetImageProgress(image,SaveImageTag,y,image->rows);
851 if (status == MagickFalse)
852 break;
853 }
854 }
855 targa_pixels=(unsigned char *) RelinquishMagickMemory(targa_pixels);
856 if (GetNextImageInList(image) == (Image *) NULL)
857 break;
858 image=SyncNextImageInList(image);
859 status=SetImageProgress(image,SaveImagesTag,scene++,
860 GetImageListLength(image));
861 if (status == MagickFalse)
862 break;
863 } while (image_info->adjoin != MagickFalse);
864 (void) CloseBlob(image);
865 return(MagickTrue);
866}