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