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