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