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-2016 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%    http://www.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*/
90static 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  register ssize_t
113    x;
114
115  register Quantum
116    *q;
117
118  register ssize_t
119    i;
120
121  register 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_data,
137    *tim_pixels;
138
139  unsigned short
140    word;
141
142  /*
143    Open image file.
144  */
145  assert(image_info != (const ImageInfo *) NULL);
146  assert(image_info->signature == MagickCoreSignature);
147  if (image_info->debug != MagickFalse)
148    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
149      image_info->filename);
150  assert(exception != (ExceptionInfo *) NULL);
151  assert(exception->signature == MagickCoreSignature);
152  image=AcquireImage(image_info,exception);
153  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
154  if (status == MagickFalse)
155    {
156      image=DestroyImageList(image);
157      return((Image *) NULL);
158    }
159  /*
160    Determine if this a TIM file.
161  */
162  tim_info.id=ReadBlobLSBLong(image);
163  do
164  {
165    /*
166      Verify TIM identifier.
167    */
168    if (tim_info.id != 0x00000010)
169      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
170    tim_info.flag=ReadBlobLSBLong(image);
171    has_clut=tim_info.flag & (1 << 3) ? 1 : 0;
172    pixel_mode=tim_info.flag & 0x07;
173    switch ((int) pixel_mode)
174    {
175      case 0: bits_per_pixel=4; break;
176      case 1: bits_per_pixel=8; break;
177      case 2: bits_per_pixel=16; break;
178      case 3: bits_per_pixel=24; break;
179      default: bits_per_pixel=4; break;
180    }
181    image->depth=8;
182    if (has_clut)
183      {
184        unsigned char
185          *tim_colormap;
186
187        /*
188          Read TIM raster colormap.
189        */
190        (void)ReadBlobLSBLong(image);
191        (void)ReadBlobLSBShort(image);
192        (void)ReadBlobLSBShort(image);
193        width=ReadBlobLSBShort(image);
194        height=ReadBlobLSBShort(image);
195        image->columns=width;
196        image->rows=height;
197        if (AcquireImageColormap(image,pixel_mode == 1 ? 256UL : 16UL,exception) == MagickFalse)
198          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
199        tim_colormap=(unsigned char *) AcquireQuantumMemory(image->colors,
200          2UL*sizeof(*tim_colormap));
201        if (tim_colormap == (unsigned char *) NULL)
202          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
203        count=ReadBlob(image,2*image->colors,tim_colormap);
204        if (count != (ssize_t) (2*image->colors))
205          ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
206        p=tim_colormap;
207        for (i=0; i < (ssize_t) image->colors; i++)
208        {
209          word=(*p++);
210          word|=(unsigned short) (*p++ << 8);
211          image->colormap[i].blue=ScaleCharToQuantum(
212            ScaleColor5to8(1UL*(word >> 10) & 0x1f));
213          image->colormap[i].green=ScaleCharToQuantum(
214            ScaleColor5to8(1UL*(word >> 5) & 0x1f));
215          image->colormap[i].red=ScaleCharToQuantum(
216            ScaleColor5to8(1UL*word & 0x1f));
217        }
218        tim_colormap=(unsigned char *) RelinquishMagickMemory(tim_colormap);
219      }
220    if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
221      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
222        break;
223    status=SetImageExtent(image,image->columns,image->rows,exception);
224    if (status == MagickFalse)
225      return(DestroyImageList(image));
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    bytes_per_line=width*2;
236    width=(width*16)/bits_per_pixel;
237    tim_data=(unsigned char *) AcquireQuantumMemory(image_size,
238      sizeof(*tim_data));
239    if (tim_data == (unsigned char *) NULL)
240      ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
241    count=ReadBlob(image,image_size,tim_data);
242    if (count != (ssize_t) (image_size))
243      ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
244    tim_pixels=tim_data;
245    /*
246      Initialize image structure.
247    */
248    image->columns=width;
249    image->rows=height;
250    /*
251      Convert TIM raster image to pixel packets.
252    */
253    switch (bits_per_pixel)
254    {
255      case 4:
256      {
257        /*
258          Convert PseudoColor scanline.
259        */
260        for (y=(ssize_t) image->rows-1; y >= 0; y--)
261        {
262          q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
263          if (q == (Quantum *) NULL)
264            break;
265          p=tim_pixels+y*bytes_per_line;
266          for (x=0; x < ((ssize_t) image->columns-1); x+=2)
267          {
268            SetPixelIndex(image,(*p) & 0x0f,q);
269            q+=GetPixelChannels(image);
270            SetPixelIndex(image,(*p >> 4) & 0x0f,q);
271            p++;
272            q+=GetPixelChannels(image);
273          }
274          if ((image->columns % 2) != 0)
275            {
276              SetPixelIndex(image,(*p >> 4) & 0x0f,q);
277              p++;
278              q+=GetPixelChannels(image);
279            }
280          if (SyncAuthenticPixels(image,exception) == MagickFalse)
281            break;
282          if (image->previous == (Image *) NULL)
283            {
284              status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
285                image->rows);
286              if (status == MagickFalse)
287                break;
288            }
289        }
290        break;
291      }
292      case 8:
293      {
294        /*
295          Convert PseudoColor scanline.
296        */
297        for (y=(ssize_t) image->rows-1; y >= 0; y--)
298        {
299          q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
300          if (q == (Quantum *) NULL)
301            break;
302          p=tim_pixels+y*bytes_per_line;
303          for (x=0; x < (ssize_t) image->columns; x++)
304          {
305            SetPixelIndex(image,*p++,q);
306            q+=GetPixelChannels(image);
307          }
308          if (SyncAuthenticPixels(image,exception) == MagickFalse)
309            break;
310          if (image->previous == (Image *) NULL)
311            {
312              status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
313                image->rows);
314              if (status == MagickFalse)
315                break;
316            }
317        }
318        break;
319      }
320      case 16:
321      {
322        /*
323          Convert DirectColor scanline.
324        */
325        for (y=(ssize_t) image->rows-1; y >= 0; y--)
326        {
327          p=tim_pixels+y*bytes_per_line;
328          q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
329          if (q == (Quantum *) NULL)
330            break;
331          for (x=0; x < (ssize_t) image->columns; x++)
332          {
333            word=(*p++);
334            word|=(*p++ << 8);
335            SetPixelBlue(image,ScaleCharToQuantum(ScaleColor5to8(
336              (1UL*word >> 10) & 0x1f)),q);
337            SetPixelGreen(image,ScaleCharToQuantum(ScaleColor5to8(
338              (1UL*word >> 5) & 0x1f)),q);
339            SetPixelRed(image,ScaleCharToQuantum(ScaleColor5to8(
340              (1UL*word >> 0) & 0x1f)),q);
341            q+=GetPixelChannels(image);
342          }
343          if (SyncAuthenticPixels(image,exception) == MagickFalse)
344            break;
345          if (image->previous == (Image *) NULL)
346            {
347              status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
348                image->rows);
349              if (status == MagickFalse)
350                break;
351            }
352        }
353        break;
354      }
355      case 24:
356      {
357        /*
358          Convert DirectColor scanline.
359        */
360        for (y=(ssize_t) image->rows-1; y >= 0; y--)
361        {
362          p=tim_pixels+y*bytes_per_line;
363          q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
364          if (q == (Quantum *) NULL)
365            break;
366          for (x=0; x < (ssize_t) image->columns; x++)
367          {
368            SetPixelRed(image,ScaleCharToQuantum(*p++),q);
369            SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
370            SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
371            q+=GetPixelChannels(image);
372          }
373          if (SyncAuthenticPixels(image,exception) == MagickFalse)
374            break;
375          if (image->previous == (Image *) NULL)
376            {
377              status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
378                image->rows);
379              if (status == MagickFalse)
380                break;
381            }
382        }
383        break;
384      }
385      default:
386        ThrowReaderException(CorruptImageError,"ImproperImageHeader");
387    }
388    if (image->storage_class == PseudoClass)
389      (void) SyncImage(image,exception);
390    tim_pixels=(unsigned char *) RelinquishMagickMemory(tim_pixels);
391    if (EOFBlob(image) != MagickFalse)
392      {
393        ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
394          image->filename);
395        break;
396      }
397    /*
398      Proceed to next image.
399    */
400    tim_info.id=ReadBlobLSBLong(image);
401    if (tim_info.id == 0x00000010)
402      {
403        /*
404          Allocate next image structure.
405        */
406        AcquireNextImage(image_info,image,exception);
407        if (GetNextImageInList(image) == (Image *) NULL)
408          {
409            image=DestroyImageList(image);
410            return((Image *) NULL);
411          }
412        image=SyncNextImageInList(image);
413        status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
414          GetBlobSize(image));
415        if (status == MagickFalse)
416          break;
417      }
418  } while (tim_info.id == 0x00000010);
419  (void) CloseBlob(image);
420  return(GetFirstImageInList(image));
421}
422
423/*
424%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
425%                                                                             %
426%                                                                             %
427%                                                                             %
428%   R e g i s t e r T I M I m a g e                                           %
429%                                                                             %
430%                                                                             %
431%                                                                             %
432%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
433%
434%  RegisterTIMImage() adds attributes for the TIM image format to
435%  the list of supported formats.  The attributes include the image format
436%  tag, a method to read and/or write the format, whether the format
437%  supports the saving of more than one frame to the same file or blob,
438%  whether the format supports native in-memory I/O, and a brief
439%  description of the format.
440%
441%  The format of the RegisterTIMImage method is:
442%
443%      size_t RegisterTIMImage(void)
444%
445*/
446ModuleExport size_t RegisterTIMImage(void)
447{
448  MagickInfo
449    *entry;
450
451  entry=AcquireMagickInfo("TIM","TIM","PSX TIM");
452  entry->decoder=(DecodeImageHandler *) ReadTIMImage;
453  (void) RegisterMagickInfo(entry);
454  return(MagickImageCoderSignature);
455}
456
457/*
458%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
459%                                                                             %
460%                                                                             %
461%                                                                             %
462%   U n r e g i s t e r T I M I m a g e                                       %
463%                                                                             %
464%                                                                             %
465%                                                                             %
466%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
467%
468%  UnregisterTIMImage() removes format registrations made by the
469%  TIM module from the list of supported formats.
470%
471%  The format of the UnregisterTIMImage method is:
472%
473%      UnregisterTIMImage(void)
474%
475*/
476ModuleExport void UnregisterTIMImage(void)
477{
478  (void) UnregisterMagickInfo("TIM");
479}
480