1 /*
2 % Copyright (C) 2003-2020 GraphicsMagick Group
3 % Copyright (C) 2002 ImageMagick Studio
4 % Copyright 1991-1999 E. I. du Pont de Nemours and Company
5 %
6 % This program is covered by multiple licenses, which are described in
7 % Copyright.txt. You should have received a copy of Copyright.txt with this
8 % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9 %
10 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11 % %
12 % %
13 % %
14 % TTTTT IIIII M M %
15 % T I MM MM %
16 % T I M M M %
17 % T I M M %
18 % T IIIII M M %
19 % %
20 % %
21 % Read PSX TIM Image Format. %
22 % %
23 % %
24 % Software Design %
25 % John Cristy %
26 % July 1992 %
27 % %
28 % %
29 % %
30 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31 %
32 %
33 */
34
35 /*
36 Include declarations.
37 */
38 #include "magick/studio.h"
39 #include "magick/blob.h"
40 #include "magick/colormap.h"
41 #include "magick/enum_strings.h"
42 #include "magick/log.h"
43 #include "magick/magick.h"
44 #include "magick/monitor.h"
45 #include "magick/pixel_cache.h"
46 #include "magick/utility.h"
47
48 /*
49 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50 % %
51 % %
52 % %
53 % R e a d T I M I m a g e %
54 % %
55 % %
56 % %
57 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
58 %
59 % Method ReadTIMImage reads a PSX TIM image file and returns it. It
60 % allocates the memory necessary for the new Image structure and returns a
61 % pointer to the new image.
62 %
63 % Contributed by os@scee.sony.co.uk.
64 %
65 % The format of the ReadTIMImage method is:
66 %
67 % Image *ReadTIMImage(const ImageInfo *image_info,ExceptionInfo *exception)
68 %
69 % A description of each parameter follows:
70 %
71 % o image: Method ReadTIMImage returns a pointer to the image after
72 % reading. A null image is returned if there is a memory shortage or
73 % if the image cannot be read.
74 %
75 % o image_info: Specifies a pointer to a ImageInfo structure.
76 %
77 % o exception: return any errors or warnings in this structure.
78 %
79 %
80 */
ReadTIMImage(const ImageInfo * image_info,ExceptionInfo * exception)81 static Image *ReadTIMImage(const ImageInfo *image_info,ExceptionInfo *exception)
82 {
83 typedef struct _TIMInfo
84 {
85 unsigned long
86 id,
87 flag;
88 } TIMInfo;
89
90 TIMInfo
91 tim_info;
92
93 Image
94 *image;
95
96 int
97 bits_per_pixel,
98 has_clut;
99
100 long
101 y;
102
103 register IndexPacket
104 *indexes;
105
106 register long
107 x;
108
109 register PixelPacket
110 *q;
111
112 register long
113 i;
114
115 register unsigned char
116 *p;
117
118 unsigned char
119 *tim_pixels;
120
121 unsigned short
122 word;
123
124 unsigned int
125 index,
126 status;
127
128 size_t
129 bytes_per_line,
130 image_size;
131
132 unsigned long
133 height,
134 pixel_mode,
135 width;
136
137 /*
138 Open image file.
139 */
140 assert(image_info != (const ImageInfo *) NULL);
141 assert(image_info->signature == MagickSignature);
142 assert(exception != (ExceptionInfo *) NULL);
143 assert(exception->signature == MagickSignature);
144 image=AllocateImage(image_info);
145 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
146 if (status == False)
147 ThrowReaderException(FileOpenError,UnableToOpenFile,image);
148 /*
149 Determine if this is a TIM file.
150 */
151 tim_info.id=ReadBlobLSBLong(image);
152 do
153 {
154 /*
155 Verify TIM identifier.
156 */
157 if (tim_info.id != 0x00000010)
158 ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
159 tim_info.flag=ReadBlobLSBLong(image);
160 has_clut=!!(tim_info.flag & (1 << 3));
161 pixel_mode=tim_info.flag & 0x07;
162 switch ((int) pixel_mode)
163 {
164 case 0: bits_per_pixel=4; break;
165 case 1: bits_per_pixel=8; break;
166 case 2: bits_per_pixel=16; break;
167 case 3: bits_per_pixel=24; break;
168 default: bits_per_pixel=4; break;
169 }
170 image->depth=8;
171 if (has_clut)
172 {
173 unsigned char
174 *tim_colormap;
175
176 /*
177 Read TIM raster colormap.
178 */
179 (void)ReadBlobLSBLong(image);
180 (void)ReadBlobLSBShort(image);
181 (void)ReadBlobLSBShort(image);
182 /* width= */ (void)ReadBlobLSBShort(image);
183 /* height= */ (void)ReadBlobLSBShort(image);
184 if (!AllocateImageColormap(image,pixel_mode == 1 ? 256 : 16))
185 ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
186 image);
187 tim_colormap=MagickAllocateResourceLimitedArray(unsigned char *,image->colors,2);
188 if (tim_colormap == (unsigned char *) NULL)
189 ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
190 image);
191 if (ReadBlob(image, (size_t)2*image->colors,(char *) tim_colormap) != (size_t)2*image->colors)
192 {
193 MagickFreeResourceLimitedMemory(tim_colormap);
194 ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
195 }
196 p=tim_colormap;
197 for (i=0; i < (long) image->colors; i++)
198 {
199 word=(*p++);
200 word|=(unsigned short) (*p++ << 8U);
201 image->colormap[i].blue=ScaleCharToQuantum(ScaleColor5to8((word >> 10U) & 0x1fU));
202 image->colormap[i].green=ScaleCharToQuantum(ScaleColor5to8((word >> 5U) & 0x1fU));
203 image->colormap[i].red=ScaleCharToQuantum(ScaleColor5to8(word & 0x1fU));
204 image->colormap[i].opacity=OpaqueOpacity;
205 }
206 MagickFreeResourceLimitedMemory(tim_colormap);
207 if (image->logging)
208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
209 "PSX-TIM read CLUT with %u entries",
210 image->colors);
211 }
212 if ((bits_per_pixel == 4) || (bits_per_pixel == 8))
213 {
214 if (image->storage_class != PseudoClass)
215 {
216 if (image->logging)
217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
218 "PSX-TIM %u bits/sample requires a CLUT!",
219 bits_per_pixel);
220 errno=0;
221 ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
222 }
223 }
224 else
225 {
226 if (image->storage_class == PseudoClass)
227 {
228 if (image->logging)
229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
230 "PSX-TIM %u bits/sample does not use"
231 " a CLUT, ignoring it",
232 bits_per_pixel);
233 image->storage_class=DirectClass;
234 }
235 }
236
237 /*
238 Read image data.
239 */
240 (void) ReadBlobLSBLong(image);
241 (void) ReadBlobLSBShort(image);
242 (void) ReadBlobLSBShort(image);
243 width=ReadBlobLSBShort(image);
244 height=ReadBlobLSBShort(image);
245 if (EOFBlob(image))
246 ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
247 image_size=MagickArraySize(2,MagickArraySize(width,height));
248 bytes_per_line=MagickArraySize(width,2);
249 width=(unsigned long)(MagickArraySize(width,16))/bits_per_pixel;
250 /*
251 Initialize image structure.
252 */
253 image->columns=width;
254 image->rows=height;
255
256 if (image->logging)
257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
258 "TIM[%lu] %lux%lu %d bits/pixel %s",
259 image->scene,
260 image->columns, image->rows,
261 bits_per_pixel,
262 ClassTypeToString(image->storage_class));
263
264 if (image_info->ping)
265 if ((image_info->subrange == 0) ||
266 ((image_info->subrange != 0) &&
267 (image->scene >= (image_info->subimage+image_info->subrange-1))))
268 break;
269
270 if (CheckImagePixelLimits(image, exception) != MagickPass)
271 ThrowReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
272
273 tim_pixels=MagickAllocateResourceLimitedMemory(unsigned char *,image_size);
274 if (tim_pixels == (unsigned char *) NULL)
275 ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
276 if (ReadBlob(image,image_size,(char *) tim_pixels) != image_size)
277 {
278 MagickFreeResourceLimitedMemory(tim_pixels);
279 ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
280 }
281
282 /*
283 Convert TIM raster image to pixel packets.
284 */
285 switch (bits_per_pixel)
286 {
287 case 4:
288 {
289 /*
290 Convert PseudoColor scanline.
291 */
292 for (y=(long) image->rows-1; y >= 0; y--)
293 {
294 q=SetImagePixelsEx(image,0,y,image->columns,1,exception);
295 if (q == (PixelPacket *) NULL)
296 break;
297 indexes=AccessMutableIndexes(image);
298 if (indexes == (IndexPacket *) NULL)
299 break;
300 p=tim_pixels+y*bytes_per_line;
301 for (x=0; x < ((long) image->columns-1); x+=2)
302 {
303 index=(*p) & 0xf;
304 VerifyColormapIndex(image,index);
305 indexes[x]=index;
306 index=(*p >> 4) & 0xf;
307 VerifyColormapIndex(image,index);
308 indexes[x+1]=index;
309 p++;
310 }
311 if ((image->columns % 2) != 0)
312 {
313 index=(*p >> 4) & 0xf;
314 VerifyColormapIndex(image,index);
315 indexes[x]=(*p >> 4) & 0xf;
316 p++;
317 }
318 if (!SyncImagePixelsEx(image,exception))
319 break;
320 if (QuantumTick(y,image->rows))
321 {
322 status=MagickMonitorFormatted((size_t)image->rows-y-1,image->rows,
323 exception,LoadImageText,
324 image->filename,
325 image->columns,image->rows);
326 if (status == False)
327 break;
328 }
329 }
330 break;
331 }
332 case 8:
333 {
334 /*
335 Convert PseudoColor scanline.
336 */
337 for (y=(long) image->rows-1; y >= 0; y--)
338 {
339 q=SetImagePixelsEx(image,0,y,image->columns,1,exception);
340 if (q == (PixelPacket *) NULL)
341 break;
342 indexes=AccessMutableIndexes(image);
343 if (indexes == (IndexPacket *) NULL)
344 break;
345 p=tim_pixels+y*bytes_per_line;
346 for (x=0; x < (long) image->columns; x++)
347 {
348 index=(*p++);
349 VerifyColormapIndex(image,index);
350 indexes[x]=index;
351 }
352 if (!SyncImagePixelsEx(image,exception))
353 break;
354 if (QuantumTick(y,image->rows))
355 {
356 status=MagickMonitorFormatted((size_t)image->rows-y-1,image->rows,
357 exception,LoadImageText,
358 image->filename,
359 image->columns,image->rows);
360 if (status == False)
361 break;
362 }
363 }
364 break;
365 }
366 case 16:
367 {
368 /*
369 Convert DirectColor scanline.
370 */
371 for (y=(long) image->rows-1; y >= 0; y--)
372 {
373 PixelPacket *t;
374 p=tim_pixels+y*bytes_per_line;
375 q=SetImagePixelsEx(image,0,y,image->columns,1,exception);
376 if (q == (PixelPacket *) NULL)
377 break;
378 t=q;
379 for (x=0; x < (long) image->columns; x++)
380 {
381 word=(*p++);
382 word|=(*p++ << 8);
383 q->blue=ScaleCharToQuantum(ScaleColor5to8((word >> 10) & 0x1f));
384 q->green=ScaleCharToQuantum(ScaleColor5to8((word >> 5) & 0x1f));
385 q->red=ScaleCharToQuantum(ScaleColor5to8(word & 0x1f));
386 q->opacity=OpaqueOpacity;
387 q++;
388 }
389 memset(t,0,image->columns*sizeof(PixelPacket));
390 if (!SyncImagePixelsEx(image,exception))
391 break;
392 if (QuantumTick(y,image->rows))
393 {
394 status=MagickMonitorFormatted((size_t)image->rows-y-1,image->rows,
395 exception,LoadImageText,
396 image->filename,
397 image->columns,image->rows);
398 if (status == False)
399 break;
400 }
401 }
402 break;
403 }
404 case 24:
405 {
406 /*
407 Convert DirectColor scanline.
408 */
409 for (y=(long) image->rows-1; y >= 0; y--)
410 {
411 p=tim_pixels+y*bytes_per_line;
412 q=SetImagePixelsEx(image,0,y,image->columns,1,exception);
413 if (q == (PixelPacket *) NULL)
414 break;
415 for (x=0; x < (long) image->columns; x++)
416 {
417 q->red=ScaleCharToQuantum(*p++);
418 q->green=ScaleCharToQuantum(*p++);
419 q->blue=ScaleCharToQuantum(*p++);
420 q->opacity=OpaqueOpacity;
421 q++;
422 }
423 if (!SyncImagePixelsEx(image,exception))
424 break;
425 if (QuantumTick(y,image->rows))
426 {
427 status=MagickMonitorFormatted((size_t)image->rows-y-1,image->rows,
428 exception,LoadImageText,
429 image->filename,
430 image->columns,image->rows);
431 if (status == False)
432 break;
433 }
434 }
435 break;
436 }
437 default:
438 {
439 MagickFreeResourceLimitedMemory(tim_pixels);
440 ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
441 }
442 }
443 if (image->storage_class == PseudoClass)
444 (void) SyncImage(image);
445 MagickFreeResourceLimitedMemory(tim_pixels);
446 if (EOFBlob(image))
447 {
448 ThrowException(exception,CorruptImageError,UnexpectedEndOfFile,
449 image->filename);
450 break;
451 }
452 StopTimer(&image->timer);
453 /*
454 Proceed to next image.
455 */
456 if (image_info->subrange != 0)
457 if (image->scene >= (image_info->subimage+image_info->subrange-1))
458 break;
459
460 tim_info.id=ReadBlobLSBLong(image);
461 if (tim_info.id == 0x00000010)
462 {
463 /*
464 Allocate next image structure.
465 */
466 AllocateNextImage(image_info,image);
467 if (image->next == (Image *) NULL)
468 {
469 DestroyImageList(image);
470 return((Image *) NULL);
471 }
472 image=SyncNextImageInList(image);
473 status=MagickMonitorFormatted(TellBlob(image),GetBlobSize(image),
474 exception,LoadImagesText,
475 image->filename);
476 if (status == False)
477 break;
478 }
479 } while (tim_info.id == 0x00000010);
480 while (image->previous != (Image *) NULL)
481 image=image->previous;
482 CloseBlob(image);
483 return(image);
484 }
485
486 /*
487 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
488 % %
489 % %
490 % %
491 % R e g i s t e r T I M I m a g e %
492 % %
493 % %
494 % %
495 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
496 %
497 % Method RegisterTIMImage adds attributes for the TIM image format to
498 % the list of supported formats. The attributes include the image format
499 % tag, a method to read and/or write the format, whether the format
500 % supports the saving of more than one frame to the same file or blob,
501 % whether the format supports native in-memory I/O, and a brief
502 % description of the format.
503 %
504 % The format of the RegisterTIMImage method is:
505 %
506 % RegisterTIMImage(void)
507 %
508 */
RegisterTIMImage(void)509 ModuleExport void RegisterTIMImage(void)
510 {
511 MagickInfo
512 *entry;
513
514 entry=SetMagickInfo("TIM");
515 entry->decoder=(DecoderHandler) ReadTIMImage;
516 entry->description="PSX TIM";
517 entry->module="TIM";
518 (void) RegisterMagickInfo(entry);
519 }
520
521 /*
522 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
523 % %
524 % %
525 % %
526 % U n r e g i s t e r T I M I m a g e %
527 % %
528 % %
529 % %
530 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
531 %
532 % Method UnregisterTIMImage removes format registrations made by the
533 % TIM module from the list of supported formats.
534 %
535 % The format of the UnregisterTIMImage method is:
536 %
537 % UnregisterTIMImage(void)
538 %
539 */
UnregisterTIMImage(void)540 ModuleExport void UnregisterTIMImage(void)
541 {
542 (void) UnregisterMagickInfo("TIM");
543 }
544