blob: 2cc0d0be1545a0de2e6a1363f0e09c3bfd2950ad [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% July 1992 %
18% %
19% %
cristyb56bb242014-11-25 17:12:48 +000020% Copyright 1999-2015 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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/*
40 Include declarations.
41*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/attribute.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/color-private.h"
48#include "MagickCore/colormap.h"
49#include "MagickCore/colormap-private.h"
50#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000051#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000052#include "MagickCore/exception.h"
53#include "MagickCore/exception-private.h"
54#include "MagickCore/image.h"
55#include "MagickCore/image-private.h"
56#include "MagickCore/list.h"
57#include "MagickCore/magick.h"
58#include "MagickCore/memory_.h"
59#include "MagickCore/monitor.h"
60#include "MagickCore/monitor-private.h"
61#include "MagickCore/pixel-accessor.h"
62#include "MagickCore/property.h"
63#include "MagickCore/quantum-private.h"
64#include "MagickCore/static.h"
65#include "MagickCore/string_.h"
66#include "MagickCore/module.h"
dirk75652a82014-05-12 19:05:56 +000067
68/*
69 Enumerated declaractions.
70*/
71typedef enum
72{
73 TGAColormap = 1,
74 TGARGB = 2,
75 TGAMonochrome = 3,
76 TGARLEColormap = 9,
77 TGARLERGB = 10,
78 TGARLEMonochrome = 11
79} TGAImageType;
80
81/*
82 Typedef declaractions.
83*/
84typedef struct _TGAInfo
85{
86 TGAImageType
87 image_type;
88
89 unsigned char
90 id_length,
91 colormap_type;
92
93 unsigned short
94 colormap_index,
95 colormap_length;
96
97 unsigned char
98 colormap_size;
99
100 unsigned short
101 x_origin,
102 y_origin,
103 width,
104 height;
105
106 unsigned char
107 bits_per_pixel,
108 attributes;
109} TGAInfo;
cristy3ed852e2009-09-05 21:47:34 +0000110
111/*
112 Forward declarations.
113*/
114static MagickBooleanType
cristy3a37efd2011-08-28 20:31:03 +0000115 WriteTGAImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000116
117/*
118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119% %
120% %
121% %
122% R e a d T G A I m a g e %
123% %
124% %
125% %
126%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
127%
128% ReadTGAImage() reads a Truevision TGA image file and returns it.
129% It allocates the memory necessary for the new Image structure and returns
130% a pointer to the new image.
131%
132% The format of the ReadTGAImage method is:
133%
134% Image *ReadTGAImage(const ImageInfo *image_info,ExceptionInfo *exception)
135%
136% A description of each parameter follows:
137%
138% o image_info: the image info.
139%
140% o exception: return any errors or warnings in this structure.
141%
142*/
dirk75652a82014-05-12 19:05:56 +0000143static Image *ReadTGAImage(const ImageInfo *image_info,
144 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000145{
cristy3ed852e2009-09-05 21:47:34 +0000146 Image
147 *image;
148
cristy3ed852e2009-09-05 21:47:34 +0000149 MagickBooleanType
150 status;
151
cristy101ab702011-10-13 13:06:32 +0000152 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000153 pixel;
154
cristy4c08aed2011-07-01 19:47:50 +0000155 Quantum
156 index;
cristy3ed852e2009-09-05 21:47:34 +0000157
cristy4c08aed2011-07-01 19:47:50 +0000158 register Quantum
cristyc6da28e2011-04-28 01:41:35 +0000159 *q;
160
cristybb503372010-05-27 20:51:26 +0000161 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000162 i,
163 x;
164
cristyc6da28e2011-04-28 01:41:35 +0000165 size_t
166 base,
167 flag,
168 offset,
169 real,
170 skip;
cristy3ed852e2009-09-05 21:47:34 +0000171
172 ssize_t
cristyc6da28e2011-04-28 01:41:35 +0000173 count,
174 y;
cristy3ed852e2009-09-05 21:47:34 +0000175
176 TGAInfo
177 tga_info;
178
179 unsigned char
180 j,
181 k,
cristy5dd83a52011-11-24 01:54:17 +0000182 pixels[4],
cristy3ed852e2009-09-05 21:47:34 +0000183 runlength;
184
cristy18307f12011-12-30 01:20:16 +0000185 unsigned int
186 alpha_bits;
187
cristy3ed852e2009-09-05 21:47:34 +0000188 /*
189 Open image file.
190 */
191 assert(image_info != (const ImageInfo *) NULL);
192 assert(image_info->signature == MagickSignature);
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 == MagickSignature);
cristy9950d572011-10-01 18:22:35 +0000198 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000199 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);
dirk75652a82014-05-12 19:05:56 +0000210 tga_info.image_type=(TGAImageType) ReadBlobByte(image);
cristy98140912011-04-20 14:11:45 +0000211 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;
cristy18307f12011-12-30 01:20:16 +0000241 alpha_bits=(tga_info.attributes & 0x0FU);
cristyba213402013-06-28 14:05:12 +0000242 image->alpha_trait=(alpha_bits > 0) || (tga_info.bits_per_pixel == 32) ||
243 (tga_info.colormap_size == 32) ? BlendPixelTrait : UndefinedPixelTrait;
cristy98140912011-04-20 14:11:45 +0000244 if ((tga_info.image_type != TGAColormap) &&
245 (tga_info.image_type != TGARLEColormap))
246 image->depth=(size_t) ((tga_info.bits_per_pixel <= 8) ? 8 :
247 (tga_info.bits_per_pixel <= 16) ? 5 :
248 (tga_info.bits_per_pixel == 24) ? 8 :
249 (tga_info.bits_per_pixel == 32) ? 8 : 8);
250 else
251 image->depth=(size_t) ((tga_info.colormap_size <= 8) ? 8 :
252 (tga_info.colormap_size <= 16) ? 5 :
253 (tga_info.colormap_size == 24) ? 8 :
254 (tga_info.colormap_size == 32) ? 8 : 8);
255 if ((tga_info.image_type == TGAColormap) ||
cristy3ed852e2009-09-05 21:47:34 +0000256 (tga_info.image_type == TGAMonochrome) ||
257 (tga_info.image_type == TGARLEColormap) ||
cristy98140912011-04-20 14:11:45 +0000258 (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 image->compression=RLECompression;
264 if (image->storage_class == PseudoClass)
265 {
266 if (tga_info.colormap_type != 0)
cristyc9eb04c2014-12-20 20:34:41 +0000267 image->colors=tga_info.colormap_index+tga_info.colormap_length;
cristy98140912011-04-20 14:11:45 +0000268 else
269 {
270 size_t
271 one;
272
273 one=1;
274 image->colors=one << tga_info.bits_per_pixel;
cristy018f07f2011-09-04 21:15:19 +0000275 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy98140912011-04-20 14:11:45 +0000276 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
277 }
278 }
279 if (tga_info.id_length != 0)
280 {
281 char
282 *comment;
283
284 size_t
285 length;
286
287 /*
288 TGA image comment.
289 */
290 length=(size_t) tga_info.id_length;
291 comment=(char *) NULL;
cristy151b66d2015-04-15 10:50:31 +0000292 if (~length >= (MagickPathExtent-1))
293 comment=(char *) AcquireQuantumMemory(length+MagickPathExtent,
cristy98140912011-04-20 14:11:45 +0000294 sizeof(*comment));
295 if (comment == (char *) NULL)
296 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
297 count=ReadBlob(image,tga_info.id_length,(unsigned char *) comment);
298 comment[tga_info.id_length]='\0';
cristyd15e6592011-10-15 00:13:06 +0000299 (void) SetImageProperty(image,"comment",comment,exception);
cristy98140912011-04-20 14:11:45 +0000300 comment=DestroyString(comment);
301 }
dirke1b03882014-05-13 11:03:42 +0000302 if (image_info->ping != MagickFalse)
303 {
304 (void) CloseBlob(image);
305 return(image);
306 }
cristyacabb842014-12-14 23:36:33 +0000307 status=SetImageExtent(image,image->columns,image->rows,exception);
308 if (status == MagickFalse)
309 return(DestroyImageList(image));
cristy98140912011-04-20 14:11:45 +0000310 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
cristy77b33ba2013-03-28 13:23:13 +0000311 pixel.alpha=(MagickRealType) OpaqueAlpha;
cristy98140912011-04-20 14:11:45 +0000312 if (tga_info.colormap_type != 0)
313 {
314 /*
315 Read TGA raster colormap.
316 */
cristy018f07f2011-09-04 21:15:19 +0000317 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy98140912011-04-20 14:11:45 +0000318 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristyc9eb04c2014-12-20 20:34:41 +0000319 for (i=0; i < (ssize_t) tga_info.colormap_index; i++)
320 image->colormap[i]=pixel;
321 for ( ; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000322 {
cristy98140912011-04-20 14:11:45 +0000323 switch (tga_info.colormap_size)
324 {
325 case 8:
326 default:
cristy3ed852e2009-09-05 21:47:34 +0000327 {
cristy98140912011-04-20 14:11:45 +0000328 /*
329 Gray scale.
330 */
cristy77b33ba2013-03-28 13:23:13 +0000331 pixel.red=(MagickRealType) ScaleCharToQuantum((unsigned char)
332 ReadBlobByte(image));
cristy98140912011-04-20 14:11:45 +0000333 pixel.green=pixel.red;
334 pixel.blue=pixel.red;
335 break;
cristy3ed852e2009-09-05 21:47:34 +0000336 }
cristy98140912011-04-20 14:11:45 +0000337 case 15:
338 case 16:
339 {
340 QuantumAny
341 range;
342
343 /*
344 5 bits each of red green and blue.
345 */
346 j=(unsigned char) ReadBlobByte(image);
347 k=(unsigned char) ReadBlobByte(image);
348 range=GetQuantumRange(5UL);
cristy77b33ba2013-03-28 13:23:13 +0000349 pixel.red=(MagickRealType) ScaleAnyToQuantum(1UL*(k & 0x7c) >> 2,
350 range);
351 pixel.green=(MagickRealType) ScaleAnyToQuantum((1UL*(k & 0x03)
352 << 3)+(1UL*(j & 0xe0) >> 5),range);
353 pixel.blue=(MagickRealType) ScaleAnyToQuantum(1UL*(j & 0x1f),range);
cristy98140912011-04-20 14:11:45 +0000354 break;
355 }
356 case 24:
cristy98140912011-04-20 14:11:45 +0000357 {
358 /*
359 8 bits each of blue, green and red.
360 */
cristy77b33ba2013-03-28 13:23:13 +0000361 pixel.blue=(MagickRealType) ScaleCharToQuantum((unsigned char)
362 ReadBlobByte(image));
363 pixel.green=(MagickRealType) ScaleCharToQuantum((unsigned char)
364 ReadBlobByte(image));
365 pixel.red=(MagickRealType) ScaleCharToQuantum((unsigned char)
366 ReadBlobByte(image));
cristy98140912011-04-20 14:11:45 +0000367 break;
368 }
cristyba213402013-06-28 14:05:12 +0000369 case 32:
370 {
371 /*
372 8 bits each of blue, green, red, and alpha.
373 */
374 pixel.blue=(MagickRealType) ScaleCharToQuantum((unsigned char)
375 ReadBlobByte(image));
376 pixel.green=(MagickRealType) ScaleCharToQuantum((unsigned char)
377 ReadBlobByte(image));
378 pixel.red=(MagickRealType) ScaleCharToQuantum((unsigned char)
379 ReadBlobByte(image));
380 pixel.alpha=(MagickRealType) ScaleCharToQuantum((unsigned char)
381 ReadBlobByte(image));
382 break;
383 }
cristy98140912011-04-20 14:11:45 +0000384 }
385 image->colormap[i]=pixel;
386 }
387 }
388 /*
389 Convert TGA pixels to pixel packets.
390 */
391 base=0;
392 flag=0;
393 skip=MagickFalse;
394 real=0;
cristy4c08aed2011-07-01 19:47:50 +0000395 index=0;
cristy98140912011-04-20 14:11:45 +0000396 runlength=0;
397 offset=0;
398 for (y=0; y < (ssize_t) image->rows; y++)
399 {
400 real=offset;
401 if (((unsigned char) (tga_info.attributes & 0x20) >> 5) == 0)
402 real=image->rows-real-1;
403 q=QueueAuthenticPixels(image,0,(ssize_t) real,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000404 if (q == (Quantum *) NULL)
cristy98140912011-04-20 14:11:45 +0000405 break;
cristy98140912011-04-20 14:11:45 +0000406 for (x=0; x < (ssize_t) image->columns; x++)
407 {
408 if ((tga_info.image_type == TGARLEColormap) ||
409 (tga_info.image_type == TGARLERGB) ||
410 (tga_info.image_type == TGARLEMonochrome))
411 {
412 if (runlength != 0)
413 {
414 runlength--;
415 skip=flag != 0;
416 }
417 else
418 {
419 count=ReadBlob(image,1,&runlength);
cristy771c8842015-01-09 12:13:22 +0000420 if (count != 1)
cristy98140912011-04-20 14:11:45 +0000421 ThrowReaderException(CorruptImageError,"UnableToReadImageData");
422 flag=runlength & 0x80;
423 if (flag != 0)
424 runlength-=128;
425 skip=MagickFalse;
426 }
427 }
428 if (skip == MagickFalse)
429 switch (tga_info.bits_per_pixel)
430 {
431 case 8:
432 default:
433 {
434 /*
435 Gray scale.
436 */
cristy4c08aed2011-07-01 19:47:50 +0000437 index=(Quantum) ReadBlobByte(image);
cristy98140912011-04-20 14:11:45 +0000438 if (tga_info.colormap_type != 0)
439 pixel=image->colormap[(ssize_t) ConstrainColormapIndex(image,
cristyc9eb04c2014-12-20 20:34:41 +0000440 (ssize_t) index,exception)];
cristy98140912011-04-20 14:11:45 +0000441 else
442 {
cristy77b33ba2013-03-28 13:23:13 +0000443 pixel.red=(MagickRealType) ScaleCharToQuantum((unsigned char)
444 index);
445 pixel.green=(MagickRealType) ScaleCharToQuantum((unsigned char)
446 index);
447 pixel.blue=(MagickRealType) ScaleCharToQuantum((unsigned char)
448 index);
cristy98140912011-04-20 14:11:45 +0000449 }
450 break;
451 }
452 case 15:
453 case 16:
454 {
455 QuantumAny
456 range;
457
458 /*
cristy5dd83a52011-11-24 01:54:17 +0000459 5 bits each of RGB.
cristy98140912011-04-20 14:11:45 +0000460 */
cristy5dd83a52011-11-24 01:54:17 +0000461 if (ReadBlob(image,2,pixels) != 2)
462 ThrowReaderException(CorruptImageError,"UnableToReadImageData");
463 j=pixels[0];
464 k=pixels[1];
cristy98140912011-04-20 14:11:45 +0000465 range=GetQuantumRange(5UL);
cristy77b33ba2013-03-28 13:23:13 +0000466 pixel.red=(MagickRealType) ScaleAnyToQuantum(1UL*(k & 0x7c) >> 2,
467 range);
cristydfc3f852015-04-11 12:37:16 +0000468 pixel.green=(MagickRealType) ScaleAnyToQuantum((1UL*
469 (k & 0x03) << 3)+(1UL*(j & 0xe0) >> 5),range);
cristy77b33ba2013-03-28 13:23:13 +0000470 pixel.blue=(MagickRealType) ScaleAnyToQuantum(1UL*(j & 0x1f),range);
cristy17f11b02014-12-20 19:37:04 +0000471 if (image->alpha_trait != UndefinedPixelTrait)
cristy77b33ba2013-03-28 13:23:13 +0000472 pixel.alpha=(MagickRealType) ((k & 0x80) == 0 ? (Quantum)
473 OpaqueAlpha : (Quantum) TransparentAlpha);
cristy98140912011-04-20 14:11:45 +0000474 if (image->storage_class == PseudoClass)
cristyc9eb04c2014-12-20 20:34:41 +0000475 index=(Quantum) ConstrainColormapIndex(image,((ssize_t) (k << 8))+
476 j,exception);
cristy98140912011-04-20 14:11:45 +0000477 break;
478 }
479 case 24:
cristy5dd83a52011-11-24 01:54:17 +0000480 {
481 /*
482 BGR pixels.
483 */
484 if (ReadBlob(image,3,pixels) != 3)
485 ThrowReaderException(CorruptImageError,"UnableToReadImageData");
cristy77b33ba2013-03-28 13:23:13 +0000486 pixel.blue=(MagickRealType) ScaleCharToQuantum(pixels[0]);
487 pixel.green=(MagickRealType) ScaleCharToQuantum(pixels[1]);
488 pixel.red=(MagickRealType) ScaleCharToQuantum(pixels[2]);
cristy5dd83a52011-11-24 01:54:17 +0000489 break;
490 }
cristy98140912011-04-20 14:11:45 +0000491 case 32:
492 {
493 /*
cristy5dd83a52011-11-24 01:54:17 +0000494 BGRA pixels.
cristy98140912011-04-20 14:11:45 +0000495 */
cristy5dd83a52011-11-24 01:54:17 +0000496 if (ReadBlob(image,4,pixels) != 4)
497 ThrowReaderException(CorruptImageError,"UnableToReadImageData");
cristy77b33ba2013-03-28 13:23:13 +0000498 pixel.blue=(MagickRealType) ScaleCharToQuantum(pixels[0]);
499 pixel.green=(MagickRealType) ScaleCharToQuantum(pixels[1]);
500 pixel.red=(MagickRealType) ScaleCharToQuantum(pixels[2]);
501 pixel.alpha=(MagickRealType) ScaleCharToQuantum(pixels[3]);
cristy98140912011-04-20 14:11:45 +0000502 break;
503 }
504 }
505 if (status == MagickFalse)
506 ThrowReaderException(CorruptImageError,"UnableToReadImageData");
507 if (image->storage_class == PseudoClass)
cristy4c08aed2011-07-01 19:47:50 +0000508 SetPixelIndex(image,index,q);
cristy77b33ba2013-03-28 13:23:13 +0000509 SetPixelRed(image,ClampToQuantum(pixel.red),q);
510 SetPixelGreen(image,ClampToQuantum(pixel.green),q);
511 SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
cristy17f11b02014-12-20 19:37:04 +0000512 if (image->alpha_trait != UndefinedPixelTrait)
cristy77b33ba2013-03-28 13:23:13 +0000513 SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
cristyed231572011-07-14 02:18:59 +0000514 q+=GetPixelChannels(image);
cristy98140912011-04-20 14:11:45 +0000515 }
cristy43254d12014-05-24 23:30:21 +0000516 if (((tga_info.attributes & 0xc0) >> 6) == 4)
cristy98140912011-04-20 14:11:45 +0000517 offset+=4;
518 else
cristy43254d12014-05-24 23:30:21 +0000519 if (((tga_info.attributes & 0xc0) >> 6) == 2)
cristy98140912011-04-20 14:11:45 +0000520 offset+=2;
521 else
522 offset++;
523 if (offset >= image->rows)
524 {
525 base++;
526 offset=base;
527 }
528 if (SyncAuthenticPixels(image,exception) == MagickFalse)
529 break;
530 if (image->previous == (Image *) NULL)
531 {
532 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
533 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000534 if (status == MagickFalse)
535 break;
536 }
cristy98140912011-04-20 14:11:45 +0000537 }
538 if (EOFBlob(image) != MagickFalse)
539 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
540 image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000541 (void) CloseBlob(image);
542 return(GetFirstImageInList(image));
543}
544
545/*
546%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
547% %
548% %
549% %
550% R e g i s t e r T G A I m a g e %
551% %
552% %
553% %
554%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
555%
556% RegisterTGAImage() adds properties for the TGA image format to
557% the list of supported formats. The properties include the image format
558% tag, a method to read and/or write the format, whether the format
559% supports the saving of more than one frame to the same file or blob,
560% whether the format supports native in-memory I/O, and a brief
561% description of the format.
562%
563% The format of the RegisterTGAImage method is:
564%
cristybb503372010-05-27 20:51:26 +0000565% size_t RegisterTGAImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000566%
567*/
cristybb503372010-05-27 20:51:26 +0000568ModuleExport size_t RegisterTGAImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000569{
570 MagickInfo
571 *entry;
572
dirk06b627a2015-04-06 18:59:17 +0000573 entry=AcquireMagickInfo("TGA","ICB","Truevision Targa image");
cristy3ed852e2009-09-05 21:47:34 +0000574 entry->decoder=(DecodeImageHandler *) ReadTGAImage;
575 entry->encoder=(EncodeImageHandler *) WriteTGAImage;
dirk08e9a112015-02-22 01:51:41 +0000576 entry->flags^=CoderAdjoinFlag;
cristy3ed852e2009-09-05 21:47:34 +0000577 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +0000578 entry=AcquireMagickInfo("TGA","TGA","Truevision Targa image");
cristy3ed852e2009-09-05 21:47:34 +0000579 entry->decoder=(DecodeImageHandler *) ReadTGAImage;
580 entry->encoder=(EncodeImageHandler *) WriteTGAImage;
dirk08e9a112015-02-22 01:51:41 +0000581 entry->flags^=CoderAdjoinFlag;
cristy3ed852e2009-09-05 21:47:34 +0000582 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +0000583 entry=AcquireMagickInfo("TGA","VDA","Truevision Targa image");
cristy3ed852e2009-09-05 21:47:34 +0000584 entry->decoder=(DecodeImageHandler *) ReadTGAImage;
585 entry->encoder=(EncodeImageHandler *) WriteTGAImage;
dirk08e9a112015-02-22 01:51:41 +0000586 entry->flags^=CoderAdjoinFlag;
cristy3ed852e2009-09-05 21:47:34 +0000587 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +0000588 entry=AcquireMagickInfo("TGA","VST","Truevision Targa image");
cristy3ed852e2009-09-05 21:47:34 +0000589 entry->decoder=(DecodeImageHandler *) ReadTGAImage;
590 entry->encoder=(EncodeImageHandler *) WriteTGAImage;
dirk08e9a112015-02-22 01:51:41 +0000591 entry->flags^=CoderAdjoinFlag;
cristy3ed852e2009-09-05 21:47:34 +0000592 (void) RegisterMagickInfo(entry);
593 return(MagickImageCoderSignature);
594}
595
596/*
597%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
598% %
599% %
600% %
601% U n r e g i s t e r T G A I m a g e %
602% %
603% %
604% %
605%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
606%
607% UnregisterTGAImage() removes format registrations made by the
608% TGA module from the list of supported formats.
609%
610% The format of the UnregisterTGAImage method is:
611%
612% UnregisterTGAImage(void)
613%
614*/
615ModuleExport void UnregisterTGAImage(void)
616{
617 (void) UnregisterMagickInfo("ICB");
618 (void) UnregisterMagickInfo("TGA");
619 (void) UnregisterMagickInfo("VDA");
620 (void) UnregisterMagickInfo("VST");
621}
622
623/*
624%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
625% %
626% %
627% %
628% W r i t e T G A I m a g e %
629% %
630% %
631% %
632%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
633%
634% WriteTGAImage() writes a image in the Truevision Targa rasterfile
635% format.
636%
637% The format of the WriteTGAImage method is:
638%
cristy3a37efd2011-08-28 20:31:03 +0000639% MagickBooleanType WriteTGAImage(const ImageInfo *image_info,
640% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000641%
642% A description of each parameter follows.
643%
644% o image_info: the image info.
645%
646% o image: The image.
647%
648*/
dirk75652a82014-05-12 19:05:56 +0000649static inline void WriteTGAPixel(Image *image,TGAImageType image_type,
dirkf142f1f2014-12-16 09:48:03 +0000650 const Quantum *p,const QuantumAny range,const double midpoint)
dirk75652a82014-05-12 19:05:56 +0000651{
652 if (image_type == TGAColormap || image_type == TGARLEColormap)
653 (void) WriteBlobByte(image,(unsigned char) GetPixelIndex(image,p));
654 else
655 {
656 if (image_type == TGAMonochrome || image_type == TGARLEMonochrome)
657 (void) WriteBlobByte(image,ScaleQuantumToChar(ClampToQuantum(
658 GetPixelLuma(image,p))));
659 else
cristydfc3f852015-04-11 12:37:16 +0000660 if (image->depth == 5)
661 {
662 unsigned char
663 green,
664 value;
665
666 green=(unsigned char) ScaleQuantumToAny(GetPixelGreen(image,p),
667 range);
668 value=((unsigned char) ScaleQuantumToAny(GetPixelBlue(image,p),
669 range)) | ((green & 0x07) << 5);
670 (void) WriteBlobByte(image,value);
671 value=(((image->alpha_trait != UndefinedPixelTrait) &&
672 ((double) GetPixelAlpha(image,p) < midpoint)) ? 80 : 0) |
673 ((unsigned char) ScaleQuantumToAny(GetPixelRed(image,p),range) <<
674 2) | ((green & 0x18) >> 3);
675 (void) WriteBlobByte(image,value);
676 }
677 else
678 {
679 (void) WriteBlobByte(image,ScaleQuantumToChar(
680 GetPixelBlue(image,p)));
681 (void) WriteBlobByte(image,ScaleQuantumToChar(
682 GetPixelGreen(image,p)));
683 (void) WriteBlobByte(image,ScaleQuantumToChar(
684 GetPixelRed(image,p)));
685 if (image->alpha_trait != UndefinedPixelTrait)
686 (void) WriteBlobByte(image,ScaleQuantumToChar(
687 GetPixelAlpha(image,p)));
688 }
dirk75652a82014-05-12 19:05:56 +0000689 }
690}
691
cristy3a37efd2011-08-28 20:31:03 +0000692static MagickBooleanType WriteTGAImage(const ImageInfo *image_info,Image *image,
693 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000694{
dirk75652a82014-05-12 19:05:56 +0000695 CompressionType
696 compression;
cristy3ed852e2009-09-05 21:47:34 +0000697
698 const char
699 *value;
700
dirkf142f1f2014-12-16 09:48:03 +0000701 const double
cristydfc3f852015-04-11 12:37:16 +0000702 midpoint = QuantumRange/2.0;
dirkf142f1f2014-12-16 09:48:03 +0000703
cristy3ed852e2009-09-05 21:47:34 +0000704 MagickBooleanType
705 status;
706
dirkf142f1f2014-12-16 09:48:03 +0000707 QuantumAny
708 range;
709
cristy4c08aed2011-07-01 19:47:50 +0000710 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000711 *p;
712
cristybb503372010-05-27 20:51:26 +0000713 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000714 x;
715
cristybb503372010-05-27 20:51:26 +0000716 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000717 i;
718
719 register unsigned char
720 *q;
721
dirk75652a82014-05-12 19:05:56 +0000722 size_t
723 channels;
724
cristy3ed852e2009-09-05 21:47:34 +0000725 ssize_t
cristyc6da28e2011-04-28 01:41:35 +0000726 count,
727 y;
cristy3ed852e2009-09-05 21:47:34 +0000728
dirk75652a82014-05-12 19:05:56 +0000729 TGAInfo
730 tga_info;
cristy3ed852e2009-09-05 21:47:34 +0000731
732 /*
733 Open output image file.
734 */
735 assert(image_info != (const ImageInfo *) NULL);
736 assert(image_info->signature == MagickSignature);
737 assert(image != (Image *) NULL);
738 assert(image->signature == MagickSignature);
739 if (image->debug != MagickFalse)
740 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +0000741 assert(exception != (ExceptionInfo *) NULL);
742 assert(exception->signature == MagickSignature);
743 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000744 if (status == MagickFalse)
745 return(status);
cristy98140912011-04-20 14:11:45 +0000746 /*
747 Initialize TGA raster file header.
748 */
749 if ((image->columns > 65535L) || (image->rows > 65535L))
750 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
cristyaf8d3912014-02-21 14:50:33 +0000751 (void) TransformImageColorspace(image,sRGBColorspace,exception);
dirk75652a82014-05-12 19:05:56 +0000752 compression=image->compression;
753 if (image_info->compression != UndefinedCompression)
754 compression=image_info->compression;
dirkf142f1f2014-12-16 09:48:03 +0000755 range=GetQuantumRange(5UL);
dirk75652a82014-05-12 19:05:56 +0000756 tga_info.id_length=0;
cristyd15e6592011-10-15 00:13:06 +0000757 value=GetImageProperty(image,"comment",exception);
cristy98140912011-04-20 14:11:45 +0000758 if (value != (const char *) NULL)
dirk75652a82014-05-12 19:05:56 +0000759 tga_info.id_length=(unsigned char) MagickMin(strlen(value),255);
760 tga_info.colormap_type=0;
761 tga_info.colormap_index=0;
762 tga_info.colormap_length=0;
763 tga_info.colormap_size=0;
764 tga_info.x_origin=0;
765 tga_info.y_origin=0;
766 tga_info.width=(unsigned short) image->columns;
767 tga_info.height=(unsigned short) image->rows;
768 tga_info.bits_per_pixel=8;
769 tga_info.attributes=0;
cristy98140912011-04-20 14:11:45 +0000770 if ((image_info->type != TrueColorType) &&
cristydef23e52015-01-22 11:52:01 +0000771 (image_info->type != TrueColorAlphaType) &&
cristy98140912011-04-20 14:11:45 +0000772 (image_info->type != PaletteType) &&
cristy17f11b02014-12-20 19:37:04 +0000773 (image->alpha_trait == UndefinedPixelTrait) &&
dirkf1d85482015-04-06 00:36:00 +0000774 (SetImageGray(image,exception) != MagickFalse))
dirk75652a82014-05-12 19:05:56 +0000775 tga_info.image_type=compression == RLECompression ? TGARLEMonochrome :
776 TGAMonochrome;
cristy98140912011-04-20 14:11:45 +0000777 else
778 if ((image->storage_class == DirectClass) || (image->colors > 256))
cristy3ed852e2009-09-05 21:47:34 +0000779 {
cristy3ed852e2009-09-05 21:47:34 +0000780 /*
cristy98140912011-04-20 14:11:45 +0000781 Full color TGA raster.
cristy3ed852e2009-09-05 21:47:34 +0000782 */
cristydfc3f852015-04-11 12:37:16 +0000783 tga_info.image_type=compression == RLECompression ? TGARLERGB : TGARGB;
dirkf142f1f2014-12-16 09:48:03 +0000784 if (image_info->depth == 5)
785 tga_info.bits_per_pixel=16;
786 else
cristy98140912011-04-20 14:11:45 +0000787 {
dirkf142f1f2014-12-16 09:48:03 +0000788 tga_info.bits_per_pixel=24;
cristy17f11b02014-12-20 19:37:04 +0000789 if (image->alpha_trait != UndefinedPixelTrait)
dirkf142f1f2014-12-16 09:48:03 +0000790 {
791 tga_info.bits_per_pixel=32;
792 tga_info.attributes=8; /* # of alpha bits */
793 }
cristy98140912011-04-20 14:11:45 +0000794 }
cristy3ed852e2009-09-05 21:47:34 +0000795 }
cristy98140912011-04-20 14:11:45 +0000796 else
cristy3ed852e2009-09-05 21:47:34 +0000797 {
cristy98140912011-04-20 14:11:45 +0000798 /*
799 Colormapped TGA raster.
800 */
dirk75652a82014-05-12 19:05:56 +0000801 tga_info.image_type=compression == RLECompression ? TGARLEColormap :
802 TGAColormap;
803 tga_info.colormap_type=1;
804 tga_info.colormap_length=(unsigned short) image->colors;
dirkf142f1f2014-12-16 09:48:03 +0000805 if (image_info->depth == 5)
806 tga_info.colormap_size=16;
807 else
808 tga_info.colormap_size=24;
cristy3ed852e2009-09-05 21:47:34 +0000809 }
cristy98140912011-04-20 14:11:45 +0000810 /*
811 Write TGA header.
812 */
dirk75652a82014-05-12 19:05:56 +0000813 (void) WriteBlobByte(image,tga_info.id_length);
814 (void) WriteBlobByte(image,tga_info.colormap_type);
dirk2a2a3e92014-05-16 08:46:18 +0000815 (void) WriteBlobByte(image,(unsigned char) tga_info.image_type);
dirk75652a82014-05-12 19:05:56 +0000816 (void) WriteBlobLSBShort(image,tga_info.colormap_index);
817 (void) WriteBlobLSBShort(image,tga_info.colormap_length);
818 (void) WriteBlobByte(image,tga_info.colormap_size);
819 (void) WriteBlobLSBShort(image,tga_info.x_origin);
820 (void) WriteBlobLSBShort(image,tga_info.y_origin);
821 (void) WriteBlobLSBShort(image,tga_info.width);
822 (void) WriteBlobLSBShort(image,tga_info.height);
823 (void) WriteBlobByte(image,tga_info.bits_per_pixel);
824 (void) WriteBlobByte(image,tga_info.attributes);
825 if (tga_info.id_length != 0)
cristydfc3f852015-04-11 12:37:16 +0000826 (void) WriteBlob(image,tga_info.id_length,(unsigned char *) value);
dirk75652a82014-05-12 19:05:56 +0000827 if (tga_info.colormap_type != 0)
cristy98140912011-04-20 14:11:45 +0000828 {
829 unsigned char
dirkf142f1f2014-12-16 09:48:03 +0000830 green,
cristy98140912011-04-20 14:11:45 +0000831 *targa_colormap;
832
833 /*
834 Dump colormap to file (blue, green, red byte order).
835 */
836 targa_colormap=(unsigned char *) AcquireQuantumMemory((size_t)
cristydfc3f852015-04-11 12:37:16 +0000837 tga_info.colormap_length,(tga_info.colormap_size/8)*
838 sizeof(*targa_colormap));
cristy98140912011-04-20 14:11:45 +0000839 if (targa_colormap == (unsigned char *) NULL)
840 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
841 q=targa_colormap;
842 for (i=0; i < (ssize_t) image->colors; i++)
843 {
dirkf142f1f2014-12-16 09:48:03 +0000844 if (image_info->depth == 5)
845 {
846 green=(unsigned char) ScaleQuantumToAny(ClampToQuantum(
847 image->colormap[i].green),range);
848 *q++=((unsigned char) ScaleQuantumToAny(ClampToQuantum(
849 image->colormap[i].blue),range)) | ((green & 0x07) << 5);
cristyc9eb04c2014-12-20 20:34:41 +0000850 *q++=(((image->alpha_trait != UndefinedPixelTrait) && ((double)
851 ClampToQuantum(image->colormap[i].alpha) < midpoint)) ? 80 : 0) |
dirkf142f1f2014-12-16 09:48:03 +0000852 ((unsigned char) ScaleQuantumToAny(ClampToQuantum(
853 image->colormap[i].red),range) << 2) | ((green & 0x18) >> 3);
854 }
855 else
856 {
857 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue));
858 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green));
859 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red));
860 }
cristy98140912011-04-20 14:11:45 +0000861 }
dirkf142f1f2014-12-16 09:48:03 +0000862 (void) WriteBlob(image,(size_t) ((tga_info.colormap_size/8)*
863 tga_info.colormap_length),targa_colormap);
cristy98140912011-04-20 14:11:45 +0000864 targa_colormap=(unsigned char *) RelinquishMagickMemory(targa_colormap);
cristy3ed852e2009-09-05 21:47:34 +0000865 }
cristy98140912011-04-20 14:11:45 +0000866 /*
867 Convert MIFF to TGA raster pixels.
868 */
dirk75652a82014-05-12 19:05:56 +0000869 channels=GetPixelChannels(image);
cristy98140912011-04-20 14:11:45 +0000870 for (y=(ssize_t) (image->rows-1); y >= 0; y--)
871 {
cristy3a37efd2011-08-28 20:31:03 +0000872 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000873 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000874 break;
dirk75652a82014-05-12 19:05:56 +0000875 if (compression == RLECompression)
876 {
877 x=0;
878 count=0;
879 while (x < (ssize_t) image->columns)
880 {
881 i=1;
882 while ((i < 128) && (count + i < 128) &&
883 ((x + i) < (ssize_t) image->columns))
cristy98140912011-04-20 14:11:45 +0000884 {
dirk75652a82014-05-12 19:05:56 +0000885 if (tga_info.image_type == TGARLEColormap)
886 {
887 if (GetPixelIndex(image,p+(i*channels)) !=
888 GetPixelIndex(image,p+((i-1)*channels)))
889 break;
890 }
891 else if (tga_info.image_type == TGARLEMonochrome)
892 {
893 if (GetPixelLuma(image,p+(i*channels)) !=
894 GetPixelLuma(image,p+((i-1)*channels)))
895 break;
896 }
897 else
898 {
899 if ((GetPixelBlue(image,p+(i*channels)) !=
900 GetPixelBlue(image,p+((i-1)*channels))) ||
901 (GetPixelGreen(image,p+(i*channels)) !=
902 GetPixelGreen(image,p+((i-1)*channels))) ||
903 (GetPixelRed(image,p+(i*channels)) !=
904 GetPixelRed(image,p+((i-1)*channels))))
905 break;
cristy17f11b02014-12-20 19:37:04 +0000906 if ((image->alpha_trait != UndefinedPixelTrait) &&
dirk75652a82014-05-12 19:05:56 +0000907 (GetPixelAlpha(image,p+(i*channels)) !=
908 GetPixelAlpha(image,p+(i-1)*channels)))
909 break;
910 }
911 i++;
cristy98140912011-04-20 14:11:45 +0000912 }
dirk75652a82014-05-12 19:05:56 +0000913 if (i < 3)
914 {
915 count+=i;
916 p+=(i*channels);
917 }
918 if ((i >= 3) || (count == 128) ||
919 ((x + i) == (ssize_t) image->columns))
920 {
921 if (count > 0)
922 {
cristy43254d12014-05-24 23:30:21 +0000923 (void) WriteBlobByte(image,(unsigned char) (--count));
dirk75652a82014-05-12 19:05:56 +0000924 while (count >= 0)
925 {
926 WriteTGAPixel(image,tga_info.image_type,p-((count+1)*
dirkf142f1f2014-12-16 09:48:03 +0000927 channels),range,midpoint);
dirk75652a82014-05-12 19:05:56 +0000928 count--;
929 }
930 count=0;
931 }
932 }
933 if (i >= 3)
934 {
cristyc9eb04c2014-12-20 20:34:41 +0000935 (void) WriteBlobByte(image,(unsigned char) ((i-1) | 0x80));
dirkf142f1f2014-12-16 09:48:03 +0000936 WriteTGAPixel(image,tga_info.image_type,p,range,midpoint);
dirk75652a82014-05-12 19:05:56 +0000937 p+=(i*channels);
938 }
939 x+=i;
940 }
941 }
942 else
943 {
944 for (x=0; x < (ssize_t) image->columns; x++)
945 {
dirkf142f1f2014-12-16 09:48:03 +0000946 WriteTGAPixel(image,tga_info.image_type,p,range,midpoint);
dirk75652a82014-05-12 19:05:56 +0000947 p+=channels;
948 }
949 }
cristy98140912011-04-20 14:11:45 +0000950 if (image->previous == (Image *) NULL)
951 {
952 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristyc6da28e2011-04-28 01:41:35 +0000953 image->rows);
cristy98140912011-04-20 14:11:45 +0000954 if (status == MagickFalse)
955 break;
956 }
957 }
cristy3ed852e2009-09-05 21:47:34 +0000958 (void) CloseBlob(image);
959 return(MagickTrue);
960}