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