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