Coverage Report

Created: 2026-07-20 07:19

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