]> 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 %                                   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   if (image_info->ping != MagickFalse)
303     {
304       (void) CloseBlob(image);
305       return(image);
306     }
307   (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
308   pixel.alpha=(MagickRealType) OpaqueAlpha;
309   if (tga_info.colormap_type != 0)
310     {
311       /*
312         Read TGA raster colormap.
313       */
314       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
315         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
316       for (i=0; i < (ssize_t) image->colors; i++)
317       {
318         switch (tga_info.colormap_size)
319         {
320           case 8:
321           default:
322           {
323             /*
324               Gray scale.
325             */
326             pixel.red=(MagickRealType) ScaleCharToQuantum((unsigned char)
327               ReadBlobByte(image));
328             pixel.green=pixel.red;
329             pixel.blue=pixel.red;
330             break;
331           }
332           case 15:
333           case 16:
334           {
335             QuantumAny
336               range;
337
338             /*
339               5 bits each of red green and blue.
340             */
341             j=(unsigned char) ReadBlobByte(image);
342             k=(unsigned char) ReadBlobByte(image);
343             range=GetQuantumRange(5UL);
344             pixel.red=(MagickRealType) ScaleAnyToQuantum(1UL*(k & 0x7c) >> 2,
345               range);
346             pixel.green=(MagickRealType) ScaleAnyToQuantum((1UL*(k & 0x03)
347               << 3)+(1UL*(j & 0xe0) >> 5),range);
348             pixel.blue=(MagickRealType) ScaleAnyToQuantum(1UL*(j & 0x1f),range);
349             break;
350           }
351           case 24:
352           {
353             /*
354               8 bits each of blue, green and red.
355             */
356             pixel.blue=(MagickRealType) ScaleCharToQuantum((unsigned char)
357               ReadBlobByte(image));
358             pixel.green=(MagickRealType) ScaleCharToQuantum((unsigned char)
359               ReadBlobByte(image));
360             pixel.red=(MagickRealType) ScaleCharToQuantum((unsigned char)
361               ReadBlobByte(image));
362             break;
363           }
364           case 32:
365           {
366             /*
367               8 bits each of blue, green, red, and alpha.
368             */
369             pixel.blue=(MagickRealType) ScaleCharToQuantum((unsigned char)
370               ReadBlobByte(image));
371             pixel.green=(MagickRealType) ScaleCharToQuantum((unsigned char)
372               ReadBlobByte(image));
373             pixel.red=(MagickRealType) ScaleCharToQuantum((unsigned char)
374               ReadBlobByte(image));
375             pixel.alpha=(MagickRealType) ScaleCharToQuantum((unsigned char)
376               ReadBlobByte(image));
377             break;
378           }
379         }
380         image->colormap[i]=pixel;
381       }
382     }
383   /*
384     Convert TGA pixels to pixel packets.
385   */
386   base=0;
387   flag=0;
388   skip=MagickFalse;
389   real=0;
390   index=0;
391   runlength=0;
392   offset=0;
393   for (y=0; y < (ssize_t) image->rows; y++)
394   {
395     real=offset;
396     if (((unsigned char) (tga_info.attributes & 0x20) >> 5) == 0)
397       real=image->rows-real-1;
398     q=QueueAuthenticPixels(image,0,(ssize_t) real,image->columns,1,exception);
399     if (q == (Quantum *) NULL)
400       break;
401     for (x=0; x < (ssize_t) image->columns; x++)
402     {
403       if ((tga_info.image_type == TGARLEColormap) ||
404           (tga_info.image_type == TGARLERGB) ||
405           (tga_info.image_type == TGARLEMonochrome))
406         {
407           if (runlength != 0)
408             {
409               runlength--;
410               skip=flag != 0;
411             }
412           else
413             {
414               count=ReadBlob(image,1,&runlength);
415               if (count == 0)
416                 ThrowReaderException(CorruptImageError,"UnableToReadImageData");
417               flag=runlength & 0x80;
418               if (flag != 0)
419                 runlength-=128;
420               skip=MagickFalse;
421             }
422         }
423       if (skip == MagickFalse)
424         switch (tga_info.bits_per_pixel)
425         {
426           case 8:
427           default:
428           {
429             /*
430               Gray scale.
431             */
432             index=(Quantum) ReadBlobByte(image);
433             if (tga_info.colormap_type != 0)
434               pixel=image->colormap[(ssize_t) ConstrainColormapIndex(image,
435                 1UL*index,exception)];
436             else
437               {
438                 pixel.red=(MagickRealType) ScaleCharToQuantum((unsigned char)
439                   index);
440                 pixel.green=(MagickRealType) ScaleCharToQuantum((unsigned char)
441                   index);
442                 pixel.blue=(MagickRealType) ScaleCharToQuantum((unsigned char)
443                   index);
444               }
445             break;
446           }
447           case 15:
448           case 16:
449           {
450             QuantumAny
451               range;
452
453             /*
454               5 bits each of RGB.
455             */
456             if (ReadBlob(image,2,pixels) != 2)
457               ThrowReaderException(CorruptImageError,"UnableToReadImageData");
458             j=pixels[0];
459             k=pixels[1];
460             range=GetQuantumRange(5UL);
461             pixel.red=(MagickRealType) ScaleAnyToQuantum(1UL*(k & 0x7c) >> 2,
462               range);
463             pixel.green=(MagickRealType) ScaleAnyToQuantum((1UL*(k & 0x03)
464               << 3)+(1UL*(j & 0xe0) >> 5),range);
465             pixel.blue=(MagickRealType) ScaleAnyToQuantum(1UL*(j & 0x1f),range);
466             if (image->alpha_trait == BlendPixelTrait)
467               pixel.alpha=(MagickRealType) ((k & 0x80) == 0 ? (Quantum)
468                 OpaqueAlpha : (Quantum) TransparentAlpha); 
469             if (image->storage_class == PseudoClass)
470               index=ConstrainColormapIndex(image,((size_t) k << 8)+j,exception);
471             break;
472           }
473           case 24:
474           {
475             /*
476               BGR pixels.
477             */
478             if (ReadBlob(image,3,pixels) != 3)
479               ThrowReaderException(CorruptImageError,"UnableToReadImageData");
480             pixel.blue=(MagickRealType) ScaleCharToQuantum(pixels[0]);
481             pixel.green=(MagickRealType) ScaleCharToQuantum(pixels[1]);
482             pixel.red=(MagickRealType) ScaleCharToQuantum(pixels[2]);
483             break;
484           }
485           case 32:
486           {
487             /*
488               BGRA pixels.
489             */
490             if (ReadBlob(image,4,pixels) != 4)
491               ThrowReaderException(CorruptImageError,"UnableToReadImageData");
492             pixel.blue=(MagickRealType) ScaleCharToQuantum(pixels[0]);
493             pixel.green=(MagickRealType) ScaleCharToQuantum(pixels[1]);
494             pixel.red=(MagickRealType) ScaleCharToQuantum(pixels[2]);
495             pixel.alpha=(MagickRealType) ScaleCharToQuantum(pixels[3]);
496             break;
497           }
498         }
499       if (status == MagickFalse)
500         ThrowReaderException(CorruptImageError,"UnableToReadImageData");
501       if (image->storage_class == PseudoClass)
502         SetPixelIndex(image,index,q);
503       SetPixelRed(image,ClampToQuantum(pixel.red),q);
504       SetPixelGreen(image,ClampToQuantum(pixel.green),q);
505       SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
506       if (image->alpha_trait == BlendPixelTrait)
507         SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
508       q+=GetPixelChannels(image);
509     }
510     if (((tga_info.attributes & 0xc0) >> 6) == 4)
511       offset+=4;
512     else
513       if (((tga_info.attributes & 0xc0) >> 6) == 2)
514         offset+=2;
515       else
516         offset++;
517     if (offset >= image->rows)
518       {
519         base++;
520         offset=base;
521       }
522     if (SyncAuthenticPixels(image,exception) == MagickFalse)
523       break;
524     if (image->previous == (Image *) NULL)
525       {
526         status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
527           image->rows);
528         if (status == MagickFalse)
529           break;
530       }
531   }
532   if (EOFBlob(image) != MagickFalse)
533     ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
534       image->filename);
535   (void) CloseBlob(image);
536   return(GetFirstImageInList(image));
537 }
538 \f
539 /*
540 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
541 %                                                                             %
542 %                                                                             %
543 %                                                                             %
544 %   R e g i s t e r T G A I m a g e                                           %
545 %                                                                             %
546 %                                                                             %
547 %                                                                             %
548 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
549 %
550 %  RegisterTGAImage() adds properties for the TGA image format to
551 %  the list of supported formats.  The properties include the image format
552 %  tag, a method to read and/or write the format, whether the format
553 %  supports the saving of more than one frame to the same file or blob,
554 %  whether the format supports native in-memory I/O, and a brief
555 %  description of the format.
556 %
557 %  The format of the RegisterTGAImage method is:
558 %
559 %      size_t RegisterTGAImage(void)
560 %
561 */
562 ModuleExport size_t RegisterTGAImage(void)
563 {
564   MagickInfo
565     *entry;
566
567   entry=SetMagickInfo("ICB");
568   entry->decoder=(DecodeImageHandler *) ReadTGAImage;
569   entry->encoder=(EncodeImageHandler *) WriteTGAImage;
570   entry->adjoin=MagickFalse;
571   entry->description=ConstantString("Truevision Targa image");
572   entry->module=ConstantString("TGA");
573   (void) RegisterMagickInfo(entry);
574   entry=SetMagickInfo("TGA");
575   entry->decoder=(DecodeImageHandler *) ReadTGAImage;
576   entry->encoder=(EncodeImageHandler *) WriteTGAImage;
577   entry->adjoin=MagickFalse;
578   entry->description=ConstantString("Truevision Targa image");
579   entry->module=ConstantString("TGA");
580   (void) RegisterMagickInfo(entry);
581   entry=SetMagickInfo("VDA");
582   entry->decoder=(DecodeImageHandler *) ReadTGAImage;
583   entry->encoder=(EncodeImageHandler *) WriteTGAImage;
584   entry->adjoin=MagickFalse;
585   entry->description=ConstantString("Truevision Targa image");
586   entry->module=ConstantString("TGA");
587   (void) RegisterMagickInfo(entry);
588   entry=SetMagickInfo("VST");
589   entry->decoder=(DecodeImageHandler *) ReadTGAImage;
590   entry->encoder=(EncodeImageHandler *) WriteTGAImage;
591   entry->adjoin=MagickFalse;
592   entry->description=ConstantString("Truevision Targa image");
593   entry->module=ConstantString("TGA");
594   (void) RegisterMagickInfo(entry);
595   return(MagickImageCoderSignature);
596 }
597 \f
598 /*
599 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
600 %                                                                             %
601 %                                                                             %
602 %                                                                             %
603 %   U n r e g i s t e r T G A I m a g e                                       %
604 %                                                                             %
605 %                                                                             %
606 %                                                                             %
607 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
608 %
609 %  UnregisterTGAImage() removes format registrations made by the
610 %  TGA module from the list of supported formats.
611 %
612 %  The format of the UnregisterTGAImage method is:
613 %
614 %      UnregisterTGAImage(void)
615 %
616 */
617 ModuleExport void UnregisterTGAImage(void)
618 {
619   (void) UnregisterMagickInfo("ICB");
620   (void) UnregisterMagickInfo("TGA");
621   (void) UnregisterMagickInfo("VDA");
622   (void) UnregisterMagickInfo("VST");
623 }
624 \f
625 /*
626 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
627 %                                                                             %
628 %                                                                             %
629 %                                                                             %
630 %   W r i t e T G A I m a g e                                                 %
631 %                                                                             %
632 %                                                                             %
633 %                                                                             %
634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
635 %
636 %  WriteTGAImage() writes a image in the Truevision Targa rasterfile
637 %  format.
638 %
639 %  The format of the WriteTGAImage method is:
640 %
641 %      MagickBooleanType WriteTGAImage(const ImageInfo *image_info,
642 %        Image *image,ExceptionInfo *exception)
643 %
644 %  A description of each parameter follows.
645 %
646 %    o image_info: the image info.
647 %
648 %    o image:  The image.
649 %
650 */
651
652 static inline size_t MagickMin(const size_t x,const size_t y)
653 {
654   if (x < y)
655     return(x);
656   return(y);
657 }
658
659 static inline void WriteTGAPixel(Image *image,TGAImageType image_type,
660   const Quantum *p)
661 {
662   if (image_type == TGAColormap || image_type == TGARLEColormap)
663     (void) WriteBlobByte(image,(unsigned char) GetPixelIndex(image,p));
664   else
665     {
666       if (image_type == TGAMonochrome || image_type == TGARLEMonochrome)
667         (void) WriteBlobByte(image,ScaleQuantumToChar(ClampToQuantum(
668           GetPixelLuma(image,p))));
669       else
670         {
671           (void) WriteBlobByte(image,ScaleQuantumToChar(GetPixelBlue(image,
672             p)));
673           (void) WriteBlobByte(image,ScaleQuantumToChar(GetPixelGreen(image,
674             p)));
675           (void) WriteBlobByte(image,ScaleQuantumToChar(GetPixelRed(image,p)));
676           if (image->alpha_trait == BlendPixelTrait)
677             (void) WriteBlobByte(image,ScaleQuantumToChar(GetPixelAlpha(image,
678               p)));
679         }
680     }
681 }
682
683 static MagickBooleanType WriteTGAImage(const ImageInfo *image_info,Image *image,
684   ExceptionInfo *exception)
685 {
686   CompressionType
687     compression;
688
689   const char
690     *value;
691
692   MagickBooleanType
693     status;
694
695   register const Quantum
696     *p;
697
698   register ssize_t
699     x;
700
701   register ssize_t
702     i;
703
704   register unsigned char
705     *q;
706
707   size_t
708     channels;
709
710   ssize_t
711     count,
712     y;
713
714   TGAInfo
715     tga_info;
716
717   /*
718     Open output image file.
719   */
720   assert(image_info != (const ImageInfo *) NULL);
721   assert(image_info->signature == MagickSignature);
722   assert(image != (Image *) NULL);
723   assert(image->signature == MagickSignature);
724   if (image->debug != MagickFalse)
725     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
726   assert(exception != (ExceptionInfo *) NULL);
727   assert(exception->signature == MagickSignature);
728   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
729   if (status == MagickFalse)
730     return(status);
731   /*
732     Initialize TGA raster file header.
733   */
734   if ((image->columns > 65535L) || (image->rows > 65535L))
735     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
736   (void) TransformImageColorspace(image,sRGBColorspace,exception);
737   compression=image->compression;
738   if (image_info->compression != UndefinedCompression)
739     compression=image_info->compression;
740   tga_info.id_length=0;
741   value=GetImageProperty(image,"comment",exception);
742   if (value != (const char *) NULL)
743     tga_info.id_length=(unsigned char) MagickMin(strlen(value),255);
744   tga_info.colormap_type=0;
745   tga_info.colormap_index=0;
746   tga_info.colormap_length=0;
747   tga_info.colormap_size=0;
748   tga_info.x_origin=0;
749   tga_info.y_origin=0;
750   tga_info.width=(unsigned short) image->columns;
751   tga_info.height=(unsigned short) image->rows;
752   tga_info.bits_per_pixel=8;
753   tga_info.attributes=0;
754   if ((image_info->type != TrueColorType) &&
755       (image_info->type != TrueColorMatteType) &&
756       (image_info->type != PaletteType) &&
757       (image->alpha_trait != BlendPixelTrait) &&
758       (IsImageGray(image,exception) != MagickFalse))
759     tga_info.image_type=compression == RLECompression ? TGARLEMonochrome :
760       TGAMonochrome;
761   else
762     if ((image->storage_class == DirectClass) || (image->colors > 256))
763       {
764         /*
765           Full color TGA raster.
766         */
767         tga_info.image_type=compression == RLECompression ? TGARLERGB :
768           TGARGB;
769         tga_info.bits_per_pixel=24;
770         if (image->alpha_trait == BlendPixelTrait)
771           {
772             tga_info.bits_per_pixel=32;
773             tga_info.attributes=8;  /* # of alpha bits */
774           }
775       }
776     else
777       {
778         /*
779           Colormapped TGA raster.
780         */
781         tga_info.image_type=compression == RLECompression ? TGARLEColormap :
782           TGAColormap;
783         tga_info.colormap_type=1;
784         tga_info.colormap_length=(unsigned short) image->colors;
785         tga_info.colormap_size=24;
786       }
787   /*
788     Write TGA header.
789   */
790   (void) WriteBlobByte(image,tga_info.id_length);
791   (void) WriteBlobByte(image,tga_info.colormap_type);
792   (void) WriteBlobByte(image,(unsigned char) tga_info.image_type);
793   (void) WriteBlobLSBShort(image,tga_info.colormap_index);
794   (void) WriteBlobLSBShort(image,tga_info.colormap_length);
795   (void) WriteBlobByte(image,tga_info.colormap_size);
796   (void) WriteBlobLSBShort(image,tga_info.x_origin);
797   (void) WriteBlobLSBShort(image,tga_info.y_origin);
798   (void) WriteBlobLSBShort(image,tga_info.width);
799   (void) WriteBlobLSBShort(image,tga_info.height);
800   (void) WriteBlobByte(image,tga_info.bits_per_pixel);
801   (void) WriteBlobByte(image,tga_info.attributes);
802   if (tga_info.id_length != 0)
803     (void) WriteBlob(image,tga_info.id_length,(unsigned char *)
804       value);
805   if (tga_info.colormap_type != 0)
806     {
807       unsigned char
808         *targa_colormap;
809
810       /*
811         Dump colormap to file (blue, green, red byte order).
812       */
813       targa_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
814         tga_info.colormap_length,3UL*sizeof(*targa_colormap));
815       if (targa_colormap == (unsigned char *) NULL)
816         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
817       q=targa_colormap;
818       for (i=0; i < (ssize_t) image->colors; i++)
819       {
820         *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue));
821         *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green));
822         *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red));
823       }
824       (void) WriteBlob(image,(size_t) (3*tga_info.colormap_length),
825         targa_colormap);
826       targa_colormap=(unsigned char *) RelinquishMagickMemory(targa_colormap);
827     }
828   /*
829     Convert MIFF to TGA raster pixels.
830   */
831   channels=GetPixelChannels(image);
832   for (y=(ssize_t) (image->rows-1); y >= 0; y--)
833   {
834     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
835     if (p == (const Quantum *) NULL)
836       break;
837     if (compression == RLECompression)
838       {
839         x=0;
840         count=0;
841         while (x < (ssize_t) image->columns)
842         {
843           i=1;
844           while ((i < 128) && (count + i < 128) &&
845                  ((x + i) < (ssize_t) image->columns))
846           {
847             if (tga_info.image_type == TGARLEColormap)
848               {
849                 if (GetPixelIndex(image,p+(i*channels)) !=
850                     GetPixelIndex(image,p+((i-1)*channels)))
851                   break;
852               }
853             else if (tga_info.image_type == TGARLEMonochrome)
854               {
855                 if (GetPixelLuma(image,p+(i*channels)) !=
856                     GetPixelLuma(image,p+((i-1)*channels)))
857                   break;
858               }
859             else
860               {
861                 if ((GetPixelBlue(image,p+(i*channels)) !=
862                      GetPixelBlue(image,p+((i-1)*channels))) ||
863                     (GetPixelGreen(image,p+(i*channels)) !=
864                      GetPixelGreen(image,p+((i-1)*channels))) ||
865                     (GetPixelRed(image,p+(i*channels)) !=
866                      GetPixelRed(image,p+((i-1)*channels))))
867                   break;
868                 if ((image->alpha_trait == BlendPixelTrait) &&
869                     (GetPixelAlpha(image,p+(i*channels)) !=
870                      GetPixelAlpha(image,p+(i-1)*channels)))
871                   break;
872               }
873             i++;
874           }
875           if (i < 3)
876             {
877               count+=i;
878               p+=(i*channels);
879             }
880           if ((i >= 3) || (count == 128) ||
881               ((x + i) == (ssize_t) image->columns))
882             {
883               if (count > 0)
884                 {
885                   (void) WriteBlobByte(image,(unsigned char) (--count));
886                   while (count >= 0)
887                   {
888                     WriteTGAPixel(image,tga_info.image_type,p-((count+1)*
889                       channels));
890                     count--;
891                   }
892                   count=0;
893                 }
894             }
895           if (i >= 3)
896             {
897               WriteBlobByte(image,(unsigned char) ((i-1) | 0x80));
898               WriteTGAPixel(image,tga_info.image_type,p);
899               p+=(i*channels);
900             }
901           x+=i;
902         }
903       }
904     else
905       {
906         for (x=0; x < (ssize_t) image->columns; x++)
907           {
908             WriteTGAPixel(image,tga_info.image_type,p);
909             p+=channels;
910           }
911       }
912     if (image->previous == (Image *) NULL)
913       {
914         status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
915           image->rows);
916         if (status == MagickFalse)
917           break;
918       }
919   }
920   (void) CloseBlob(image);
921   return(MagickTrue);
922 }