/src/imagemagick/coders/tga.c
Line | Count | Source |
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 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/license/ % |
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/module.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 "coders/coders-private.h" |
70 | | |
71 | | /* |
72 | | Enumerated declarations. |
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 declarations. |
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 | | /* |
115 | | Forward declarations. |
116 | | */ |
117 | | static MagickBooleanType |
118 | | WriteTGAImage(const ImageInfo *,Image *,ExceptionInfo *); |
119 | | |
120 | | /* |
121 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
122 | | % % |
123 | | % % |
124 | | % % |
125 | | % R e a d T G A I m a g e % |
126 | | % % |
127 | | % % |
128 | | % % |
129 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
130 | | % |
131 | | % ReadTGAImage() reads a Truevision TGA image file and returns it. |
132 | | % It allocates the memory necessary for the new Image structure and returns |
133 | | % a pointer to the new image. |
134 | | % |
135 | | % The format of the ReadTGAImage method is: |
136 | | % |
137 | | % Image *ReadTGAImage(const ImageInfo *image_info,ExceptionInfo *exception) |
138 | | % |
139 | | % A description of each parameter follows: |
140 | | % |
141 | | % o image_info: the image info. |
142 | | % |
143 | | % o exception: return any errors or warnings in this structure. |
144 | | % |
145 | | */ |
146 | | static Image *ReadTGAImage(const ImageInfo *image_info,ExceptionInfo *exception) |
147 | 1.05k | { |
148 | 1.05k | const char |
149 | 1.05k | *option; |
150 | | |
151 | 1.05k | Image |
152 | 1.05k | *image; |
153 | | |
154 | 1.05k | MagickBooleanType |
155 | 1.05k | flip_x = MagickFalse, |
156 | 1.05k | flip_y = MagickFalse, |
157 | 1.05k | status; |
158 | | |
159 | 1.05k | PixelInfo |
160 | 1.05k | pixel; |
161 | | |
162 | 1.05k | Quantum |
163 | 1.05k | index, |
164 | 1.05k | *q; |
165 | | |
166 | 1.05k | size_t |
167 | 1.05k | base, |
168 | 1.05k | flag, |
169 | 1.05k | skip; |
170 | | |
171 | 1.05k | ssize_t |
172 | 1.05k | count, |
173 | 1.05k | i, |
174 | 1.05k | offset, |
175 | 1.05k | offset_stepsize, |
176 | 1.05k | x, |
177 | 1.05k | y; |
178 | | |
179 | 1.05k | TGAInfo |
180 | 1.05k | tga_info; |
181 | | |
182 | 1.05k | unsigned char |
183 | 1.05k | j, |
184 | 1.05k | k, |
185 | 1.05k | pixels[4], |
186 | 1.05k | runlength; |
187 | | |
188 | | /* |
189 | | Open image file. |
190 | | */ |
191 | 1.05k | assert(image_info != (const ImageInfo *) NULL); |
192 | 1.05k | assert(image_info->signature == MagickCoreSignature); |
193 | 1.05k | assert(exception != (ExceptionInfo *) NULL); |
194 | 1.05k | assert(exception->signature == MagickCoreSignature); |
195 | 1.05k | if (IsEventLogging() != MagickFalse) |
196 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", |
197 | 0 | image_info->filename); |
198 | 1.05k | image=AcquireImage(image_info,exception); |
199 | 1.05k | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
200 | 1.05k | if (status == MagickFalse) |
201 | 0 | { |
202 | 0 | image=DestroyImageList(image); |
203 | 0 | return((Image *) NULL); |
204 | 0 | } |
205 | | /* |
206 | | Read TGA header information. |
207 | | */ |
208 | 1.05k | (void) memset(&tga_info,0,sizeof(tga_info)); |
209 | 1.05k | count=ReadBlob(image,1,&tga_info.id_length); |
210 | 1.05k | tga_info.colormap_type=(unsigned char) ReadBlobByte(image); |
211 | 1.05k | tga_info.image_type=(TGAImageType) ReadBlobByte(image); |
212 | 1.05k | if ((count != 1) || |
213 | 1.05k | ((tga_info.image_type != TGAColormap) && |
214 | 857 | (tga_info.image_type != TGARGB) && |
215 | 551 | (tga_info.image_type != TGAMonochrome) && |
216 | 512 | (tga_info.image_type != TGARLEColormap) && |
217 | 422 | (tga_info.image_type != TGARLERGB) && |
218 | 103 | (tga_info.image_type != TGARLEMonochrome)) || |
219 | 1.03k | (((tga_info.image_type == TGAColormap) || |
220 | 837 | (tga_info.image_type == TGARLEColormap)) && |
221 | 285 | (tga_info.colormap_type == 0))) |
222 | 1.03k | ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
223 | 1.03k | tga_info.colormap_index=ReadBlobLSBShort(image); |
224 | 1.03k | tga_info.colormap_length=ReadBlobLSBShort(image); |
225 | 1.03k | tga_info.colormap_size=(unsigned char) ReadBlobByte(image); |
226 | 1.03k | tga_info.x_origin=ReadBlobLSBShort(image); |
227 | 1.03k | tga_info.y_origin=ReadBlobLSBShort(image); |
228 | 1.03k | tga_info.width=(unsigned short) ReadBlobLSBShort(image); |
229 | 1.03k | tga_info.height=(unsigned short) ReadBlobLSBShort(image); |
230 | 1.03k | tga_info.bits_per_pixel=(unsigned char) ReadBlobByte(image); |
231 | 1.03k | tga_info.attributes=(unsigned char) ReadBlobByte(image); |
232 | 1.03k | if (EOFBlob(image) != MagickFalse) |
233 | 1.00k | ThrowReaderException(CorruptImageError,"UnableToReadImageData"); |
234 | 1.00k | if ((((tga_info.bits_per_pixel < 1) || (tga_info.bits_per_pixel >= 17)) && |
235 | 78 | (tga_info.bits_per_pixel != 24) && (tga_info.bits_per_pixel != 32))) |
236 | 992 | ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
237 | | /* |
238 | | Initialize image structure. |
239 | | */ |
240 | 992 | image->columns=tga_info.width; |
241 | 992 | image->rows=tga_info.height; |
242 | 992 | image->page.x=(ssize_t) tga_info.x_origin; |
243 | 992 | image->page.y=(ssize_t) tga_info.y_origin; |
244 | 992 | if ((tga_info.image_type != TGAMonochrome) && |
245 | 955 | (tga_info.image_type != TGARLEMonochrome)) |
246 | 877 | { |
247 | 877 | unsigned int |
248 | 877 | alpha_bits; |
249 | | |
250 | 877 | alpha_bits=(tga_info.attributes & 0x0FU); |
251 | 877 | image->alpha_trait=(alpha_bits > 0) || (tga_info.bits_per_pixel == 32) || |
252 | 664 | (tga_info.colormap_size == 32) ? BlendPixelTrait : UndefinedPixelTrait; |
253 | 877 | } |
254 | 992 | if ((tga_info.image_type != TGAColormap) && |
255 | 802 | (tga_info.image_type != TGARLEColormap)) |
256 | 718 | image->depth=(size_t) ((tga_info.bits_per_pixel <= 8) ? 8 : |
257 | 718 | (tga_info.bits_per_pixel <= 16) ? 5 : 8); |
258 | 274 | else |
259 | 274 | image->depth=(size_t) ((tga_info.colormap_size <= 8) ? 8 : |
260 | 274 | (tga_info.colormap_size <= 16) ? 5 : 8); |
261 | 992 | if ((tga_info.image_type == TGAColormap) || |
262 | 802 | (tga_info.image_type == TGARLEColormap)) |
263 | 274 | image->storage_class=PseudoClass; |
264 | 992 | if ((tga_info.image_type == TGAMonochrome) || |
265 | 955 | (tga_info.image_type == TGARLEMonochrome)) |
266 | 115 | (void) SetImageColorspace(image,GRAYColorspace,exception); |
267 | 992 | image->compression=NoCompression; |
268 | 992 | if ((tga_info.image_type == TGARLEColormap) || |
269 | 908 | (tga_info.image_type == TGARLEMonochrome) || |
270 | 830 | (tga_info.image_type == TGARLERGB)) |
271 | 469 | image->compression=RLECompression; |
272 | 992 | if (image->storage_class == PseudoClass) |
273 | 274 | { |
274 | 274 | if (tga_info.colormap_type != 0) |
275 | 274 | image->colors=tga_info.colormap_index+tga_info.colormap_length; |
276 | 0 | else |
277 | 0 | { |
278 | 0 | size_t |
279 | 0 | one; |
280 | |
|
281 | 0 | one=1; |
282 | 0 | image->colors=one << tga_info.bits_per_pixel; |
283 | 0 | if ((MagickSizeType) image->colors > GetBlobSize(image)) |
284 | 0 | ThrowReaderException(CorruptImageError, |
285 | 0 | "InsufficientImageDataInFile"); |
286 | 0 | if (AcquireImageColormap(image,image->colors,exception) == MagickFalse) |
287 | 0 | ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); |
288 | 0 | } |
289 | 274 | } |
290 | 992 | if (tga_info.id_length != 0) |
291 | 220 | { |
292 | 220 | char |
293 | 220 | *comment; |
294 | | |
295 | 220 | size_t |
296 | 220 | length; |
297 | | |
298 | | /* |
299 | | TGA image comment. |
300 | | */ |
301 | 220 | length=(size_t) tga_info.id_length; |
302 | 220 | comment=(char *) NULL; |
303 | 220 | if (~length >= (MagickPathExtent-1)) |
304 | 220 | comment=(char *) AcquireQuantumMemory(length+MagickPathExtent, |
305 | 220 | sizeof(*comment)); |
306 | 220 | if (comment == (char *) NULL) |
307 | 220 | ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); |
308 | 220 | count=ReadBlob(image,length,(unsigned char *) comment); |
309 | 220 | if (count == (ssize_t) length) |
310 | 128 | { |
311 | 128 | comment[length]='\0'; |
312 | 128 | (void) SetImageProperty(image,"comment",comment,exception); |
313 | 128 | } |
314 | 220 | comment=DestroyString(comment); |
315 | 220 | } |
316 | 992 | if ((tga_info.attributes & (1UL << 4)) == 0) |
317 | 613 | { |
318 | 613 | if ((tga_info.attributes & (1UL << 5)) == 0) |
319 | 453 | { |
320 | 453 | image->orientation=BottomLeftOrientation; |
321 | 453 | flip_y=MagickTrue; |
322 | 453 | } |
323 | 160 | else |
324 | 160 | image->orientation=TopLeftOrientation; |
325 | 613 | } |
326 | 379 | else |
327 | 379 | { |
328 | 379 | if ((tga_info.attributes & (1UL << 5)) == 0) |
329 | 109 | { |
330 | 109 | image->orientation=BottomRightOrientation; |
331 | 109 | flip_x=MagickTrue; |
332 | 109 | flip_y=MagickTrue; |
333 | 109 | } |
334 | 270 | else |
335 | 270 | { |
336 | 270 | image->orientation=TopRightOrientation; |
337 | 270 | flip_x=MagickTrue; |
338 | 270 | } |
339 | 379 | } |
340 | 992 | option=GetImageOption(image_info,"tga:preserve-orientation"); |
341 | 992 | if (IsStringTrue(option) != MagickFalse) |
342 | 0 | { |
343 | 0 | flip_x=MagickFalse; |
344 | 0 | flip_y=MagickFalse; |
345 | 0 | } |
346 | 992 | else |
347 | 992 | image->orientation=TopLeftOrientation; |
348 | 992 | if (image_info->ping != MagickFalse) |
349 | 0 | { |
350 | 0 | (void) CloseBlob(image); |
351 | 0 | return(image); |
352 | 0 | } |
353 | 992 | status=SetImageExtent(image,image->columns,image->rows,exception); |
354 | 992 | if (status == MagickFalse) |
355 | 57 | return(DestroyImageList(image)); |
356 | 935 | GetPixelInfo(image,&pixel); |
357 | 935 | if (tga_info.colormap_type != 0) |
358 | 725 | { |
359 | | /* |
360 | | Read TGA raster colormap. |
361 | | */ |
362 | 725 | if (image->colors < tga_info.colormap_index) |
363 | 461 | image->colors=tga_info.colormap_index; |
364 | 725 | if (AcquireImageColormap(image,image->colors,exception) == MagickFalse) |
365 | 723 | ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); |
366 | 12.1M | for (i=0; i < (ssize_t) tga_info.colormap_index; i++) |
367 | 12.1M | image->colormap[i]=pixel; |
368 | 1.08M | for ( ; i < (ssize_t) image->colors; i++) |
369 | 1.08M | { |
370 | 1.08M | switch (tga_info.colormap_size) |
371 | 1.08M | { |
372 | 0 | case 8: |
373 | 553k | default: |
374 | 553k | { |
375 | | /* |
376 | | Gray scale. |
377 | | */ |
378 | 553k | pixel.red=(MagickRealType) ScaleCharToQuantum((unsigned char) |
379 | 553k | ReadBlobByte(image)); |
380 | 553k | pixel.green=pixel.red; |
381 | 553k | pixel.blue=pixel.red; |
382 | 553k | break; |
383 | 0 | } |
384 | 112k | case 15: |
385 | 205k | case 16: |
386 | 205k | { |
387 | 205k | QuantumAny |
388 | 205k | range; |
389 | | |
390 | | /* |
391 | | 5 bits each of red green and blue. |
392 | | */ |
393 | 205k | j=(unsigned char) ReadBlobByte(image); |
394 | 205k | k=(unsigned char) ReadBlobByte(image); |
395 | 205k | range=GetQuantumRange(5UL); |
396 | 205k | pixel.red=(MagickRealType) ScaleAnyToQuantum(1UL*(k & 0x7c) >> 2, |
397 | 205k | range); |
398 | 205k | pixel.green=(MagickRealType) ScaleAnyToQuantum((1UL*(k & 0x03) |
399 | 205k | << 3)+(1UL*(j & 0xe0) >> 5),range); |
400 | 205k | pixel.blue=(MagickRealType) ScaleAnyToQuantum(1UL*(j & 0x1f),range); |
401 | 205k | break; |
402 | 112k | } |
403 | 283k | case 24: |
404 | 283k | { |
405 | | /* |
406 | | 8 bits each of blue, green and red. |
407 | | */ |
408 | 283k | pixel.blue=(MagickRealType) ScaleCharToQuantum((unsigned char) |
409 | 283k | ReadBlobByte(image)); |
410 | 283k | pixel.green=(MagickRealType) ScaleCharToQuantum((unsigned char) |
411 | 283k | ReadBlobByte(image)); |
412 | 283k | pixel.red=(MagickRealType) ScaleCharToQuantum((unsigned char) |
413 | 283k | ReadBlobByte(image)); |
414 | 283k | break; |
415 | 112k | } |
416 | 45.4k | case 32: |
417 | 45.4k | { |
418 | | /* |
419 | | 8 bits each of blue, green, red, and alpha. |
420 | | */ |
421 | 45.4k | pixel.blue=(MagickRealType) ScaleCharToQuantum((unsigned char) |
422 | 45.4k | ReadBlobByte(image)); |
423 | 45.4k | pixel.green=(MagickRealType) ScaleCharToQuantum((unsigned char) |
424 | 45.4k | ReadBlobByte(image)); |
425 | 45.4k | pixel.red=(MagickRealType) ScaleCharToQuantum((unsigned char) |
426 | 45.4k | ReadBlobByte(image)); |
427 | 45.4k | pixel.alpha=(MagickRealType) ScaleCharToQuantum((unsigned char) |
428 | 45.4k | ReadBlobByte(image)); |
429 | 45.4k | break; |
430 | 112k | } |
431 | 1.08M | } |
432 | 1.08M | image->colormap[i]=pixel; |
433 | 1.08M | } |
434 | 723 | } |
435 | | /* |
436 | | Convert TGA pixels to pixel packets. |
437 | | */ |
438 | 933 | base=0; |
439 | 933 | flag=0; |
440 | 933 | skip=MagickFalse; |
441 | 933 | index=0; |
442 | 933 | runlength=0; |
443 | 933 | offset=0; |
444 | 933 | offset_stepsize=1; |
445 | 933 | if (((unsigned char) (tga_info.attributes & 0xc0) >> 6) == 2) |
446 | 112 | offset_stepsize=2; |
447 | 50.8k | for (y=0; y < (ssize_t) image->rows; y++) |
448 | 50.2k | { |
449 | 50.2k | ssize_t |
450 | 50.2k | y_offset=(flip_y == MagickFalse) ? offset : (ssize_t) image->rows- |
451 | 21.5k | 1-offset; |
452 | | |
453 | 50.2k | q=QueueAuthenticPixels(image,0,y_offset,image->columns,1,exception); |
454 | 50.2k | if (q == (Quantum *) NULL) |
455 | 0 | break; |
456 | 50.2k | if (flip_x != MagickFalse) |
457 | 27.0k | q+=(ptrdiff_t) GetPixelChannels(image)*(image->columns-1); |
458 | 4.35M | for (x=0; x < (ssize_t) image->columns; x++) |
459 | 4.30M | { |
460 | 4.30M | if ((tga_info.image_type == TGARLEColormap) || |
461 | 3.75M | (tga_info.image_type == TGARLERGB) || |
462 | 1.09M | (tga_info.image_type == TGARLEMonochrome)) |
463 | 3.97M | { |
464 | 3.97M | if (runlength != 0) |
465 | 3.93M | { |
466 | 3.93M | runlength--; |
467 | 3.93M | skip=flag != 0; |
468 | 3.93M | } |
469 | 38.7k | else |
470 | 38.7k | { |
471 | 38.7k | count=ReadBlob(image,1,&runlength); |
472 | 38.7k | if (count != 1) |
473 | 38.6k | ThrowReaderException(CorruptImageError,"UnableToReadImageData"); |
474 | 38.6k | flag=runlength & 0x80; |
475 | 38.6k | if (flag != 0) |
476 | 26.8k | runlength-=128; |
477 | 38.6k | skip=MagickFalse; |
478 | 38.6k | } |
479 | 3.97M | } |
480 | 4.30M | if (skip == MagickFalse) |
481 | 1.06M | switch (tga_info.bits_per_pixel) |
482 | 1.06M | { |
483 | 1.04M | case 1: |
484 | 1.04M | { |
485 | 1.04M | if ((x & 0x07) == 0) |
486 | 129k | { |
487 | 129k | if (ReadBlob(image,1,pixels) != 1) |
488 | 67 | ThrowReaderException(CorruptImageError, |
489 | 129k | "UnableToReadImageData"); |
490 | 129k | index=(Quantum) pixels[0]; |
491 | 129k | } |
492 | 915k | else |
493 | 915k | index=(Quantum) ((size_t) index << 1); |
494 | 1.04M | if (tga_info.colormap_type != 0) |
495 | 384k | pixel=image->colormap[(ssize_t) ConstrainColormapIndex(image, |
496 | 384k | ((unsigned char) index) & 0x80 ? 1 : 0,exception)]; |
497 | 660k | else |
498 | 660k | { |
499 | 660k | pixel.red=(MagickRealType) (((unsigned char) index) & 0x80 ? |
500 | 337k | QuantumRange : 0); |
501 | 660k | pixel.green=pixel.red; |
502 | 660k | pixel.blue=pixel.red; |
503 | 660k | } |
504 | 1.04M | break; |
505 | 1.04M | } |
506 | 815 | case 8: |
507 | 10.7k | default: |
508 | 10.7k | { |
509 | | /* |
510 | | Gray scale. |
511 | | */ |
512 | 10.7k | if (ReadBlob(image,1,pixels) != 1) |
513 | 10.6k | ThrowReaderException(CorruptImageError,"UnableToReadImageData"); |
514 | 10.6k | index=(Quantum) pixels[0]; |
515 | 10.6k | if (tga_info.colormap_type != 0) |
516 | 9.77k | pixel=image->colormap[(ssize_t) ConstrainColormapIndex(image, |
517 | 9.77k | (ssize_t) index,exception)]; |
518 | 907 | else |
519 | 907 | { |
520 | 907 | pixel.red=(MagickRealType) ScaleCharToQuantum((unsigned char) |
521 | 907 | index); |
522 | 907 | pixel.green=pixel.red; |
523 | 907 | pixel.blue=pixel.red; |
524 | 907 | } |
525 | 10.6k | break; |
526 | 10.7k | } |
527 | 1.69k | case 15: |
528 | 12.5k | case 16: |
529 | 12.5k | { |
530 | 12.5k | QuantumAny |
531 | 12.5k | range; |
532 | | |
533 | | /* |
534 | | 5 bits each of RGB. |
535 | | */ |
536 | 12.5k | if (ReadBlob(image,2,pixels) != 2) |
537 | 12.5k | ThrowReaderException(CorruptImageError,"UnableToReadImageData"); |
538 | 12.5k | j=pixels[0]; |
539 | 12.5k | k=pixels[1]; |
540 | 12.5k | range=GetQuantumRange(5UL); |
541 | 12.5k | pixel.red=(MagickRealType) ScaleAnyToQuantum(1UL*(k & 0x7c) >> 2, |
542 | 12.5k | range); |
543 | 12.5k | pixel.green=(MagickRealType) ScaleAnyToQuantum((1UL* |
544 | 12.5k | (k & 0x03) << 3)+(1UL*(j & 0xe0) >> 5),range); |
545 | 12.5k | pixel.blue=(MagickRealType) ScaleAnyToQuantum(1UL*(j & 0x1f),range); |
546 | 12.5k | if (image->alpha_trait != UndefinedPixelTrait) |
547 | 2.64k | pixel.alpha=(MagickRealType) ((k & 0x80) == 0 ? (Quantum) |
548 | 2.12k | TransparentAlpha : (Quantum) OpaqueAlpha); |
549 | 12.5k | if (image->storage_class == PseudoClass) |
550 | 816 | index=(Quantum) ConstrainColormapIndex(image,((ssize_t) k << 8)+ |
551 | 816 | j,exception); |
552 | 12.5k | break; |
553 | 12.5k | } |
554 | 206 | case 24: |
555 | 206 | { |
556 | | /* |
557 | | BGR pixels. |
558 | | */ |
559 | 206 | if (ReadBlob(image,3,pixels) != 3) |
560 | 197 | ThrowReaderException(CorruptImageError,"UnableToReadImageData"); |
561 | 197 | pixel.blue=(MagickRealType) ScaleCharToQuantum(pixels[0]); |
562 | 197 | pixel.green=(MagickRealType) ScaleCharToQuantum(pixels[1]); |
563 | 197 | pixel.red=(MagickRealType) ScaleCharToQuantum(pixels[2]); |
564 | 197 | break; |
565 | 206 | } |
566 | 886 | case 32: |
567 | 886 | { |
568 | | /* |
569 | | BGRA pixels. |
570 | | */ |
571 | 886 | if (ReadBlob(image,4,pixels) != 4) |
572 | 875 | ThrowReaderException(CorruptImageError,"UnableToReadImageData"); |
573 | 875 | pixel.blue=(MagickRealType) ScaleCharToQuantum(pixels[0]); |
574 | 875 | pixel.green=(MagickRealType) ScaleCharToQuantum(pixels[1]); |
575 | 875 | pixel.red=(MagickRealType) ScaleCharToQuantum(pixels[2]); |
576 | 875 | pixel.alpha=(MagickRealType) ScaleCharToQuantum(pixels[3]); |
577 | 875 | break; |
578 | 886 | } |
579 | 1.06M | } |
580 | 4.30M | if (status == MagickFalse) |
581 | 4.30M | ThrowReaderException(CorruptImageError,"UnableToReadImageData"); |
582 | 4.30M | if (image->storage_class == PseudoClass) |
583 | 2.74M | SetPixelIndex(image,index,q); |
584 | 4.30M | SetPixelRed(image,ClampToQuantum(pixel.red),q); |
585 | 4.30M | SetPixelGreen(image,ClampToQuantum(pixel.green),q); |
586 | 4.30M | SetPixelBlue(image,ClampToQuantum(pixel.blue),q); |
587 | 4.30M | if (image->alpha_trait != UndefinedPixelTrait) |
588 | 2.61M | SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q); |
589 | 4.30M | if (flip_x != MagickFalse) |
590 | 1.10M | q-=GetPixelChannels(image); |
591 | 3.20M | else |
592 | 3.20M | q+=(ptrdiff_t) GetPixelChannels(image); |
593 | 4.30M | } |
594 | 49.9k | offset+=offset_stepsize; |
595 | 49.9k | if (offset >= (ssize_t) image->rows) |
596 | 647 | { |
597 | 647 | base++; |
598 | 647 | offset=(ssize_t) base; |
599 | 647 | } |
600 | 49.9k | if (SyncAuthenticPixels(image,exception) == MagickFalse) |
601 | 0 | break; |
602 | 49.9k | if (image->previous == (Image *) NULL) |
603 | 49.9k | { |
604 | 49.9k | status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, |
605 | 49.9k | image->rows); |
606 | 49.9k | if (status == MagickFalse) |
607 | 0 | break; |
608 | 49.9k | } |
609 | 49.9k | } |
610 | 605 | if (EOFBlob(image) != MagickFalse) |
611 | 0 | ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile", |
612 | 605 | image->filename); |
613 | 605 | if (SeekBlob(image,-26,SEEK_END) >= 18) |
614 | 267 | { |
615 | | /* |
616 | | Optional header. |
617 | | */ |
618 | 267 | char |
619 | 267 | signature[18]; |
620 | | |
621 | 267 | unsigned long |
622 | 267 | extension; |
623 | | |
624 | 267 | extension=(unsigned long) ReadBlobLSBLong(image); |
625 | 267 | (void) ReadBlobLSBLong(image); |
626 | 267 | count=ReadBlob(image,18,signature); |
627 | 267 | if ((count == 18) && (extension > 3) && |
628 | 254 | (LocaleCompare(signature,"TRUEVISION-XFILE.") == 0) && |
629 | 104 | (SeekBlob(image,(MagickOffsetType) extension,SEEK_SET) == (MagickOffsetType) extension) && |
630 | 104 | (ReadBlobLSBShort(image) == 495)) |
631 | 34 | { |
632 | | /* |
633 | | Optional extension. |
634 | | */ |
635 | 34 | char |
636 | 34 | buffer[325] = { 0 }; |
637 | | |
638 | 34 | (void) ReadBlob(image,41,buffer); |
639 | 34 | buffer[41]='\0'; |
640 | 34 | (void) SetImageProperty(image,"tga:author",buffer,exception); |
641 | 34 | (void) ReadBlob(image,324,buffer); |
642 | 34 | buffer[324]='\0'; |
643 | 34 | (void) SetImageProperty(image,"tga:comment",buffer,exception); |
644 | 34 | (void) DiscardBlobBytes(image,59); |
645 | 34 | (void) ReadBlob(image,41,buffer); |
646 | 34 | buffer[41]='\0'; |
647 | 34 | (void) SetImageProperty(image,"tga:software",buffer,exception); |
648 | 34 | (void) DiscardBlobBytes(image,27); |
649 | 34 | if (((image->alpha_trait & BlendPixelTrait) != 0) && |
650 | 28 | (ReadBlobByte(image) != 3)) |
651 | 26 | image->alpha_trait=UndefinedPixelTrait; |
652 | 34 | } |
653 | 267 | } |
654 | 605 | if (CloseBlob(image) == MagickFalse) |
655 | 0 | status=MagickFalse; |
656 | 605 | if (status == MagickFalse) |
657 | 0 | return(DestroyImageList(image)); |
658 | 605 | return(GetFirstImageInList(image)); |
659 | 605 | } |
660 | | |
661 | | /* |
662 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
663 | | % % |
664 | | % % |
665 | | % % |
666 | | % R e g i s t e r T G A I m a g e % |
667 | | % % |
668 | | % % |
669 | | % % |
670 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
671 | | % |
672 | | % RegisterTGAImage() adds properties for the TGA image format to |
673 | | % the list of supported formats. The properties include the image format |
674 | | % tag, a method to read and/or write the format, whether the format |
675 | | % supports the saving of more than one frame to the same file or blob, |
676 | | % whether the format supports native in-memory I/O, and a brief |
677 | | % description of the format. |
678 | | % |
679 | | % The format of the RegisterTGAImage method is: |
680 | | % |
681 | | % size_t RegisterTGAImage(void) |
682 | | % |
683 | | */ |
684 | | ModuleExport size_t RegisterTGAImage(void) |
685 | 8 | { |
686 | 8 | MagickInfo |
687 | 8 | *entry; |
688 | | |
689 | 8 | entry=AcquireMagickInfo("TGA","ICB","Truevision Targa image"); |
690 | 8 | entry->decoder=(DecodeImageHandler *) ReadTGAImage; |
691 | 8 | entry->encoder=(EncodeImageHandler *) WriteTGAImage; |
692 | 8 | entry->flags|=CoderDecoderSeekableStreamFlag; |
693 | 8 | entry->flags^=CoderAdjoinFlag; |
694 | 8 | (void) RegisterMagickInfo(entry); |
695 | 8 | entry=AcquireMagickInfo("TGA","TGA","Truevision Targa image"); |
696 | 8 | entry->decoder=(DecodeImageHandler *) ReadTGAImage; |
697 | 8 | entry->encoder=(EncodeImageHandler *) WriteTGAImage; |
698 | 8 | entry->flags|=CoderDecoderSeekableStreamFlag; |
699 | 8 | entry->flags^=CoderAdjoinFlag; |
700 | 8 | (void) RegisterMagickInfo(entry); |
701 | 8 | entry=AcquireMagickInfo("TGA","VDA","Truevision Targa image"); |
702 | 8 | entry->decoder=(DecodeImageHandler *) ReadTGAImage; |
703 | 8 | entry->encoder=(EncodeImageHandler *) WriteTGAImage; |
704 | 8 | entry->flags|=CoderDecoderSeekableStreamFlag; |
705 | 8 | entry->flags^=CoderAdjoinFlag; |
706 | 8 | (void) RegisterMagickInfo(entry); |
707 | 8 | entry=AcquireMagickInfo("TGA","VST","Truevision Targa image"); |
708 | 8 | entry->decoder=(DecodeImageHandler *) ReadTGAImage; |
709 | 8 | entry->encoder=(EncodeImageHandler *) WriteTGAImage; |
710 | 8 | entry->flags|=CoderDecoderSeekableStreamFlag; |
711 | 8 | entry->flags^=CoderAdjoinFlag; |
712 | 8 | (void) RegisterMagickInfo(entry); |
713 | 8 | return(MagickImageCoderSignature); |
714 | 8 | } |
715 | | |
716 | | /* |
717 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
718 | | % % |
719 | | % % |
720 | | % % |
721 | | % U n r e g i s t e r T G A I m a g e % |
722 | | % % |
723 | | % % |
724 | | % % |
725 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
726 | | % |
727 | | % UnregisterTGAImage() removes format registrations made by the |
728 | | % TGA module from the list of supported formats. |
729 | | % |
730 | | % The format of the UnregisterTGAImage method is: |
731 | | % |
732 | | % UnregisterTGAImage(void) |
733 | | % |
734 | | */ |
735 | | ModuleExport void UnregisterTGAImage(void) |
736 | 0 | { |
737 | 0 | (void) UnregisterMagickInfo("ICB"); |
738 | 0 | (void) UnregisterMagickInfo("TGA"); |
739 | 0 | (void) UnregisterMagickInfo("VDA"); |
740 | 0 | (void) UnregisterMagickInfo("VST"); |
741 | 0 | } |
742 | | |
743 | | /* |
744 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
745 | | % % |
746 | | % % |
747 | | % % |
748 | | % W r i t e T G A I m a g e % |
749 | | % % |
750 | | % % |
751 | | % % |
752 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
753 | | % |
754 | | % WriteTGAImage() writes a image in the Truevision Targa rasterfile |
755 | | % format. |
756 | | % |
757 | | % The format of the WriteTGAImage method is: |
758 | | % |
759 | | % MagickBooleanType WriteTGAImage(const ImageInfo *image_info, |
760 | | % Image *image,ExceptionInfo *exception) |
761 | | % |
762 | | % A description of each parameter follows. |
763 | | % |
764 | | % o image_info: the image info. |
765 | | % |
766 | | % o image: The image. |
767 | | % |
768 | | */ |
769 | | static inline unsigned char *WriteTGAPixel(Image *image,TGAImageType image_type, |
770 | | const Quantum *p,const QuantumAny range,const double midpoint,unsigned char *q) |
771 | 1.00M | { |
772 | 1.00M | if (image_type == TGAColormap || image_type == TGARLEColormap) |
773 | 40.2k | *q++=(unsigned char) ((ssize_t) GetPixelIndex(image,p)); |
774 | 960k | else |
775 | 960k | { |
776 | 960k | if (image_type == TGAMonochrome || image_type == TGARLEMonochrome) |
777 | 29.6k | *q++=ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(image,p))); |
778 | 930k | else |
779 | 930k | if (image->depth == 5) |
780 | 9.18k | { |
781 | 9.18k | unsigned char |
782 | 9.18k | green, |
783 | 9.18k | value; |
784 | | |
785 | 9.18k | green=(unsigned char) ScaleQuantumToAny(GetPixelGreen(image,p), |
786 | 9.18k | range); |
787 | 9.18k | value=((unsigned char) ScaleQuantumToAny(GetPixelBlue(image,p), |
788 | 9.18k | range)) | ((green & 0x07) << 5); |
789 | 9.18k | *q++=value; |
790 | 9.18k | value=(((image->alpha_trait != UndefinedPixelTrait) && |
791 | 7.28k | ((double) GetPixelAlpha(image,p) > midpoint)) ? 0x80 : 0) | |
792 | 9.18k | ((unsigned char) ScaleQuantumToAny(GetPixelRed(image,p),range) << |
793 | 9.18k | 2) | ((green & 0x18) >> 3); |
794 | 9.18k | *q++=value; |
795 | 9.18k | } |
796 | 921k | else |
797 | 921k | { |
798 | 921k | *q++=ScaleQuantumToChar(GetPixelBlue(image,p)); |
799 | 921k | *q++=ScaleQuantumToChar(GetPixelGreen(image,p)); |
800 | 921k | *q++=ScaleQuantumToChar(GetPixelRed(image,p)); |
801 | 921k | if (image->alpha_trait != UndefinedPixelTrait) |
802 | 920k | *q++=ScaleQuantumToChar(GetPixelAlpha(image,p)); |
803 | 921k | } |
804 | 960k | } |
805 | 1.00M | return(q); |
806 | 1.00M | } |
807 | | |
808 | | static MagickBooleanType WriteTGAImage(const ImageInfo *image_info,Image *image, |
809 | | ExceptionInfo *exception) |
810 | 579 | { |
811 | 579 | CompressionType |
812 | 579 | compression; |
813 | | |
814 | 579 | const char |
815 | 579 | *comment, |
816 | 579 | *option; |
817 | | |
818 | 579 | const double |
819 | 579 | midpoint = (double) QuantumRange/2.0; |
820 | | |
821 | 579 | const Quantum |
822 | 579 | *p; |
823 | | |
824 | 579 | MagickBooleanType |
825 | 579 | status; |
826 | | |
827 | 579 | QuantumAny |
828 | 579 | range; |
829 | | |
830 | 579 | size_t |
831 | 579 | channels; |
832 | | |
833 | 579 | ssize_t |
834 | 579 | base, |
835 | 579 | count, |
836 | 579 | i, |
837 | 579 | offset, |
838 | 579 | x, |
839 | 579 | y; |
840 | | |
841 | 579 | TGAInfo |
842 | 579 | tga_info; |
843 | | |
844 | 579 | unsigned char |
845 | 579 | *pixels, |
846 | 579 | *q; |
847 | | |
848 | | /* |
849 | | Open output image file. |
850 | | */ |
851 | 579 | assert(image_info != (const ImageInfo *) NULL); |
852 | 579 | assert(image_info->signature == MagickCoreSignature); |
853 | 579 | assert(image != (Image *) NULL); |
854 | 579 | assert(image->signature == MagickCoreSignature); |
855 | 579 | assert(exception != (ExceptionInfo *) NULL); |
856 | 579 | assert(exception->signature == MagickCoreSignature); |
857 | 579 | if (IsEventLogging() != MagickFalse) |
858 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
859 | 579 | status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); |
860 | 579 | if (status == MagickFalse) |
861 | 0 | return(status); |
862 | | /* |
863 | | Initialize TGA raster file header. |
864 | | */ |
865 | 579 | if ((image->columns > 65535L) || (image->rows > 65535L)) |
866 | 579 | ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit"); |
867 | 579 | if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) |
868 | 0 | (void) TransformImageColorspace(image,sRGBColorspace,exception); |
869 | 579 | compression=image->compression; |
870 | 579 | if (image_info->compression != UndefinedCompression) |
871 | 0 | compression=image_info->compression; |
872 | 579 | range=GetQuantumRange(5UL); |
873 | 579 | tga_info.id_length=0; |
874 | 579 | comment=GetImageProperty(image,"comment",exception); |
875 | 579 | if (comment != (const char *) NULL) |
876 | 105 | tga_info.id_length=(unsigned char) MagickMin(strlen(comment),255); |
877 | 579 | tga_info.colormap_type=0; |
878 | 579 | tga_info.colormap_index=0; |
879 | 579 | tga_info.colormap_length=0; |
880 | 579 | tga_info.colormap_size=0; |
881 | 579 | tga_info.x_origin=0; |
882 | 579 | if ((image->page.x > 0) && (image->page.x <= MAGICK_USHORT_MAX)) |
883 | 486 | tga_info.x_origin=(unsigned short) image->page.x; |
884 | 579 | tga_info.y_origin=0; |
885 | 579 | if ((image->page.y > 0) && (image->page.y <= MAGICK_USHORT_MAX)) |
886 | 536 | tga_info.y_origin=(unsigned short) image->page.y; |
887 | 579 | tga_info.width=(unsigned short) image->columns; |
888 | 579 | tga_info.height=(unsigned short) image->rows; |
889 | 579 | tga_info.bits_per_pixel=8; |
890 | 579 | tga_info.attributes=0; |
891 | 579 | if ((image_info->type != TrueColorType) && |
892 | 579 | (image_info->type != TrueColorAlphaType) && |
893 | 579 | (image_info->type != PaletteType) && |
894 | 579 | ((image->alpha_trait & BlendPixelTrait) == 0) && |
895 | 205 | (IdentifyImageCoderGray(image,exception) != MagickFalse)) |
896 | 142 | tga_info.image_type=compression == RLECompression ? TGARLEMonochrome : |
897 | 142 | TGAMonochrome; |
898 | 437 | else |
899 | 437 | if ((image->storage_class == DirectClass) || (image->colors > 256)) |
900 | 284 | { |
901 | | /* |
902 | | Full color TGA raster. |
903 | | */ |
904 | 284 | tga_info.image_type=compression == RLECompression ? TGARLERGB : TGARGB; |
905 | 284 | if (image_info->depth == 5) |
906 | 0 | { |
907 | 0 | tga_info.bits_per_pixel=16; |
908 | 0 | if (image->alpha_trait != UndefinedPixelTrait) |
909 | 0 | tga_info.attributes=1; /* # of alpha bits */ |
910 | 0 | } |
911 | 284 | else |
912 | 284 | { |
913 | 284 | tga_info.bits_per_pixel=24; |
914 | 284 | if (image->alpha_trait != UndefinedPixelTrait) |
915 | 229 | { |
916 | 229 | tga_info.bits_per_pixel=32; |
917 | 229 | tga_info.attributes=8; /* # of alpha bits */ |
918 | 229 | } |
919 | 284 | } |
920 | 284 | } |
921 | 153 | else |
922 | 153 | { |
923 | | /* |
924 | | Colormapped TGA raster. |
925 | | */ |
926 | 153 | tga_info.image_type=compression == RLECompression ? TGARLEColormap : |
927 | 153 | TGAColormap; |
928 | 153 | tga_info.colormap_type=1; |
929 | 153 | tga_info.colormap_length=(unsigned short) image->colors; |
930 | 153 | if (image_info->depth == 5) |
931 | 0 | tga_info.colormap_size=16; |
932 | 153 | else |
933 | 153 | if (image->alpha_trait != UndefinedPixelTrait) |
934 | 145 | tga_info.colormap_size=32; |
935 | 8 | else |
936 | 8 | tga_info.colormap_size=24; |
937 | 153 | } |
938 | 579 | switch (image->orientation) |
939 | 579 | { |
940 | 0 | case BottomRightOrientation: |
941 | 0 | { |
942 | 0 | tga_info.attributes|=0x10; |
943 | 0 | break; |
944 | 0 | } |
945 | 0 | case UndefinedOrientation: |
946 | 579 | case TopLeftOrientation: |
947 | 579 | { |
948 | 579 | tga_info.attributes|=0x20; |
949 | 579 | break; |
950 | 0 | } |
951 | 0 | case TopRightOrientation: |
952 | 0 | { |
953 | 0 | tga_info.attributes|=0x30; |
954 | 0 | break; |
955 | 0 | } |
956 | 0 | default: |
957 | 0 | break; |
958 | 579 | } |
959 | 579 | if ((image->columns > 65535) || (image->rows > 65535)) |
960 | 579 | ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit"); |
961 | | /* |
962 | | Write TGA header. |
963 | | */ |
964 | 579 | (void) WriteBlobByte(image,tga_info.id_length); |
965 | 579 | (void) WriteBlobByte(image,tga_info.colormap_type); |
966 | 579 | (void) WriteBlobByte(image,(unsigned char) tga_info.image_type); |
967 | 579 | (void) WriteBlobLSBShort(image,tga_info.colormap_index); |
968 | 579 | (void) WriteBlobLSBShort(image,tga_info.colormap_length); |
969 | 579 | (void) WriteBlobByte(image,tga_info.colormap_size); |
970 | 579 | (void) WriteBlobLSBShort(image,tga_info.x_origin); |
971 | 579 | (void) WriteBlobLSBShort(image,tga_info.y_origin); |
972 | 579 | (void) WriteBlobLSBShort(image,tga_info.width); |
973 | 579 | (void) WriteBlobLSBShort(image,tga_info.height); |
974 | 579 | (void) WriteBlobByte(image,tga_info.bits_per_pixel); |
975 | 579 | (void) WriteBlobByte(image,tga_info.attributes); |
976 | 579 | if (tga_info.id_length != 0) |
977 | 83 | (void) WriteBlob(image,tga_info.id_length,(unsigned char *) comment); |
978 | 579 | if (tga_info.colormap_type != 0) |
979 | 153 | { |
980 | 153 | unsigned char |
981 | 153 | green, |
982 | 153 | *targa_colormap; |
983 | | |
984 | | /* |
985 | | Dump colormap to file (blue, green, red byte order). |
986 | | */ |
987 | 153 | targa_colormap=(unsigned char *) AcquireQuantumMemory((size_t) |
988 | 153 | tga_info.colormap_length,(tga_info.colormap_size/8)* |
989 | 153 | sizeof(*targa_colormap)); |
990 | 153 | if (targa_colormap == (unsigned char *) NULL) |
991 | 153 | ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); |
992 | 153 | q=targa_colormap; |
993 | 15.6k | for (i=0; i < (ssize_t) image->colors; i++) |
994 | 15.4k | { |
995 | 15.4k | if (image_info->depth == 5) |
996 | 0 | { |
997 | 0 | green=(unsigned char) ScaleQuantumToAny(ClampToQuantum( |
998 | 0 | image->colormap[i].green),range); |
999 | 0 | *q++=((unsigned char) ScaleQuantumToAny(ClampToQuantum( |
1000 | 0 | image->colormap[i].blue),range)) | ((green & 0x07) << 5); |
1001 | 0 | *q++=(((image->alpha_trait != UndefinedPixelTrait) && ((double) |
1002 | 0 | ClampToQuantum(image->colormap[i].alpha) > midpoint)) ? |
1003 | 0 | 0x80 : 0) | ((unsigned char) ScaleQuantumToAny(ClampToQuantum( |
1004 | 0 | image->colormap[i].red),range) << 2) | ((green & 0x18) >> 3); |
1005 | 0 | } |
1006 | 15.4k | else |
1007 | 15.4k | { |
1008 | 15.4k | *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue)); |
1009 | 15.4k | *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green)); |
1010 | 15.4k | *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red)); |
1011 | 15.4k | if (image->alpha_trait != UndefinedPixelTrait) |
1012 | 15.1k | *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].alpha)); |
1013 | 15.4k | } |
1014 | 15.4k | } |
1015 | 153 | (void) WriteBlob(image,(size_t) ((tga_info.colormap_size/8)* |
1016 | 153 | tga_info.colormap_length),targa_colormap); |
1017 | 153 | targa_colormap=(unsigned char *) RelinquishMagickMemory(targa_colormap); |
1018 | 153 | } |
1019 | | /* |
1020 | | Convert MIFF to TGA raster pixels. |
1021 | | */ |
1022 | 579 | pixels=(unsigned char *) AcquireQuantumMemory(image->columns, |
1023 | 579 | (compression == RLECompression ? 5 : 4)*sizeof(*pixels)); |
1024 | 579 | if (pixels == (unsigned char *) NULL) |
1025 | 579 | ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); |
1026 | 579 | base=0; |
1027 | 579 | offset=0; |
1028 | 579 | channels=GetPixelChannels(image); |
1029 | 46.2k | for (y=0; y < (ssize_t) image->rows; y++) |
1030 | 45.6k | { |
1031 | 45.6k | p=GetVirtualPixels(image,0,offset,image->columns,1,exception); |
1032 | 45.6k | if (p == (const Quantum *) NULL) |
1033 | 0 | break; |
1034 | 45.6k | q=pixels; |
1035 | 45.6k | if (compression == RLECompression) |
1036 | 42.5k | { |
1037 | 42.5k | x=0; |
1038 | 42.5k | count=0; |
1039 | 572k | while (x < (ssize_t) image->columns) |
1040 | 530k | { |
1041 | 530k | i=1; |
1042 | 3.87M | while ((i < 128) && (count + i < 128) && |
1043 | 3.85M | ((x + i) < (ssize_t) image->columns)) |
1044 | 3.81M | { |
1045 | 3.81M | if (tga_info.image_type == TGARLEColormap) |
1046 | 186k | { |
1047 | 186k | if (GetPixelIndex(image,p+(i*(ssize_t) channels)) != |
1048 | 186k | GetPixelIndex(image,p+((i-1)*(ssize_t) channels))) |
1049 | 35.1k | break; |
1050 | 186k | } |
1051 | 3.62M | else |
1052 | 3.62M | if (tga_info.image_type == TGARLEMonochrome) |
1053 | 1.63M | { |
1054 | 1.63M | if (GetPixelLuma(image,p+(i*(ssize_t) channels)) != |
1055 | 1.63M | GetPixelLuma(image,p+((i-1)*(ssize_t) channels))) |
1056 | 836 | break; |
1057 | 1.63M | } |
1058 | 1.99M | else |
1059 | 1.99M | { |
1060 | 1.99M | if ((GetPixelBlue(image,p+(i*(ssize_t) channels)) != |
1061 | 1.99M | GetPixelBlue(image,p+((i-1)*(ssize_t) channels))) || |
1062 | 1.56M | (GetPixelGreen(image,p+(i*(ssize_t) channels)) != |
1063 | 1.56M | GetPixelGreen(image,p+((i-1)*(ssize_t) channels))) || |
1064 | 1.56M | (GetPixelRed(image,p+(i*(ssize_t) channels)) != |
1065 | 1.56M | GetPixelRed(image,p+((i-1)*(ssize_t) channels)))) |
1066 | 429k | break; |
1067 | 1.56M | if ((image->alpha_trait != UndefinedPixelTrait) && |
1068 | 1.55M | (GetPixelAlpha(image,p+(i*(ssize_t) channels)) != |
1069 | 1.55M | GetPixelAlpha(image,p+(i-1)*(ssize_t) channels))) |
1070 | 197 | break; |
1071 | 1.56M | } |
1072 | 3.34M | i++; |
1073 | 3.34M | } |
1074 | 530k | if (i < 3) |
1075 | 486k | { |
1076 | 486k | count+=i; |
1077 | 486k | p+=(ptrdiff_t) (i*(ssize_t) channels); |
1078 | 486k | } |
1079 | 530k | if ((i >= 3) || (count == 128) || |
1080 | 485k | ((x + i) == (ssize_t) image->columns)) |
1081 | 76.0k | { |
1082 | 76.0k | if (count > 0) |
1083 | 42.1k | { |
1084 | 42.1k | *q++=(unsigned char) (--count); |
1085 | 674k | while (count >= 0) |
1086 | 632k | { |
1087 | 632k | q=WriteTGAPixel(image,tga_info.image_type,p- |
1088 | 632k | ((count+1)*(ssize_t) channels),range,midpoint,q); |
1089 | 632k | count--; |
1090 | 632k | } |
1091 | 42.1k | count=0; |
1092 | 42.1k | } |
1093 | 76.0k | } |
1094 | 530k | if (i >= 3) |
1095 | 43.0k | { |
1096 | 43.0k | *q++=(unsigned char) ((i-1) | 0x80); |
1097 | 43.0k | q=WriteTGAPixel(image,tga_info.image_type,p,range,midpoint,q); |
1098 | 43.0k | p+=(ptrdiff_t) (i*(ssize_t) channels); |
1099 | 43.0k | } |
1100 | 530k | x+=i; |
1101 | 530k | } |
1102 | 42.5k | } |
1103 | 3.11k | else |
1104 | 328k | for (x=0; x < (ssize_t) image->columns; x++) |
1105 | 325k | { |
1106 | 325k | q=WriteTGAPixel(image,tga_info.image_type,p,range,midpoint,q); |
1107 | 325k | p+=(ptrdiff_t) channels; |
1108 | 325k | } |
1109 | 45.6k | count=WriteBlob(image,(size_t) (q-pixels),pixels); |
1110 | 45.6k | if (count != (ssize_t) (q-pixels)) |
1111 | 0 | { |
1112 | 0 | status=MagickFalse; |
1113 | 0 | break; |
1114 | 0 | } |
1115 | 45.6k | if (((unsigned char) (tga_info.attributes & 0xc0) >> 6) == 2) |
1116 | 0 | offset+=2; |
1117 | 45.6k | else |
1118 | 45.6k | offset++; |
1119 | 45.6k | if (offset >= (ssize_t) image->rows) |
1120 | 579 | { |
1121 | 579 | base++; |
1122 | 579 | offset=base; |
1123 | 579 | } |
1124 | 45.6k | if (image->previous == (Image *) NULL) |
1125 | 45.6k | { |
1126 | 45.6k | status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, |
1127 | 45.6k | image->rows); |
1128 | 45.6k | if (status == MagickFalse) |
1129 | 0 | break; |
1130 | 45.6k | } |
1131 | 45.6k | } |
1132 | 579 | pixels=(unsigned char *) RelinquishMagickMemory(pixels); |
1133 | | /* |
1134 | | Optional footer. |
1135 | | */ |
1136 | 579 | option=GetImageOption(image_info,"tga:write-footer"); |
1137 | 579 | if ((status != MagickFalse) && (IsStringTrue(option) != MagickFalse)) |
1138 | 0 | { |
1139 | 0 | WriteBlobLong(image,0); |
1140 | 0 | WriteBlobLong(image,0); |
1141 | 0 | WriteBlobString(image,"TRUEVISION-XFILE."); |
1142 | 0 | WriteBlobByte(image,0); |
1143 | 0 | } |
1144 | 579 | if (CloseBlob(image) == MagickFalse) |
1145 | 0 | status=MagickFalse; |
1146 | 579 | return(status); |
1147 | 579 | } |