1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                            TTTTT  IIIII  M   M                              %
7 %                              T      I    MM MM                              %
8 %                              T      I    M M M                              %
9 %                              T      I    M   M                              %
10 %                              T    IIIII  M   M                              %
11 %                                                                             %
12 %                                                                             %
13 %                           Read PSX TIM Image Format                         %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2021 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   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/blob.h"
44 #include "MagickCore/blob-private.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/colormap.h"
47 #include "MagickCore/exception.h"
48 #include "MagickCore/exception-private.h"
49 #include "MagickCore/image.h"
50 #include "MagickCore/image-private.h"
51 #include "MagickCore/list.h"
52 #include "MagickCore/magick.h"
53 #include "MagickCore/memory_.h"
54 #include "MagickCore/monitor.h"
55 #include "MagickCore/monitor-private.h"
56 #include "MagickCore/pixel-accessor.h"
57 #include "MagickCore/quantum-private.h"
58 #include "MagickCore/static.h"
59 #include "MagickCore/string_.h"
60 #include "MagickCore/module.h"
61 
62 /*
63 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
64 %                                                                             %
65 %                                                                             %
66 %                                                                             %
67 %  R e a d T I M I m a g e                                                    %
68 %                                                                             %
69 %                                                                             %
70 %                                                                             %
71 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
72 %
73 %  ReadTIMImage() reads a PSX TIM image file and returns it.  It
74 %  allocates the memory necessary for the new Image structure and returns a
75 %  pointer to the new image.
76 %
77 %  Contributed by os@scee.sony.co.uk.
78 %
79 %  The format of the ReadTIMImage method is:
80 %
81 %      Image *ReadTIMImage(const ImageInfo *image_info,ExceptionInfo *exception)
82 %
83 %  A description of each parameter follows:
84 %
85 %    o image_info: the image info.
86 %
87 %    o exception: return any errors or warnings in this structure.
88 %
89 */
ReadTIMImage(const ImageInfo * image_info,ExceptionInfo * exception)90 static Image *ReadTIMImage(const ImageInfo *image_info,ExceptionInfo *exception)
91 {
92   typedef struct _TIMInfo
93   {
94     size_t
95       id,
96       flag;
97   } TIMInfo;
98 
99   TIMInfo
100     tim_info;
101 
102   Image
103     *image;
104 
105   int
106     bits_per_pixel,
107     has_clut;
108 
109   MagickBooleanType
110     status;
111 
112   ssize_t
113     x;
114 
115   Quantum
116     *q;
117 
118   ssize_t
119     i;
120 
121   unsigned char
122     *p;
123 
124   size_t
125     bytes_per_line,
126     height,
127     image_size,
128     pixel_mode,
129     width;
130 
131   ssize_t
132     count,
133     y;
134 
135   unsigned char
136     *tim_pixels;
137 
138   unsigned short
139     word;
140 
141   /*
142     Open image file.
143   */
144   assert(image_info != (const ImageInfo *) NULL);
145   assert(image_info->signature == MagickCoreSignature);
146   if (image_info->debug != MagickFalse)
147     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
148       image_info->filename);
149   assert(exception != (ExceptionInfo *) NULL);
150   assert(exception->signature == MagickCoreSignature);
151   image=AcquireImage(image_info,exception);
152   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
153   if (status == MagickFalse)
154     {
155       image=DestroyImageList(image);
156       return((Image *) NULL);
157     }
158   /*
159     Determine if this a TIM file.
160   */
161   tim_info.id=ReadBlobLSBLong(image);
162   do
163   {
164     /*
165       Verify TIM identifier.
166     */
167     if (tim_info.id != 0x00000010)
168       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
169     tim_info.flag=ReadBlobLSBLong(image);
170     has_clut=tim_info.flag & (1 << 3) ? 1 : 0;
171     pixel_mode=tim_info.flag & 0x07;
172     switch ((int) pixel_mode)
173     {
174       case 0: bits_per_pixel=4; break;
175       case 1: bits_per_pixel=8; break;
176       case 2: bits_per_pixel=16; break;
177       case 3: bits_per_pixel=24; break;
178       default: bits_per_pixel=4; break;
179     }
180     image->depth=8;
181     if (has_clut)
182       {
183         unsigned char
184           *tim_colormap;
185 
186         /*
187           Read TIM raster colormap.
188         */
189         (void)ReadBlobLSBLong(image);
190         (void)ReadBlobLSBShort(image);
191         (void)ReadBlobLSBShort(image);
192         width=ReadBlobLSBShort(image);
193         height=ReadBlobLSBShort(image);
194         image->columns=width;
195         image->rows=height;
196         if (AcquireImageColormap(image,pixel_mode == 1 ? 256UL : 16UL,exception) == MagickFalse)
197           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
198         tim_colormap=(unsigned char *) AcquireQuantumMemory(image->colors,
199           2UL*sizeof(*tim_colormap));
200         if (tim_colormap == (unsigned char *) NULL)
201           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
202         count=ReadBlob(image,2*image->colors,tim_colormap);
203         if (count != (ssize_t) (2*image->colors))
204           {
205             tim_colormap=(unsigned char *) RelinquishMagickMemory(tim_colormap);
206             ThrowReaderException(CorruptImageError,
207               "InsufficientImageDataInFile");
208           }
209         p=tim_colormap;
210         for (i=0; i < (ssize_t) image->colors; i++)
211         {
212           word=(*p++);
213           word|=(unsigned short) (*p++ << 8);
214           image->colormap[i].blue=ScaleCharToQuantum(
215             ScaleColor5to8(1UL*(word >> 10) & 0x1f));
216           image->colormap[i].green=ScaleCharToQuantum(
217             ScaleColor5to8(1UL*(word >> 5) & 0x1f));
218           image->colormap[i].red=ScaleCharToQuantum(
219             ScaleColor5to8(1UL*word & 0x1f));
220         }
221         tim_colormap=(unsigned char *) RelinquishMagickMemory(tim_colormap);
222       }
223     if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
224       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
225         break;
226     /*
227       Read image data.
228     */
229     (void) ReadBlobLSBLong(image);
230     (void) ReadBlobLSBShort(image);
231     (void) ReadBlobLSBShort(image);
232     width=ReadBlobLSBShort(image);
233     height=ReadBlobLSBShort(image);
234     image_size=2*width*height;
235     if (image_size > GetBlobSize(image))
236       ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
237     bytes_per_line=width*2;
238     width=(width*16)/bits_per_pixel;
239     image->columns=width;
240     image->rows=height;
241     status=SetImageExtent(image,image->columns,image->rows,exception);
242     if (status == MagickFalse)
243       return(DestroyImageList(image));
244     status=ResetImagePixels(image,exception);
245     if (status == MagickFalse)
246       return(DestroyImageList(image));
247     tim_pixels=(unsigned char *) AcquireQuantumMemory(image_size,
248       sizeof(*tim_pixels));
249     if (tim_pixels == (unsigned char *) NULL)
250       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
251     count=ReadBlob(image,image_size,tim_pixels);
252     if (count != (ssize_t) (image_size))
253       {
254         tim_pixels=(unsigned char *) RelinquishMagickMemory(tim_pixels);
255         ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
256       }
257     /*
258       Convert TIM raster image to pixel packets.
259     */
260     switch (bits_per_pixel)
261     {
262       case 4:
263       {
264         /*
265           Convert PseudoColor scanline.
266         */
267         for (y=(ssize_t) image->rows-1; y >= 0; y--)
268         {
269           q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
270           if (q == (Quantum *) NULL)
271             break;
272           p=tim_pixels+y*bytes_per_line;
273           for (x=0; x < ((ssize_t) image->columns-1); x+=2)
274           {
275             SetPixelIndex(image,(*p) & 0x0f,q);
276             q+=GetPixelChannels(image);
277             SetPixelIndex(image,(*p >> 4) & 0x0f,q);
278             p++;
279             q+=GetPixelChannels(image);
280           }
281           if ((image->columns % 2) != 0)
282             {
283               SetPixelIndex(image,(*p >> 4) & 0x0f,q);
284               p++;
285               q+=GetPixelChannels(image);
286             }
287           if (SyncAuthenticPixels(image,exception) == MagickFalse)
288             break;
289           if (image->previous == (Image *) NULL)
290             {
291               status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
292                 image->rows);
293               if (status == MagickFalse)
294                 break;
295             }
296         }
297         break;
298       }
299       case 8:
300       {
301         /*
302           Convert PseudoColor scanline.
303         */
304         for (y=(ssize_t) image->rows-1; y >= 0; y--)
305         {
306           q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
307           if (q == (Quantum *) NULL)
308             break;
309           p=tim_pixels+y*bytes_per_line;
310           for (x=0; x < (ssize_t) image->columns; x++)
311           {
312             SetPixelIndex(image,*p++,q);
313             q+=GetPixelChannels(image);
314           }
315           if (SyncAuthenticPixels(image,exception) == MagickFalse)
316             break;
317           if (image->previous == (Image *) NULL)
318             {
319               status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
320                 image->rows);
321               if (status == MagickFalse)
322                 break;
323             }
324         }
325         break;
326       }
327       case 16:
328       {
329         /*
330           Convert DirectColor scanline.
331         */
332         for (y=(ssize_t) image->rows-1; y >= 0; y--)
333         {
334           p=tim_pixels+y*bytes_per_line;
335           q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
336           if (q == (Quantum *) NULL)
337             break;
338           for (x=0; x < (ssize_t) image->columns; x++)
339           {
340             word=(*p++);
341             word|=(*p++ << 8);
342             SetPixelBlue(image,ScaleCharToQuantum(ScaleColor5to8(
343               (1UL*word >> 10) & 0x1f)),q);
344             SetPixelGreen(image,ScaleCharToQuantum(ScaleColor5to8(
345               (1UL*word >> 5) & 0x1f)),q);
346             SetPixelRed(image,ScaleCharToQuantum(ScaleColor5to8(
347               (1UL*word >> 0) & 0x1f)),q);
348             q+=GetPixelChannels(image);
349           }
350           if (SyncAuthenticPixels(image,exception) == MagickFalse)
351             break;
352           if (image->previous == (Image *) NULL)
353             {
354               status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
355                 image->rows);
356               if (status == MagickFalse)
357                 break;
358             }
359         }
360         break;
361       }
362       case 24:
363       {
364         /*
365           Convert DirectColor scanline.
366         */
367         for (y=(ssize_t) image->rows-1; y >= 0; y--)
368         {
369           p=tim_pixels+y*bytes_per_line;
370           q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
371           if (q == (Quantum *) NULL)
372             break;
373           for (x=0; x < (ssize_t) image->columns; x++)
374           {
375             SetPixelRed(image,ScaleCharToQuantum(*p++),q);
376             SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
377             SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
378             q+=GetPixelChannels(image);
379           }
380           if (SyncAuthenticPixels(image,exception) == MagickFalse)
381             break;
382           if (image->previous == (Image *) NULL)
383             {
384               status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
385                 image->rows);
386               if (status == MagickFalse)
387                 break;
388             }
389         }
390         break;
391       }
392       default:
393       {
394         tim_pixels=(unsigned char *) RelinquishMagickMemory(tim_pixels);
395         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
396       }
397     }
398     if (image->storage_class == PseudoClass)
399       (void) SyncImage(image,exception);
400     tim_pixels=(unsigned char *) RelinquishMagickMemory(tim_pixels);
401     if (EOFBlob(image) != MagickFalse)
402       {
403         ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
404           image->filename);
405         break;
406       }
407     /*
408       Proceed to next image.
409     */
410     if (image_info->number_scenes != 0)
411       if (image->scene >= (image_info->scene+image_info->number_scenes-1))
412         break;
413     tim_info.id=ReadBlobLSBLong(image);
414     if (tim_info.id == 0x00000010)
415       {
416         /*
417           Allocate next image structure.
418         */
419         AcquireNextImage(image_info,image,exception);
420         if (GetNextImageInList(image) == (Image *) NULL)
421           {
422             status=MagickFalse;
423             break;
424           }
425         image=SyncNextImageInList(image);
426         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
427           GetBlobSize(image));
428         if (status == MagickFalse)
429           break;
430       }
431   } while (tim_info.id == 0x00000010);
432   (void) CloseBlob(image);
433   if (status == MagickFalse)
434     return(DestroyImageList(image));
435   return(GetFirstImageInList(image));
436 }
437 
438 /*
439 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
440 %                                                                             %
441 %                                                                             %
442 %                                                                             %
443 %   R e g i s t e r T I M I m a g e                                           %
444 %                                                                             %
445 %                                                                             %
446 %                                                                             %
447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
448 %
449 %  RegisterTIMImage() adds attributes for the TIM image format to
450 %  the list of supported formats.  The attributes include the image format
451 %  tag, a method to read and/or write the format, whether the format
452 %  supports the saving of more than one frame to the same file or blob,
453 %  whether the format supports native in-memory I/O, and a brief
454 %  description of the format.
455 %
456 %  The format of the RegisterTIMImage method is:
457 %
458 %      size_t RegisterTIMImage(void)
459 %
460 */
RegisterTIMImage(void)461 ModuleExport size_t RegisterTIMImage(void)
462 {
463   MagickInfo
464     *entry;
465 
466   entry=AcquireMagickInfo("TIM","TIM","PSX TIM");
467   entry->decoder=(DecodeImageHandler *) ReadTIMImage;
468   (void) RegisterMagickInfo(entry);
469   return(MagickImageCoderSignature);
470 }
471 
472 /*
473 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
474 %                                                                             %
475 %                                                                             %
476 %                                                                             %
477 %   U n r e g i s t e r T I M I m a g e                                       %
478 %                                                                             %
479 %                                                                             %
480 %                                                                             %
481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
482 %
483 %  UnregisterTIMImage() removes format registrations made by the
484 %  TIM module from the list of supported formats.
485 %
486 %  The format of the UnregisterTIMImage method is:
487 %
488 %      UnregisterTIMImage(void)
489 %
490 */
UnregisterTIMImage(void)491 ModuleExport void UnregisterTIMImage(void)
492 {
493   (void) UnregisterMagickInfo("TIM");
494 }
495