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 %                                   Cristy                                    %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2021 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 %    https://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 "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/attribute.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/blob-private.h"
47 #include "MagickCore/cache.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colormap.h"
50 #include "MagickCore/colormap-private.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/colorspace-private.h"
53 #include "MagickCore/exception.h"
54 #include "MagickCore/exception-private.h"
55 #include "MagickCore/image.h"
56 #include "MagickCore/image-private.h"
57 #include "MagickCore/list.h"
58 #include "MagickCore/magick.h"
59 #include "MagickCore/memory_.h"
60 #include "MagickCore/monitor.h"
61 #include "MagickCore/monitor-private.h"
62 #include "MagickCore/option.h"
63 #include "MagickCore/pixel-accessor.h"
64 #include "MagickCore/property.h"
65 #include "MagickCore/quantum-private.h"
66 #include "MagickCore/static.h"
67 #include "MagickCore/string_.h"
68 #include "MagickCore/module.h"
69 
70 /*
71   Enumerated declaractions.
72 */
73 typedef enum
74 {
75   TGAColormap = 1,
76   TGARGB = 2,
77   TGAMonochrome = 3,
78   TGARLEColormap = 9,
79   TGARLERGB = 10,
80   TGARLEMonochrome = 11
81 } TGAImageType;
82 
83 /*
84   Typedef declaractions.
85 */
86 typedef struct _TGAInfo
87 {
88   TGAImageType
89     image_type;
90 
91   unsigned char
92     id_length,
93     colormap_type;
94 
95   unsigned short
96     colormap_index,
97     colormap_length;
98 
99   unsigned char
100     colormap_size;
101 
102   unsigned short
103     x_origin,
104     y_origin,
105     width,
106     height;
107 
108   unsigned char
109     bits_per_pixel,
110     attributes;
111 } TGAInfo;
112 
113 /*
114   Forward declarations.
115 */
116 static MagickBooleanType
117   WriteTGAImage(const ImageInfo *,Image *,ExceptionInfo *);
118 
119 /*
120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
121 %                                                                             %
122 %                                                                             %
123 %                                                                             %
124 %   R e a d T G A I m a g e                                                   %
125 %                                                                             %
126 %                                                                             %
127 %                                                                             %
128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
129 %
130 %  ReadTGAImage() reads a Truevision TGA image file and returns it.
131 %  It allocates the memory necessary for the new Image structure and returns
132 %  a pointer to the new image.
133 %
134 %  The format of the ReadTGAImage method is:
135 %
136 %      Image *ReadTGAImage(const ImageInfo *image_info,ExceptionInfo *exception)
137 %
138 %  A description of each parameter follows:
139 %
140 %    o image_info: the image info.
141 %
142 %    o exception: return any errors or warnings in this structure.
143 %
144 */
ReadTGAImage(const ImageInfo * image_info,ExceptionInfo * exception)145 static Image *ReadTGAImage(const ImageInfo *image_info,ExceptionInfo *exception)
146 {
147   Image
148     *image;
149 
150   MagickBooleanType
151     status;
152 
153   PixelInfo
154     pixel;
155 
156   Quantum
157     index;
158 
159   Quantum
160     *q;
161 
162   ssize_t
163     i,
164     x;
165 
166   size_t
167     base,
168     flag,
169     skip;
170 
171   ssize_t
172     count,
173     offset,
174     y;
175 
176   TGAInfo
177     tga_info;
178 
179   unsigned char
180     j,
181     k,
182     pixels[4],
183     runlength;
184 
185   unsigned int
186     alpha_bits;
187 
188   /*
189     Open image file.
190   */
191   assert(image_info != (const ImageInfo *) NULL);
192   assert(image_info->signature == MagickCoreSignature);
193   if (image_info->debug != MagickFalse)
194     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
195       image_info->filename);
196   assert(exception != (ExceptionInfo *) NULL);
197   assert(exception->signature == MagickCoreSignature);
198   image=AcquireImage(image_info,exception);
199   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
200   if (status == MagickFalse)
201     {
202       image=DestroyImageList(image);
203       return((Image *) NULL);
204     }
205   /*
206     Read TGA header information.
207   */
208   count=ReadBlob(image,1,&tga_info.id_length);
209   tga_info.colormap_type=(unsigned char) ReadBlobByte(image);
210   tga_info.image_type=(TGAImageType) ReadBlobByte(image);
211   if ((count != 1) ||
212       ((tga_info.image_type != TGAColormap) &&
213        (tga_info.image_type != TGARGB) &&
214        (tga_info.image_type != TGAMonochrome) &&
215        (tga_info.image_type != TGARLEColormap) &&
216        (tga_info.image_type != TGARLERGB) &&
217        (tga_info.image_type != TGARLEMonochrome)) ||
218       (((tga_info.image_type == TGAColormap) ||
219        (tga_info.image_type == TGARLEColormap)) &&
220        (tga_info.colormap_type == 0)))
221     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
222   tga_info.colormap_index=ReadBlobLSBShort(image);
223   tga_info.colormap_length=ReadBlobLSBShort(image);
224   tga_info.colormap_size=(unsigned char) ReadBlobByte(image);
225   tga_info.x_origin=ReadBlobLSBShort(image);
226   tga_info.y_origin=ReadBlobLSBShort(image);
227   tga_info.width=(unsigned short) ReadBlobLSBShort(image);
228   tga_info.height=(unsigned short) ReadBlobLSBShort(image);
229   tga_info.bits_per_pixel=(unsigned char) ReadBlobByte(image);
230   tga_info.attributes=(unsigned char) ReadBlobByte(image);
231   if (EOFBlob(image) != MagickFalse)
232     ThrowReaderException(CorruptImageError,"UnableToReadImageData");
233   if ((((tga_info.bits_per_pixel <= 1) || (tga_info.bits_per_pixel >= 17)) &&
234        (tga_info.bits_per_pixel != 24) && (tga_info.bits_per_pixel != 32)))
235     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
236   /*
237     Initialize image structure.
238   */
239   image->columns=tga_info.width;
240   image->rows=tga_info.height;
241   if ((tga_info.image_type != TGAMonochrome) &&
242       (tga_info.image_type != TGARLEMonochrome))
243     {
244       alpha_bits=(tga_info.attributes & 0x0FU);
245       image->alpha_trait=(alpha_bits > 0) || (tga_info.bits_per_pixel == 32) ||
246         (tga_info.colormap_size == 32) ?  BlendPixelTrait : UndefinedPixelTrait;
247     }
248   if ((tga_info.image_type != TGAColormap) &&
249       (tga_info.image_type != TGARLEColormap))
250     image->depth=(size_t) ((tga_info.bits_per_pixel <= 8) ? 8 :
251       (tga_info.bits_per_pixel <= 16) ? 5 : 8);
252   else
253     image->depth=(size_t) ((tga_info.colormap_size <= 8) ? 8 :
254       (tga_info.colormap_size <= 16) ? 5 : 8);
255   if ((tga_info.image_type == TGAColormap) ||
256       (tga_info.image_type == TGARLEColormap))
257     image->storage_class=PseudoClass;
258   if ((tga_info.image_type == TGAMonochrome) ||
259       (tga_info.image_type == TGARLEMonochrome))
260     {
261       image->type=GrayscaleType;
262       image->colorspace=GRAYColorspace;
263     }
264   image->compression=NoCompression;
265   if ((tga_info.image_type == TGARLEColormap) ||
266       (tga_info.image_type == TGARLEMonochrome) ||
267       (tga_info.image_type == TGARLERGB))
268     image->compression=RLECompression;
269   if (image->storage_class == PseudoClass)
270     {
271       if (tga_info.colormap_type != 0)
272         image->colors=tga_info.colormap_index+tga_info.colormap_length;
273       else
274         {
275           size_t
276             one;
277 
278           one=1;
279           image->colors=one << tga_info.bits_per_pixel;
280           if ((MagickSizeType) image->colors > GetBlobSize(image))
281             ThrowReaderException(CorruptImageError,
282               "InsufficientImageDataInFile");
283           if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
284             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
285         }
286     }
287   if (tga_info.id_length != 0)
288     {
289       char
290         *comment;
291 
292       size_t
293         length;
294 
295       /*
296         TGA image comment.
297       */
298       length=(size_t) tga_info.id_length;
299       comment=(char *) NULL;
300       if (~length >= (MagickPathExtent-1))
301         comment=(char *) AcquireQuantumMemory(length+MagickPathExtent,
302           sizeof(*comment));
303       if (comment == (char *) NULL)
304         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
305       count=ReadBlob(image,length,(unsigned char *) comment);
306       if (count == (ssize_t) length)
307         {
308           comment[length]='\0';
309           (void) SetImageProperty(image,"comment",comment,exception);
310         }
311       comment=DestroyString(comment);
312     }
313   image->orientation=BottomLeftOrientation;
314   if ((tga_info.attributes & (1UL << 4)) == 0)
315     {
316       if ((tga_info.attributes & (1UL << 5)) == 0)
317         image->orientation=BottomLeftOrientation;
318       else
319         image->orientation=TopLeftOrientation;
320     }
321   else
322     {
323       if ((tga_info.attributes & (1UL << 5)) == 0)
324         image->orientation=BottomRightOrientation;
325       else
326         image->orientation=TopRightOrientation;
327     }
328   if (image_info->ping != MagickFalse)
329     {
330       (void) CloseBlob(image);
331       return(image);
332     }
333   status=SetImageExtent(image,image->columns,image->rows,exception);
334   if (status == MagickFalse)
335     return(DestroyImageList(image));
336   (void) memset(&pixel,0,sizeof(pixel));
337   pixel.alpha=(MagickRealType) OpaqueAlpha;
338   if (tga_info.colormap_type != 0)
339     {
340       /*
341         Read TGA raster colormap.
342       */
343       if (image->colors < tga_info.colormap_index)
344         image->colors=tga_info.colormap_index;
345       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
346         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
347       for (i=0; i < (ssize_t) tga_info.colormap_index; i++)
348         image->colormap[i]=pixel;
349       for ( ; i < (ssize_t) image->colors; i++)
350       {
351         switch (tga_info.colormap_size)
352         {
353           case 8:
354           default:
355           {
356             /*
357               Gray scale.
358             */
359             pixel.red=(MagickRealType) ScaleCharToQuantum((unsigned char)
360               ReadBlobByte(image));
361             pixel.green=pixel.red;
362             pixel.blue=pixel.red;
363             break;
364           }
365           case 15:
366           case 16:
367           {
368             QuantumAny
369               range;
370 
371             /*
372               5 bits each of red green and blue.
373             */
374             j=(unsigned char) ReadBlobByte(image);
375             k=(unsigned char) ReadBlobByte(image);
376             range=GetQuantumRange(5UL);
377             pixel.red=(MagickRealType) ScaleAnyToQuantum(1UL*(k & 0x7c) >> 2,
378               range);
379             pixel.green=(MagickRealType) ScaleAnyToQuantum((1UL*(k & 0x03)
380               << 3)+(1UL*(j & 0xe0) >> 5),range);
381             pixel.blue=(MagickRealType) ScaleAnyToQuantum(1UL*(j & 0x1f),range);
382             break;
383           }
384           case 24:
385           {
386             /*
387               8 bits each of blue, green and red.
388             */
389             pixel.blue=(MagickRealType) ScaleCharToQuantum((unsigned char)
390               ReadBlobByte(image));
391             pixel.green=(MagickRealType) ScaleCharToQuantum((unsigned char)
392               ReadBlobByte(image));
393             pixel.red=(MagickRealType) ScaleCharToQuantum((unsigned char)
394               ReadBlobByte(image));
395             break;
396           }
397           case 32:
398           {
399             /*
400               8 bits each of blue, green, red, and alpha.
401             */
402             pixel.blue=(MagickRealType) ScaleCharToQuantum((unsigned char)
403               ReadBlobByte(image));
404             pixel.green=(MagickRealType) ScaleCharToQuantum((unsigned char)
405               ReadBlobByte(image));
406             pixel.red=(MagickRealType) ScaleCharToQuantum((unsigned char)
407               ReadBlobByte(image));
408             pixel.alpha=(MagickRealType) ScaleCharToQuantum((unsigned char)
409               ReadBlobByte(image));
410             break;
411           }
412         }
413         image->colormap[i]=pixel;
414       }
415     }
416   /*
417     Convert TGA pixels to pixel packets.
418   */
419   base=0;
420   flag=0;
421   skip=MagickFalse;
422   index=0;
423   runlength=0;
424   offset=0;
425   for (y=0; y < (ssize_t) image->rows; y++)
426   {
427     q=QueueAuthenticPixels(image,0,offset,image->columns,1,exception);
428     if (q == (Quantum *) NULL)
429       break;
430     for (x=0; x < (ssize_t) image->columns; x++)
431     {
432       if ((tga_info.image_type == TGARLEColormap) ||
433           (tga_info.image_type == TGARLERGB) ||
434           (tga_info.image_type == TGARLEMonochrome))
435         {
436           if (runlength != 0)
437             {
438               runlength--;
439               skip=flag != 0;
440             }
441           else
442             {
443               count=ReadBlob(image,1,&runlength);
444               if (count != 1)
445                 ThrowReaderException(CorruptImageError,"UnableToReadImageData");
446               flag=runlength & 0x80;
447               if (flag != 0)
448                 runlength-=128;
449               skip=MagickFalse;
450             }
451         }
452       if (skip == MagickFalse)
453         switch (tga_info.bits_per_pixel)
454         {
455           case 8:
456           default:
457           {
458             /*
459               Gray scale.
460             */
461             if (ReadBlob(image,1,pixels) != 1)
462               ThrowReaderException(CorruptImageError,"UnableToReadImageData");
463             index=(Quantum) pixels[0];
464             if (tga_info.colormap_type != 0)
465               pixel=image->colormap[(ssize_t) ConstrainColormapIndex(image,
466                 (ssize_t) index,exception)];
467             else
468               {
469                 pixel.red=(MagickRealType) ScaleCharToQuantum((unsigned char)
470                   index);
471                 pixel.green=(MagickRealType) ScaleCharToQuantum((unsigned char)
472                   index);
473                 pixel.blue=(MagickRealType) ScaleCharToQuantum((unsigned char)
474                   index);
475               }
476             break;
477           }
478           case 15:
479           case 16:
480           {
481             QuantumAny
482               range;
483 
484             /*
485               5 bits each of RGB.
486             */
487             if (ReadBlob(image,2,pixels) != 2)
488               ThrowReaderException(CorruptImageError,"UnableToReadImageData");
489             j=pixels[0];
490             k=pixels[1];
491             range=GetQuantumRange(5UL);
492             pixel.red=(MagickRealType) ScaleAnyToQuantum(1UL*(k & 0x7c) >> 2,
493               range);
494             pixel.green=(MagickRealType) ScaleAnyToQuantum((1UL*
495               (k & 0x03) << 3)+(1UL*(j & 0xe0) >> 5),range);
496             pixel.blue=(MagickRealType) ScaleAnyToQuantum(1UL*(j & 0x1f),range);
497             if (image->alpha_trait != UndefinedPixelTrait)
498               pixel.alpha=(MagickRealType) ((k & 0x80) == 0 ? (Quantum)
499                 TransparentAlpha : (Quantum) OpaqueAlpha);
500             if (image->storage_class == PseudoClass)
501               index=(Quantum) ConstrainColormapIndex(image,((ssize_t) (k << 8))+
502                 j,exception);
503             break;
504           }
505           case 24:
506           {
507             /*
508               BGR pixels.
509             */
510             if (ReadBlob(image,3,pixels) != 3)
511               ThrowReaderException(CorruptImageError,"UnableToReadImageData");
512             pixel.blue=(MagickRealType) ScaleCharToQuantum(pixels[0]);
513             pixel.green=(MagickRealType) ScaleCharToQuantum(pixels[1]);
514             pixel.red=(MagickRealType) ScaleCharToQuantum(pixels[2]);
515             break;
516           }
517           case 32:
518           {
519             /*
520               BGRA pixels.
521             */
522             if (ReadBlob(image,4,pixels) != 4)
523               ThrowReaderException(CorruptImageError,"UnableToReadImageData");
524             pixel.blue=(MagickRealType) ScaleCharToQuantum(pixels[0]);
525             pixel.green=(MagickRealType) ScaleCharToQuantum(pixels[1]);
526             pixel.red=(MagickRealType) ScaleCharToQuantum(pixels[2]);
527             pixel.alpha=(MagickRealType) ScaleCharToQuantum(pixels[3]);
528             break;
529           }
530         }
531       if (status == MagickFalse)
532         ThrowReaderException(CorruptImageError,"UnableToReadImageData");
533       if (image->storage_class == PseudoClass)
534         SetPixelIndex(image,index,q);
535       SetPixelRed(image,ClampToQuantum(pixel.red),q);
536       SetPixelGreen(image,ClampToQuantum(pixel.green),q);
537       SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
538       if (image->alpha_trait != UndefinedPixelTrait)
539         SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
540       q+=GetPixelChannels(image);
541     }
542     if (((unsigned char) (tga_info.attributes & 0xc0) >> 6) == 2)
543       offset+=2;
544     else
545       offset++;
546     if (offset >= (ssize_t) image->rows)
547       {
548         base++;
549         offset=base;
550       }
551     if (SyncAuthenticPixels(image,exception) == MagickFalse)
552       break;
553     if (image->previous == (Image *) NULL)
554       {
555         status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
556           image->rows);
557         if (status == MagickFalse)
558           break;
559       }
560   }
561   if (EOFBlob(image) != MagickFalse)
562     ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
563       image->filename);
564   (void) CloseBlob(image);
565   return(GetFirstImageInList(image));
566 }
567 
568 /*
569 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
570 %                                                                             %
571 %                                                                             %
572 %                                                                             %
573 %   R e g i s t e r T G A I m a g e                                           %
574 %                                                                             %
575 %                                                                             %
576 %                                                                             %
577 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
578 %
579 %  RegisterTGAImage() adds properties for the TGA image format to
580 %  the list of supported formats.  The properties include the image format
581 %  tag, a method to read and/or write the format, whether the format
582 %  supports the saving of more than one frame to the same file or blob,
583 %  whether the format supports native in-memory I/O, and a brief
584 %  description of the format.
585 %
586 %  The format of the RegisterTGAImage method is:
587 %
588 %      size_t RegisterTGAImage(void)
589 %
590 */
RegisterTGAImage(void)591 ModuleExport size_t RegisterTGAImage(void)
592 {
593   MagickInfo
594     *entry;
595 
596   entry=AcquireMagickInfo("TGA","ICB","Truevision Targa image");
597   entry->decoder=(DecodeImageHandler *) ReadTGAImage;
598   entry->encoder=(EncodeImageHandler *) WriteTGAImage;
599   entry->flags|=CoderDecoderSeekableStreamFlag;
600   entry->flags^=CoderAdjoinFlag;
601   (void) RegisterMagickInfo(entry);
602   entry=AcquireMagickInfo("TGA","TGA","Truevision Targa image");
603   entry->decoder=(DecodeImageHandler *) ReadTGAImage;
604   entry->encoder=(EncodeImageHandler *) WriteTGAImage;
605   entry->flags|=CoderDecoderSeekableStreamFlag;
606   entry->flags^=CoderAdjoinFlag;
607   (void) RegisterMagickInfo(entry);
608   entry=AcquireMagickInfo("TGA","VDA","Truevision Targa image");
609   entry->decoder=(DecodeImageHandler *) ReadTGAImage;
610   entry->encoder=(EncodeImageHandler *) WriteTGAImage;
611   entry->flags|=CoderDecoderSeekableStreamFlag;
612   entry->flags^=CoderAdjoinFlag;
613   (void) RegisterMagickInfo(entry);
614   entry=AcquireMagickInfo("TGA","VST","Truevision Targa image");
615   entry->decoder=(DecodeImageHandler *) ReadTGAImage;
616   entry->encoder=(EncodeImageHandler *) WriteTGAImage;
617   entry->flags|=CoderDecoderSeekableStreamFlag;
618   entry->flags^=CoderAdjoinFlag;
619   (void) RegisterMagickInfo(entry);
620   return(MagickImageCoderSignature);
621 }
622 
623 /*
624 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
625 %                                                                             %
626 %                                                                             %
627 %                                                                             %
628 %   U n r e g i s t e r T G A I m a g e                                       %
629 %                                                                             %
630 %                                                                             %
631 %                                                                             %
632 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
633 %
634 %  UnregisterTGAImage() removes format registrations made by the
635 %  TGA module from the list of supported formats.
636 %
637 %  The format of the UnregisterTGAImage method is:
638 %
639 %      UnregisterTGAImage(void)
640 %
641 */
UnregisterTGAImage(void)642 ModuleExport void UnregisterTGAImage(void)
643 {
644   (void) UnregisterMagickInfo("ICB");
645   (void) UnregisterMagickInfo("TGA");
646   (void) UnregisterMagickInfo("VDA");
647   (void) UnregisterMagickInfo("VST");
648 }
649 
650 /*
651 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
652 %                                                                             %
653 %                                                                             %
654 %                                                                             %
655 %   W r i t e T G A I m a g e                                                 %
656 %                                                                             %
657 %                                                                             %
658 %                                                                             %
659 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
660 %
661 %  WriteTGAImage() writes a image in the Truevision Targa rasterfile
662 %  format.
663 %
664 %  The format of the WriteTGAImage method is:
665 %
666 %      MagickBooleanType WriteTGAImage(const ImageInfo *image_info,
667 %        Image *image,ExceptionInfo *exception)
668 %
669 %  A description of each parameter follows.
670 %
671 %    o image_info: the image info.
672 %
673 %    o image:  The image.
674 %
675 */
WriteTGAPixel(Image * image,TGAImageType image_type,const Quantum * p,const QuantumAny range,const double midpoint)676 static inline void WriteTGAPixel(Image *image,TGAImageType image_type,
677   const Quantum *p,const QuantumAny range,const double midpoint)
678 {
679   if (image_type == TGAColormap || image_type == TGARLEColormap)
680     (void) WriteBlobByte(image,(unsigned char) ((ssize_t) GetPixelIndex(image,p)));
681   else
682     {
683       if (image_type == TGAMonochrome || image_type == TGARLEMonochrome)
684         (void) WriteBlobByte(image,ScaleQuantumToChar(ClampToQuantum(
685           GetPixelLuma(image,p))));
686       else
687         if (image->depth == 5)
688           {
689             unsigned char
690               green,
691               value;
692 
693             green=(unsigned char) ScaleQuantumToAny(GetPixelGreen(image,p),
694               range);
695             value=((unsigned char) ScaleQuantumToAny(GetPixelBlue(image,p),
696               range)) | ((green & 0x07) << 5);
697             (void) WriteBlobByte(image,value);
698             value=(((image->alpha_trait != UndefinedPixelTrait) &&
699               ((double) GetPixelAlpha(image,p) > midpoint)) ? 0x80 : 0) |
700               ((unsigned char) ScaleQuantumToAny(GetPixelRed(image,p),range) <<
701               2) | ((green & 0x18) >> 3);
702             (void) WriteBlobByte(image,value);
703           }
704         else
705           {
706             (void) WriteBlobByte(image,ScaleQuantumToChar(
707               GetPixelBlue(image,p)));
708             (void) WriteBlobByte(image,ScaleQuantumToChar(
709               GetPixelGreen(image,p)));
710             (void) WriteBlobByte(image,ScaleQuantumToChar(
711               GetPixelRed(image,p)));
712             if (image->alpha_trait != UndefinedPixelTrait)
713               (void) WriteBlobByte(image,ScaleQuantumToChar(
714                 GetPixelAlpha(image,p)));
715           }
716     }
717 }
718 
WriteTGAImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)719 static MagickBooleanType WriteTGAImage(const ImageInfo *image_info,Image *image,
720   ExceptionInfo *exception)
721 {
722   CompressionType
723     compression;
724 
725   const char
726     *comment;
727 
728   const double
729     midpoint = QuantumRange/2.0;
730 
731   MagickBooleanType
732     status;
733 
734   QuantumAny
735     range;
736 
737   const Quantum
738     *p;
739 
740   ssize_t
741     x;
742 
743   ssize_t
744     i;
745 
746   unsigned char
747     *q;
748 
749   size_t
750     channels;
751 
752   ssize_t
753     base,
754     count,
755     offset,
756     y;
757 
758   TGAInfo
759     tga_info;
760 
761   /*
762     Open output image file.
763   */
764   assert(image_info != (const ImageInfo *) NULL);
765   assert(image_info->signature == MagickCoreSignature);
766   assert(image != (Image *) NULL);
767   assert(image->signature == MagickCoreSignature);
768   if (image->debug != MagickFalse)
769     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
770   assert(exception != (ExceptionInfo *) NULL);
771   assert(exception->signature == MagickCoreSignature);
772   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
773   if (status == MagickFalse)
774     return(status);
775   /*
776     Initialize TGA raster file header.
777   */
778   if ((image->columns > 65535L) || (image->rows > 65535L))
779     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
780   (void) TransformImageColorspace(image,sRGBColorspace,exception);
781   compression=image->compression;
782   if (image_info->compression != UndefinedCompression)
783     compression=image_info->compression;
784   range=GetQuantumRange(5UL);
785   tga_info.id_length=0;
786   comment=GetImageProperty(image,"comment",exception);
787   if (comment != (const char *) NULL)
788     tga_info.id_length=(unsigned char) MagickMin(strlen(comment),255);
789   tga_info.colormap_type=0;
790   tga_info.colormap_index=0;
791   tga_info.colormap_length=0;
792   tga_info.colormap_size=0;
793   tga_info.x_origin=0;
794   tga_info.y_origin=0;
795   tga_info.width=(unsigned short) image->columns;
796   tga_info.height=(unsigned short) image->rows;
797   tga_info.bits_per_pixel=8;
798   tga_info.attributes=0;
799   if ((image_info->type != TrueColorType) &&
800       (image_info->type != TrueColorAlphaType) &&
801       (image_info->type != PaletteType) &&
802       (image->alpha_trait == UndefinedPixelTrait) &&
803       (SetImageGray(image,exception) != MagickFalse))
804     tga_info.image_type=compression == RLECompression ? TGARLEMonochrome :
805       TGAMonochrome;
806   else
807     if ((image->storage_class == DirectClass) || (image->colors > 256))
808       {
809         /*
810           Full color TGA raster.
811         */
812         tga_info.image_type=compression == RLECompression ? TGARLERGB : TGARGB;
813         if (image_info->depth == 5)
814           {
815             tga_info.bits_per_pixel=16;
816             if (image->alpha_trait != UndefinedPixelTrait)
817               tga_info.attributes=1;  /* # of alpha bits */
818           }
819         else
820           {
821             tga_info.bits_per_pixel=24;
822             if (image->alpha_trait != UndefinedPixelTrait)
823               {
824                 tga_info.bits_per_pixel=32;
825                 tga_info.attributes=8;  /* # of alpha bits */
826               }
827           }
828       }
829     else
830       {
831         /*
832           Colormapped TGA raster.
833         */
834         tga_info.image_type=compression == RLECompression ? TGARLEColormap :
835           TGAColormap;
836         tga_info.colormap_type=1;
837         tga_info.colormap_length=(unsigned short) image->colors;
838         if (image_info->depth == 5)
839           tga_info.colormap_size=16;
840         else
841           tga_info.colormap_size=24;
842       }
843   if ((image->orientation == BottomRightOrientation) ||
844       (image->orientation == TopRightOrientation))
845     tga_info.attributes|=(1UL << 4);
846   if ((image->orientation == TopLeftOrientation) ||
847       (image->orientation == UndefinedOrientation) ||
848       (image->orientation == TopRightOrientation))
849     tga_info.attributes|=(1UL << 5);
850   if ((image->columns > 65535) || (image->rows > 65535))
851     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
852   /*
853     Write TGA header.
854   */
855   (void) WriteBlobByte(image,tga_info.id_length);
856   (void) WriteBlobByte(image,tga_info.colormap_type);
857   (void) WriteBlobByte(image,(unsigned char) tga_info.image_type);
858   (void) WriteBlobLSBShort(image,tga_info.colormap_index);
859   (void) WriteBlobLSBShort(image,tga_info.colormap_length);
860   (void) WriteBlobByte(image,tga_info.colormap_size);
861   (void) WriteBlobLSBShort(image,tga_info.x_origin);
862   (void) WriteBlobLSBShort(image,tga_info.y_origin);
863   (void) WriteBlobLSBShort(image,tga_info.width);
864   (void) WriteBlobLSBShort(image,tga_info.height);
865   (void) WriteBlobByte(image,tga_info.bits_per_pixel);
866   (void) WriteBlobByte(image,tga_info.attributes);
867   if (tga_info.id_length != 0)
868     (void) WriteBlob(image,tga_info.id_length,(unsigned char *) comment);
869   if (tga_info.colormap_type != 0)
870     {
871       unsigned char
872         green,
873         *targa_colormap;
874 
875       /*
876         Dump colormap to file (blue, green, red byte order).
877       */
878       targa_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
879         tga_info.colormap_length,(tga_info.colormap_size/8)*
880         sizeof(*targa_colormap));
881       if (targa_colormap == (unsigned char *) NULL)
882         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
883       q=targa_colormap;
884       for (i=0; i < (ssize_t) image->colors; i++)
885       {
886         if (image_info->depth == 5)
887           {
888             green=(unsigned char) ScaleQuantumToAny(ClampToQuantum(
889               image->colormap[i].green),range);
890             *q++=((unsigned char) ScaleQuantumToAny(ClampToQuantum(
891               image->colormap[i].blue),range)) | ((green & 0x07) << 5);
892             *q++=(((image->alpha_trait != UndefinedPixelTrait) && ((double)
893               ClampToQuantum(image->colormap[i].alpha) > midpoint)) ? 0x80 : 0) |
894               ((unsigned char) ScaleQuantumToAny(ClampToQuantum(
895               image->colormap[i].red),range) << 2) | ((green & 0x18) >> 3);
896           }
897         else
898           {
899             *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue));
900             *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green));
901             *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red));
902           }
903       }
904       (void) WriteBlob(image,(size_t) ((tga_info.colormap_size/8)*
905         tga_info.colormap_length),targa_colormap);
906       targa_colormap=(unsigned char *) RelinquishMagickMemory(targa_colormap);
907     }
908   /*
909     Convert MIFF to TGA raster pixels.
910   */
911   base=0;
912   offset=0;
913   channels=GetPixelChannels(image);
914   for (y=0; y < (ssize_t) image->rows; y++)
915   {
916     p=GetVirtualPixels(image,0,offset,image->columns,1,exception);
917     if (p == (const Quantum *) NULL)
918       break;
919     if (compression == RLECompression)
920       {
921         x=0;
922         count=0;
923         while (x < (ssize_t) image->columns)
924         {
925           i=1;
926           while ((i < 128) && (count + i < 128) &&
927                  ((x + i) < (ssize_t) image->columns))
928           {
929             if (tga_info.image_type == TGARLEColormap)
930               {
931                 if (GetPixelIndex(image,p+(i*channels)) !=
932                     GetPixelIndex(image,p+((i-1)*channels)))
933                   break;
934               }
935             else
936               if (tga_info.image_type == TGARLEMonochrome)
937                 {
938                   if (GetPixelLuma(image,p+(i*channels)) !=
939                       GetPixelLuma(image,p+((i-1)*channels)))
940                     break;
941                 }
942               else
943                 {
944                   if ((GetPixelBlue(image,p+(i*channels)) !=
945                        GetPixelBlue(image,p+((i-1)*channels))) ||
946                       (GetPixelGreen(image,p+(i*channels)) !=
947                        GetPixelGreen(image,p+((i-1)*channels))) ||
948                       (GetPixelRed(image,p+(i*channels)) !=
949                        GetPixelRed(image,p+((i-1)*channels))))
950                     break;
951                   if ((image->alpha_trait != UndefinedPixelTrait) &&
952                       (GetPixelAlpha(image,p+(i*channels)) !=
953                        GetPixelAlpha(image,p+(i-1)*channels)))
954                     break;
955                 }
956             i++;
957           }
958           if (i < 3)
959             {
960               count+=i;
961               p+=(i*channels);
962             }
963           if ((i >= 3) || (count == 128) ||
964               ((x + i) == (ssize_t) image->columns))
965             {
966               if (count > 0)
967                 {
968                   (void) WriteBlobByte(image,(unsigned char) (--count));
969                   while (count >= 0)
970                   {
971                     WriteTGAPixel(image,tga_info.image_type,p-((count+1)*
972                       channels),range,midpoint);
973                     count--;
974                   }
975                   count=0;
976                 }
977             }
978           if (i >= 3)
979             {
980               (void) WriteBlobByte(image,(unsigned char) ((i-1) | 0x80));
981               WriteTGAPixel(image,tga_info.image_type,p,range,midpoint);
982               p+=(i*channels);
983             }
984           x+=i;
985         }
986       }
987     else
988       for (x=0; x < (ssize_t) image->columns; x++)
989       {
990         WriteTGAPixel(image,tga_info.image_type,p,range,midpoint);
991         p+=channels;
992       }
993      if (((unsigned char) (tga_info.attributes & 0xc0) >> 6) == 2)
994        offset+=2;
995      else
996        offset++;
997       if (offset >= (ssize_t) image->rows)
998         {
999           base++;
1000           offset=base;
1001         }
1002     if (image->previous == (Image *) NULL)
1003       {
1004         status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1005           image->rows);
1006         if (status == MagickFalse)
1007           break;
1008       }
1009   }
1010   (void) CloseBlob(image);
1011   return(MagickTrue);
1012 }
1013