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