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