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