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