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