/src/imagemagick/coders/tim2.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3 | | % % |
4 | | % % |
5 | | % % |
6 | | % TTTTT IIIII M M 222 % |
7 | | % T I MM MM 2 2 % |
8 | | % T I M M M 2 % |
9 | | % T I M M 2 % |
10 | | % T IIIII M M 22222 % |
11 | | % % |
12 | | % % |
13 | | % Read PSX TIM2 Image Format % |
14 | | % % |
15 | | % Software Design % |
16 | | % Ramiro Balado Ordax % |
17 | | % May 2019 % |
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/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 | | /* |
41 | | Include declarations. |
42 | | */ |
43 | | #include "MagickCore/studio.h" |
44 | | #include "MagickCore/blob.h" |
45 | | #include "MagickCore/blob-private.h" |
46 | | #include "MagickCore/cache.h" |
47 | | #include "MagickCore/colormap.h" |
48 | | #include "MagickCore/channel.h" |
49 | | #include "MagickCore/exception.h" |
50 | | #include "MagickCore/exception-private.h" |
51 | | #include "MagickCore/image.h" |
52 | | #include "MagickCore/image-private.h" |
53 | | #include "MagickCore/list.h" |
54 | | #include "MagickCore/magick.h" |
55 | | #include "MagickCore/memory_.h" |
56 | | #include "MagickCore/monitor.h" |
57 | | #include "MagickCore/monitor-private.h" |
58 | | #include "MagickCore/pixel-accessor.h" |
59 | | #include "MagickCore/quantum-private.h" |
60 | | #include "MagickCore/static.h" |
61 | | #include "MagickCore/string_.h" |
62 | | #include "MagickCore/module.h" |
63 | | |
64 | | /* |
65 | | Typedef declarations |
66 | | */ |
67 | | typedef struct _TIM2FileHeader |
68 | | { |
69 | | unsigned int |
70 | | magic_num; |
71 | | |
72 | | unsigned char |
73 | | format_vers, |
74 | | format_type; |
75 | | |
76 | | unsigned short |
77 | | image_count; |
78 | | } TIM2FileHeader; |
79 | | |
80 | | typedef struct _TIM2ImageHeader |
81 | | { |
82 | | unsigned int |
83 | | total_size, |
84 | | clut_size, |
85 | | image_size; |
86 | | |
87 | | unsigned short |
88 | | header_size, |
89 | | clut_color_count; |
90 | | |
91 | | unsigned char |
92 | | img_format, |
93 | | mipmap_count, |
94 | | clut_type, |
95 | | bpp_type; |
96 | | |
97 | | unsigned short |
98 | | width, |
99 | | height; |
100 | | |
101 | | MagickSizeType |
102 | | GsTex0, |
103 | | GsTex1; |
104 | | |
105 | | unsigned int |
106 | | GsRegs, |
107 | | GsTexClut; |
108 | | } TIM2ImageHeader; |
109 | | |
110 | | typedef enum |
111 | | { |
112 | | CSM1=0, |
113 | | CSM2=1, |
114 | | } CSM; |
115 | | |
116 | | typedef enum |
117 | | { |
118 | | RGBA32=0, |
119 | | RGB24=1, |
120 | | RGBA16=2, |
121 | | } TIM2ColorEncoding; |
122 | | |
123 | | /* |
124 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
125 | | % % |
126 | | % % |
127 | | % % |
128 | | % R e a d T I M 2 I m a g e % |
129 | | % % |
130 | | % % |
131 | | % % |
132 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
133 | | % |
134 | | % ReadTIM2Image() reads a PS2 TIM image file and returns it. It |
135 | | % allocates the memory necessary for the new Image structure and returns a |
136 | | % pointer to the new image. |
137 | | % |
138 | | % The format of the ReadTIM2Image method is: |
139 | | % |
140 | | % Image *ReadTIM2Image(const ImageInfo *image_info, |
141 | | % ExceptionInfo *exception) |
142 | | % |
143 | | % A description of each parameter follows: |
144 | | % |
145 | | % o image_info: the image info. |
146 | | % |
147 | | % o exception: return any errors or warnings in this structure. |
148 | | % |
149 | | */ |
150 | | static inline void ReadTIM2ImageHeader(Image *image,TIM2ImageHeader *header) |
151 | 7 | { |
152 | 7 | header->total_size=ReadBlobLSBLong(image); |
153 | 7 | header->clut_size=ReadBlobLSBLong(image); |
154 | 7 | header->image_size=ReadBlobLSBLong(image); |
155 | 7 | header->header_size=ReadBlobLSBShort(image); |
156 | | |
157 | 7 | header->clut_color_count=ReadBlobLSBShort(image); |
158 | 7 | header->img_format=(unsigned char) ReadBlobByte(image); |
159 | 7 | header->mipmap_count=(unsigned char) ReadBlobByte(image); |
160 | 7 | header->clut_type=(unsigned char) ReadBlobByte(image); |
161 | 7 | header->bpp_type=(unsigned char) ReadBlobByte(image); |
162 | | |
163 | 7 | header->width=ReadBlobLSBShort(image); |
164 | 7 | header->height=ReadBlobLSBShort(image); |
165 | | |
166 | 7 | header->GsTex0=ReadBlobMSBLongLong(image); |
167 | 7 | header->GsTex1=ReadBlobMSBLongLong(image); |
168 | 7 | header->GsRegs=ReadBlobMSBLong(image); |
169 | 7 | header->GsTexClut=ReadBlobMSBLong(image); |
170 | 7 | } |
171 | | |
172 | | static inline Quantum GetChannelValue(unsigned int word,unsigned char channel, |
173 | | TIM2ColorEncoding ce) |
174 | 0 | { |
175 | 0 | switch(ce) |
176 | 0 | { |
177 | 0 | case RGBA16: |
178 | | /* Documentation specifies padding with zeros for converting from 5 to 8 bits. */ |
179 | 0 | return ScaleCharToQuantum((word>>channel*5 & ~(~0x0U<<5))<<3); |
180 | 0 | case RGB24: |
181 | 0 | case RGBA32: |
182 | 0 | return ScaleCharToQuantum(word>>channel*8 & ~(~0x0U<<8)); |
183 | 0 | default: |
184 | 0 | return QuantumRange; |
185 | 0 | } |
186 | 0 | } |
187 | | |
188 | | static inline Quantum GetAlpha(unsigned int word,TIM2ColorEncoding ce) |
189 | 0 | { |
190 | 0 | switch(ce) |
191 | 0 | { |
192 | 0 | case RGBA16: |
193 | 0 | return ScaleCharToQuantum((word>>3*5&0x1F)==0?0:0xFF); |
194 | 0 | case RGBA32: |
195 | | /* 0x80 -> 1.0 alpha. Multiply by 2 and clamp to 0xFF */ |
196 | 0 | return ScaleCharToQuantum(MagickMin((word>>3*8&0xFF)<<1,0xFF)); |
197 | 0 | default: |
198 | 0 | return 0xFF; |
199 | 0 | } |
200 | 0 | } |
201 | | |
202 | | static inline void deshufflePalette(Image *image,PixelInfo* oldColormap) |
203 | 0 | { |
204 | 0 | const size_t |
205 | 0 | pages=image->colors/32, /* Pages per CLUT */ |
206 | 0 | blocks=4, /* Blocks per page */ |
207 | 0 | colors=8; /* Colors per block */ |
208 | |
|
209 | 0 | int |
210 | 0 | page; |
211 | |
|
212 | 0 | size_t |
213 | 0 | i=0; |
214 | |
|
215 | 0 | (void) memcpy(oldColormap,image->colormap,(size_t)image->colors* |
216 | 0 | sizeof(*oldColormap)); |
217 | | |
218 | | /* |
219 | | * Swap the 2nd and 3rd block in each page |
220 | | */ |
221 | 0 | for (page=0; page < (ssize_t) pages; page++) |
222 | 0 | { |
223 | 0 | memcpy(&(image->colormap[i+1*colors]),&(oldColormap[i+2*colors]),colors* |
224 | 0 | sizeof(PixelInfo)); |
225 | 0 | memcpy(&(image->colormap[i+2*colors]),&(oldColormap[i+1*colors]),colors* |
226 | 0 | sizeof(PixelInfo)); |
227 | |
|
228 | 0 | i+=blocks*colors; |
229 | 0 | } |
230 | 0 | } |
231 | | |
232 | | static MagickBooleanType ReadTIM2ImageData(const ImageInfo *image_info, |
233 | | Image *image,TIM2ImageHeader *header,char clut_depth,char bits_per_pixel, |
234 | | ExceptionInfo *exception) |
235 | 0 | { |
236 | 0 | MagickBooleanType |
237 | 0 | status; |
238 | |
|
239 | 0 | ssize_t |
240 | 0 | x; |
241 | |
|
242 | 0 | Quantum |
243 | 0 | *q; |
244 | |
|
245 | 0 | size_t |
246 | 0 | bits_per_line, |
247 | 0 | bytes_per_line; |
248 | |
|
249 | 0 | ssize_t |
250 | 0 | count, |
251 | 0 | y; |
252 | |
|
253 | 0 | unsigned char |
254 | 0 | *p, |
255 | 0 | *row_data; |
256 | |
|
257 | 0 | unsigned int |
258 | 0 | word; |
259 | |
|
260 | 0 | status=SetImageExtent(image,image->columns,image->rows,exception); |
261 | 0 | if (status == MagickFalse) |
262 | 0 | return(MagickFalse); |
263 | | /* |
264 | | * User data |
265 | | */ |
266 | 0 | status=DiscardBlobBytes(image,header->header_size-48); |
267 | 0 | if (status == MagickFalse) |
268 | 0 | return(MagickFalse); |
269 | | /* |
270 | | * Image data |
271 | | */ |
272 | 0 | bits_per_line=image->columns*(size_t) bits_per_pixel; |
273 | 0 | bytes_per_line=bits_per_line/8 + ((bits_per_line%8==0) ? 0 : 1); |
274 | 0 | row_data=(unsigned char*) AcquireQuantumMemory(1,bytes_per_line); |
275 | 0 | if (row_data == (unsigned char *) NULL) |
276 | 0 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
277 | 0 | image_info->filename); |
278 | 0 | if (clut_depth != 0) |
279 | 0 | { |
280 | 0 | image->colors=header->clut_color_count; |
281 | 0 | if (AcquireImageColormap(image,image->colors,exception) == MagickFalse) |
282 | 0 | { |
283 | 0 | row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
284 | 0 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
285 | 0 | image_info->filename); |
286 | 0 | } |
287 | 0 | switch (bits_per_pixel) |
288 | 0 | { |
289 | 0 | case 4: |
290 | 0 | { |
291 | 0 | for (y=0; y<(ssize_t) image->rows; y++) |
292 | 0 | { |
293 | 0 | q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
294 | 0 | if (q == (Quantum *) NULL) |
295 | 0 | break; |
296 | 0 | count=ReadBlob(image,bytes_per_line,row_data); |
297 | 0 | if (count != (ssize_t) bytes_per_line) |
298 | 0 | { |
299 | 0 | row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
300 | 0 | ThrowBinaryException(CorruptImageError, |
301 | 0 | "InsufficientImageDataInFile",image_info->filename); |
302 | 0 | } |
303 | 0 | p=row_data; |
304 | 0 | for (x=0; x < ((ssize_t) image->columns-1); x+=2) |
305 | 0 | { |
306 | 0 | SetPixelIndex(image,(Quantum)((*p >> 0) & 0x0f),q); |
307 | 0 | q+=(ptrdiff_t) GetPixelChannels(image); |
308 | 0 | SetPixelIndex(image,(Quantum)((*p >> 4) & 0x0f),q); |
309 | 0 | p++; |
310 | 0 | q+=(ptrdiff_t) GetPixelChannels(image); |
311 | 0 | } |
312 | 0 | if ((image->columns % 2) != 0) |
313 | 0 | { |
314 | 0 | SetPixelIndex(image,(Quantum)((*p >> 4) & 0x0f),q); |
315 | 0 | p++; |
316 | 0 | q+=(ptrdiff_t) GetPixelChannels(image); |
317 | 0 | } |
318 | 0 | if (SyncAuthenticPixels(image,exception) == MagickFalse) |
319 | 0 | break; |
320 | 0 | if (image->previous == (Image *) NULL) |
321 | 0 | { |
322 | 0 | status=SetImageProgress(image,LoadImageTag, |
323 | 0 | (MagickOffsetType) y,image->rows); |
324 | 0 | if (status == MagickFalse) |
325 | 0 | break; |
326 | 0 | } |
327 | 0 | } |
328 | 0 | break; |
329 | 0 | } |
330 | 0 | case 8: |
331 | 0 | { |
332 | 0 | for (y=0;y<(ssize_t) image->rows; y++) |
333 | 0 | { |
334 | 0 | q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
335 | 0 | if (q == (Quantum *) NULL) |
336 | 0 | break; |
337 | 0 | count=ReadBlob(image,bytes_per_line,row_data); |
338 | 0 | if (count != (ssize_t) bytes_per_line) |
339 | 0 | { |
340 | 0 | row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
341 | 0 | ThrowBinaryException(CorruptImageError, |
342 | 0 | "InsufficientImageDataInFile",image_info->filename); |
343 | 0 | } |
344 | 0 | p=row_data; |
345 | 0 | for (x=0; x < (ssize_t) image->columns; x++) |
346 | 0 | { |
347 | 0 | SetPixelIndex(image,*p,q); |
348 | 0 | p++; |
349 | 0 | q+=(ptrdiff_t) GetPixelChannels(image); |
350 | 0 | } |
351 | 0 | if (SyncAuthenticPixels(image,exception) == MagickFalse) |
352 | 0 | break; |
353 | 0 | if (image->previous == (Image *) NULL) |
354 | 0 | { |
355 | 0 | status=SetImageProgress(image,LoadImageTag, |
356 | 0 | (MagickOffsetType) y,image->rows); |
357 | 0 | if (status == MagickFalse) |
358 | 0 | break; |
359 | 0 | } |
360 | 0 | } |
361 | 0 | break; |
362 | 0 | } |
363 | 0 | default: |
364 | 0 | { |
365 | 0 | row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
366 | 0 | ThrowBinaryException(CorruptImageError,"ImproperImageHeader", |
367 | 0 | image_info->filename); |
368 | 0 | } |
369 | 0 | } |
370 | 0 | SyncImage(image,exception); |
371 | 0 | } |
372 | 0 | else /* has_clut==false */ |
373 | 0 | { |
374 | 0 | switch (bits_per_pixel) |
375 | 0 | { |
376 | 0 | case 16: |
377 | 0 | { |
378 | 0 | for (y=0; y<(ssize_t) image->rows; y++) |
379 | 0 | { |
380 | 0 | q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
381 | 0 | if (q == (Quantum *) NULL) |
382 | 0 | break; |
383 | 0 | count=ReadBlob(image,bytes_per_line,row_data); |
384 | 0 | if (count != (ssize_t) bytes_per_line) |
385 | 0 | { |
386 | 0 | row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
387 | 0 | ThrowBinaryException(CorruptImageError, |
388 | 0 | "InsufficientImageDataInFile",image_info->filename); |
389 | 0 | } |
390 | 0 | p=row_data; |
391 | 0 | for (x=0; x < (ssize_t) image->columns; x++) |
392 | 0 | { |
393 | 0 | word = ((unsigned int)* p )<<0*8 | |
394 | 0 | ((unsigned int)*(p+1))<<1*8; |
395 | |
|
396 | 0 | SetPixelRed(image,GetChannelValue(word,0,RGBA16),q); |
397 | 0 | SetPixelGreen(image,GetChannelValue(word,1,RGBA16),q); |
398 | 0 | SetPixelBlue(image,GetChannelValue(word,2,RGBA16),q); |
399 | 0 | SetPixelAlpha(image,GetAlpha(word,RGBA16),q); |
400 | 0 | q+=(ptrdiff_t) GetPixelChannels(image); |
401 | 0 | p+=(ptrdiff_t) sizeof(unsigned short); |
402 | 0 | } |
403 | 0 | if (SyncAuthenticPixels(image,exception) == MagickFalse) |
404 | 0 | break; |
405 | 0 | if (image->previous == (Image *) NULL) |
406 | 0 | { |
407 | 0 | status=SetImageProgress(image,LoadImageTag, |
408 | 0 | (MagickOffsetType) y,image->rows); |
409 | 0 | if (status == MagickFalse) |
410 | 0 | break; |
411 | 0 | } |
412 | 0 | } |
413 | 0 | break; |
414 | 0 | } |
415 | 0 | case 24: |
416 | 0 | { |
417 | 0 | for (y = 0; y<(ssize_t) image->rows; y++) |
418 | 0 | { |
419 | 0 | q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
420 | 0 | if (q == (Quantum *) NULL) |
421 | 0 | break; |
422 | 0 | count=ReadBlob(image,bytes_per_line,row_data); |
423 | 0 | if (count != (ssize_t) bytes_per_line) |
424 | 0 | { |
425 | 0 | row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
426 | 0 | ThrowBinaryException(CorruptImageError, |
427 | 0 | "InsufficientImageDataInFile",image_info->filename); |
428 | 0 | } |
429 | 0 | p=row_data; |
430 | 0 | for (x=0; x < (ssize_t) image->columns; x++) |
431 | 0 | { |
432 | 0 | word = (unsigned int)(* p )<<0*8 | |
433 | 0 | (unsigned int)(*(p+1))<<1*8 | |
434 | 0 | (unsigned int)(*(p+2))<<2*8; |
435 | |
|
436 | 0 | SetPixelRed(image,GetChannelValue(word,0,RGB24),q); |
437 | 0 | SetPixelGreen(image,GetChannelValue(word,1,RGB24),q); |
438 | 0 | SetPixelBlue(image,GetChannelValue(word,2,RGB24),q); |
439 | 0 | q+=(ptrdiff_t) GetPixelChannels(image); |
440 | 0 | p+=(ptrdiff_t) 3; |
441 | 0 | } |
442 | 0 | if (SyncAuthenticPixels(image,exception) == MagickFalse) |
443 | 0 | break; |
444 | 0 | if (image->previous == (Image *) NULL) |
445 | 0 | { |
446 | 0 | status=SetImageProgress(image,LoadImageTag, |
447 | 0 | (MagickOffsetType) y,image->rows); |
448 | 0 | if (status == MagickFalse) |
449 | 0 | break; |
450 | 0 | } |
451 | 0 | } |
452 | 0 | break; |
453 | 0 | } |
454 | 0 | case 32: |
455 | 0 | { |
456 | 0 | for (y = 0; y<(ssize_t) image->rows; y++) |
457 | 0 | { |
458 | 0 | q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
459 | 0 | if (q == (Quantum *) NULL) |
460 | 0 | break; |
461 | 0 | count=ReadBlob(image,bytes_per_line,row_data); |
462 | 0 | if (count != (ssize_t) bytes_per_line) |
463 | 0 | { |
464 | 0 | row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
465 | 0 | ThrowBinaryException(CorruptImageError, |
466 | 0 | "InsufficientImageDataInFile",image_info->filename); |
467 | 0 | } |
468 | 0 | p=row_data; |
469 | 0 | for (x=0; x < (ssize_t) image->columns; x++) |
470 | 0 | { |
471 | 0 | word = ((unsigned int)* p )<<0*8 | |
472 | 0 | ((unsigned int)*(p+1))<<1*8 | |
473 | 0 | ((unsigned int)*(p+2))<<2*8 | |
474 | 0 | ((unsigned int)*(p+3))<<3*8; |
475 | |
|
476 | 0 | SetPixelRed(image,GetChannelValue(word,0,RGBA32),q); |
477 | 0 | SetPixelGreen(image,GetChannelValue(word,1,RGBA32),q); |
478 | 0 | SetPixelBlue(image,GetChannelValue(word,2,RGBA32),q); |
479 | 0 | SetPixelAlpha(image,GetAlpha(word,RGBA32),q); |
480 | 0 | q+=(ptrdiff_t) GetPixelChannels(image); |
481 | 0 | p+=(ptrdiff_t) 4; |
482 | 0 | } |
483 | 0 | if (SyncAuthenticPixels(image,exception) == MagickFalse) |
484 | 0 | break; |
485 | 0 | if (image->previous == (Image *) NULL) |
486 | 0 | { |
487 | 0 | status=SetImageProgress(image,LoadImageTag, |
488 | 0 | (MagickOffsetType) y,image->rows); |
489 | 0 | if (status == MagickFalse) |
490 | 0 | break; |
491 | 0 | } |
492 | 0 | } |
493 | 0 | break; |
494 | 0 | } |
495 | 0 | default: |
496 | 0 | { |
497 | 0 | row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
498 | 0 | ThrowBinaryException(CorruptImageError,"ImproperImageHeader", |
499 | 0 | image_info->filename); |
500 | 0 | } |
501 | 0 | } |
502 | 0 | } |
503 | 0 | row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
504 | 0 | if ((status != MagickFalse) && (clut_depth != 0)) |
505 | 0 | { |
506 | 0 | CSM |
507 | 0 | csm; |
508 | |
|
509 | 0 | size_t |
510 | 0 | clut_size; |
511 | |
|
512 | 0 | ssize_t |
513 | 0 | i; |
514 | |
|
515 | 0 | unsigned char |
516 | 0 | *clut_data; |
517 | | |
518 | | /* |
519 | | * ### Read CLUT Data ### |
520 | | */ |
521 | 0 | clut_size=MagickMax(header->clut_size,(size_t) (clut_depth/8)* |
522 | 0 | image->colors); |
523 | 0 | clut_data=(unsigned char *) AcquireQuantumMemory(clut_size, |
524 | 0 | sizeof(*clut_data)); |
525 | 0 | if (clut_data == (unsigned char *) NULL) |
526 | 0 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
527 | 0 | image_info->filename); |
528 | 0 | (void) memset(clut_data,0,clut_size); |
529 | 0 | count=ReadBlob(image,clut_size,clut_data); |
530 | 0 | if (count != (ssize_t) clut_size) |
531 | 0 | { |
532 | 0 | clut_data=(unsigned char *) RelinquishMagickMemory(clut_data); |
533 | 0 | ThrowBinaryException(CorruptImageError,"InsufficientImageDataInFile", |
534 | 0 | image_info->filename); |
535 | 0 | } |
536 | | /* |
537 | | * ### Process CLUT Data ### |
538 | | */ |
539 | 0 | p=clut_data; |
540 | 0 | switch(clut_depth) |
541 | 0 | { |
542 | 0 | case 16: |
543 | 0 | { |
544 | 0 | for (i=0; i < (ssize_t) image->colors; i++) |
545 | 0 | { |
546 | 0 | word = (unsigned int) (((unsigned short)* p )<<0*8 | |
547 | 0 | ((unsigned short)*(p+1))<<1*8); |
548 | |
|
549 | 0 | image->colormap[i].red=GetChannelValue(word,0,RGBA16); |
550 | 0 | image->colormap[i].green=GetChannelValue(word,1,RGBA16); |
551 | 0 | image->colormap[i].blue=GetChannelValue(word,2,RGBA16); |
552 | 0 | image->colormap[i].alpha=GetAlpha(word,RGBA16); |
553 | 0 | p+=(ptrdiff_t) 2; |
554 | 0 | } |
555 | 0 | break; |
556 | 0 | } |
557 | 0 | case 24: |
558 | 0 | { |
559 | 0 | for (i=0; i < (ssize_t) image->colors; i++) |
560 | 0 | { |
561 | 0 | word = ((unsigned int)* p )<<0*8 | |
562 | 0 | ((unsigned int)*(p+1))<<1*8 | |
563 | 0 | ((unsigned int)*(p+2))<<2*8; |
564 | |
|
565 | 0 | image->colormap[i].red=GetChannelValue(word,0,RGB24); |
566 | 0 | image->colormap[i].green=GetChannelValue(word,1,RGB24); |
567 | 0 | image->colormap[i].blue=GetChannelValue(word,2,RGB24); |
568 | 0 | p+=(ptrdiff_t) 3; |
569 | 0 | } |
570 | 0 | break; |
571 | 0 | } |
572 | 0 | case 32: |
573 | 0 | { |
574 | 0 | for (i=0; i < (ssize_t) image->colors; i++) |
575 | 0 | { |
576 | 0 | word = ((unsigned int)* p )<<0*8 | |
577 | 0 | ((unsigned int)*(p+1))<<1*8 | |
578 | 0 | ((unsigned int)*(p+2))<<2*8 | |
579 | 0 | ((unsigned int)*(p+3))<<3*8; |
580 | |
|
581 | 0 | image->colormap[i].red=GetChannelValue(word,0,RGBA32); |
582 | 0 | image->colormap[i].green=GetChannelValue(word,1,RGBA32); |
583 | 0 | image->colormap[i].blue=GetChannelValue(word,2,RGBA32); |
584 | 0 | image->colormap[i].alpha=GetAlpha(word,RGBA32); |
585 | 0 | p+=(ptrdiff_t) 4; |
586 | 0 | } |
587 | 0 | break; |
588 | 0 | } |
589 | 0 | } |
590 | 0 | clut_data=(unsigned char *) RelinquishMagickMemory(clut_data); |
591 | | /* CSM: CLUT Storage Mode */ |
592 | 0 | switch ((int) header->clut_type>>4) /* High 4 bits */ |
593 | 0 | { |
594 | 0 | case 0: |
595 | 0 | csm=CSM1; |
596 | 0 | break; |
597 | 0 | case 1: |
598 | 0 | csm=CSM2; |
599 | 0 | break; |
600 | 0 | default: |
601 | 0 | ThrowBinaryException(CorruptImageError,"ImproperImageHeader", |
602 | 0 | image_info->filename); |
603 | 0 | break; |
604 | 0 | } |
605 | 0 | if (csm == CSM1) |
606 | 0 | { |
607 | 0 | PixelInfo |
608 | 0 | *oldColormap; |
609 | |
|
610 | 0 | oldColormap=(PixelInfo *) AcquireQuantumMemory((size_t)(image->colors)+ |
611 | 0 | 1,sizeof(*image->colormap)); |
612 | 0 | if (oldColormap == (PixelInfo *) NULL) |
613 | 0 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
614 | 0 | image_info->filename); |
615 | 0 | deshufflePalette(image,oldColormap); |
616 | 0 | RelinquishMagickMemory(oldColormap); |
617 | 0 | } |
618 | 0 | } |
619 | 0 | return(status); |
620 | 0 | } |
621 | | |
622 | | static Image *ReadTIM2Image(const ImageInfo *image_info, |
623 | | ExceptionInfo *exception) |
624 | 366 | { |
625 | 366 | Image |
626 | 366 | *image; |
627 | | |
628 | 366 | MagickBooleanType |
629 | 366 | status; |
630 | | |
631 | 366 | ssize_t |
632 | 366 | i; |
633 | | |
634 | 366 | TIM2FileHeader |
635 | 366 | file_header; |
636 | | |
637 | | /* |
638 | | * Open image file. |
639 | | */ |
640 | 366 | assert(image_info != (const ImageInfo *) NULL); |
641 | 366 | assert(image_info->signature == MagickCoreSignature); |
642 | 366 | assert(exception != (ExceptionInfo *) NULL); |
643 | 366 | assert(exception->signature == MagickCoreSignature); |
644 | 366 | if (IsEventLogging() != MagickFalse) |
645 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", |
646 | 0 | image_info->filename); |
647 | 366 | image=AcquireImage(image_info,exception); |
648 | 366 | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
649 | 366 | if (status == MagickFalse) |
650 | 187 | { |
651 | 187 | image=DestroyImageList(image); |
652 | 187 | return((Image *) NULL); |
653 | 187 | } |
654 | 179 | file_header.magic_num=ReadBlobMSBLong(image); |
655 | 179 | if (file_header.magic_num != 0x54494D32) /* "TIM2" */ |
656 | 165 | ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
657 | 14 | file_header.format_vers=(unsigned char) ReadBlobByte(image); |
658 | 14 | if (file_header.format_vers != 0x04) |
659 | 13 | ThrowReaderException(CoderError,"ImageTypeNotSupported"); |
660 | 13 | file_header.format_type=(unsigned char) ReadBlobByte(image); |
661 | 13 | file_header.image_count=ReadBlobLSBShort(image); |
662 | 13 | if (DiscardBlobBytes(image,8) == MagickFalse) /* reserved */ |
663 | 11 | ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); |
664 | 11 | if ((file_header.format_type > 0) && |
665 | 11 | (DiscardBlobBytes(image,112) == MagickFalse)) |
666 | 9 | ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); |
667 | | /* |
668 | | * Process each image. Only one image supported for now |
669 | | */ |
670 | 9 | if (file_header.image_count != 1) |
671 | 7 | ThrowReaderException(CoderError,"NumberOfImagesIsNotSupported"); |
672 | 8 | for (i=0; i < (ssize_t) file_header.image_count; i++) |
673 | 7 | { |
674 | 7 | char |
675 | 7 | clut_depth, |
676 | 7 | bits_per_pixel; |
677 | | |
678 | 7 | TIM2ImageHeader |
679 | 7 | image_header; |
680 | | |
681 | 7 | if (i > 0) |
682 | 0 | { |
683 | | /* |
684 | | Proceed to next image. |
685 | | */ |
686 | 0 | if (image_info->number_scenes != 0) |
687 | 0 | if (image->scene >= (image_info->scene+image_info->number_scenes-1)) |
688 | 0 | break; |
689 | | /* |
690 | | Allocate next image structure. |
691 | | */ |
692 | 0 | AcquireNextImage(image_info,image,exception); |
693 | 0 | if (GetNextImageInList(image) == (Image *) NULL) |
694 | 0 | { |
695 | 0 | status=MagickFalse; |
696 | 0 | break; |
697 | 0 | } |
698 | 0 | image=SyncNextImageInList(image); |
699 | 0 | status=SetImageProgress(image,LoadImagesTag,(MagickOffsetType) |
700 | 0 | image->scene-1,image->scene); |
701 | 0 | if (status == MagickFalse) |
702 | 0 | break; |
703 | 0 | } |
704 | 7 | ReadTIM2ImageHeader(image,&image_header); |
705 | 7 | if (image_header.mipmap_count != 1) |
706 | 6 | ThrowReaderException(CoderError,"NumberOfImagesIsNotSupported"); |
707 | 6 | if (image_header.header_size < 48) |
708 | 5 | ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
709 | 5 | if ((MagickSizeType) image_header.image_size > GetBlobSize(image)) |
710 | 4 | ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); |
711 | 4 | if ((MagickSizeType) image_header.clut_size > GetBlobSize(image)) |
712 | 3 | ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); |
713 | 3 | image->columns=image_header.width; |
714 | 3 | image->rows=image_header.height; |
715 | 3 | clut_depth=0; |
716 | 3 | if (image_header.clut_type !=0) |
717 | 2 | { |
718 | 2 | switch((int) image_header.clut_type&0x0F) /* Low 4 bits */ |
719 | 2 | { |
720 | 0 | case 1: |
721 | 0 | clut_depth=16; |
722 | 0 | break; |
723 | 1 | case 2: |
724 | 1 | clut_depth=24; |
725 | 1 | break; |
726 | 0 | case 3: |
727 | 0 | clut_depth=32; |
728 | 0 | break; |
729 | 1 | default: |
730 | 1 | ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
731 | 0 | break; |
732 | 2 | } |
733 | 2 | } |
734 | 2 | switch ((int) image_header.bpp_type) |
735 | 2 | { |
736 | 0 | case 1: |
737 | 0 | bits_per_pixel=16; |
738 | 0 | break; |
739 | 1 | case 2: |
740 | 1 | bits_per_pixel=24; |
741 | 1 | break; |
742 | 0 | case 3: |
743 | 0 | bits_per_pixel=32; |
744 | 0 | break; |
745 | 0 | case 4: |
746 | 0 | bits_per_pixel=4; /* Implies CLUT */ |
747 | 0 | break; |
748 | 0 | case 5: |
749 | 0 | bits_per_pixel=8; /* Implies CLUT */ |
750 | 0 | break; |
751 | 1 | default: |
752 | 1 | ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
753 | 0 | break; |
754 | 2 | } |
755 | 1 | image->depth=(size_t) ((clut_depth != 0) ? clut_depth : bits_per_pixel); |
756 | 1 | if ((image->depth == 16) || (image->depth == 32)) |
757 | 0 | image->alpha_trait=BlendPixelTrait; |
758 | 1 | if (image->ping == MagickFalse) |
759 | 0 | { |
760 | 0 | status=ReadTIM2ImageData(image_info,image,&image_header,clut_depth, |
761 | 0 | bits_per_pixel,exception); |
762 | 0 | if (status==MagickFalse) |
763 | 0 | break; |
764 | 0 | } |
765 | 1 | if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0)) |
766 | 0 | if (image->scene >= (image_info->scene+image_info->number_scenes-1)) |
767 | 0 | break; |
768 | 1 | if ((image->storage_class == PseudoClass) && (EOFBlob(image) != MagickFalse)) |
769 | 0 | { |
770 | 0 | ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile", |
771 | 0 | image->filename); |
772 | 0 | break; |
773 | 0 | } |
774 | 1 | } |
775 | 1 | if (CloseBlob(image) == MagickFalse) |
776 | 0 | status=MagickFalse; |
777 | 1 | if (status == MagickFalse) |
778 | 0 | return(DestroyImageList(image)); |
779 | 1 | return(GetFirstImageInList(image)); |
780 | 1 | } |
781 | | |
782 | | /* |
783 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
784 | | % % |
785 | | % % |
786 | | % % |
787 | | % R e g i s t e r T I M 2 I m a g e % |
788 | | % % |
789 | | % % |
790 | | % % |
791 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
792 | | % |
793 | | % RegisterTIM2Image() adds attributes for the TIM2 image format to |
794 | | % the list of supported formats. The attributes include the image format |
795 | | % tag, a method to read and/or write the format, whether the format |
796 | | % supports the saving of more than one frame to the same file or blob, |
797 | | % whether the format supports native in-memory I/O, and a brief |
798 | | % description of the format. |
799 | | % |
800 | | % The format of the RegisterTIM2Image method is: |
801 | | % |
802 | | % size_t RegisterTIM2Image(void) |
803 | | % |
804 | | */ |
805 | | ModuleExport size_t RegisterTIM2Image(void) |
806 | 9 | { |
807 | 9 | MagickInfo |
808 | 9 | *entry; |
809 | | |
810 | 9 | entry=AcquireMagickInfo("TIM2","TM2","PS2 TIM2"); |
811 | 9 | entry->decoder=(DecodeImageHandler *) ReadTIM2Image; |
812 | 9 | (void) RegisterMagickInfo(entry); |
813 | 9 | return(MagickImageCoderSignature); |
814 | 9 | } |
815 | | |
816 | | /* |
817 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
818 | | % % |
819 | | % % |
820 | | % % |
821 | | % U n r e g i s t e r T I M 2 I m a g e % |
822 | | % % |
823 | | % % |
824 | | % % |
825 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
826 | | % |
827 | | % UnregisterTIM2Image() removes format registrations made by the |
828 | | % TIM2 module from the list of supported formats. |
829 | | % |
830 | | % The format of the UnregisterTIM2Image method is: |
831 | | % |
832 | | % UnregisterTIM2Image(void) |
833 | | % |
834 | | */ |
835 | | ModuleExport void UnregisterTIM2Image(void) |
836 | 0 | { |
837 | 0 | (void) UnregisterMagickInfo("TM2"); |
838 | 0 | } |