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