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