blob: 9a281b3b055a4d9aad3a81e353fbd90d113b33ef [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% July 1992 %
18% %
19% %
Cristyd8420112021-01-01 14:52:00 -050020% Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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% %
Cristy83d74de2018-10-13 10:17:25 -040026% https://imagemagick.org/script/license.php %
cristy3ed852e2009-09-05 21:47:34 +000027% %
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*/
cristy4c08aed2011-07-01 19:47:50 +000042#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"
cristy3ed852e2009-09-05 21:47:34 +000061
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 {
cristybb503372010-05-27 20:51:26 +000094 size_t
cristy3ed852e2009-09-05 21:47:34 +000095 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
cristy3ed852e2009-09-05 21:47:34 +0000109 MagickBooleanType
110 status;
111
Cristyf2dc1dd2020-12-28 13:59:26 -0500112 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000113 x;
114
Cristyf2dc1dd2020-12-28 13:59:26 -0500115 Quantum
cristy3ed852e2009-09-05 21:47:34 +0000116 *q;
117
Cristyf2dc1dd2020-12-28 13:59:26 -0500118 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000119 i;
120
Cristyf2dc1dd2020-12-28 13:59:26 -0500121 unsigned char
cristy3ed852e2009-09-05 21:47:34 +0000122 *p;
123
cristyc6da28e2011-04-28 01:41:35 +0000124 size_t
125 bytes_per_line,
126 height,
127 image_size,
128 pixel_mode,
129 width;
130
cristy3ed852e2009-09-05 21:47:34 +0000131 ssize_t
cristyc6da28e2011-04-28 01:41:35 +0000132 count,
133 y;
cristy3ed852e2009-09-05 21:47:34 +0000134
135 unsigned char
Cristyd1eccc82017-10-01 09:34:57 -0400136 *tim_pixels;
cristy3ed852e2009-09-05 21:47:34 +0000137
138 unsigned short
139 word;
140
cristy3ed852e2009-09-05 21:47:34 +0000141 /*
142 Open image file.
143 */
144 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000145 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000146 if (image_info->debug != MagickFalse)
147 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
148 image_info->filename);
149 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000150 assert(exception->signature == MagickCoreSignature);
cristy9950d572011-10-01 18:22:35 +0000151 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000152 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 }
cristy7a0fa422013-09-09 13:09:21 +0000180 image->depth=8;
cristy3ed852e2009-09-05 21:47:34 +0000181 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;
cristy018f07f2011-09-04 21:15:19 +0000196 if (AcquireImageColormap(image,pixel_mode == 1 ? 256UL : 16UL,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000197 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))
Cristyad350cb2017-10-02 07:31:48 -0400204 {
205 tim_colormap=(unsigned char *) RelinquishMagickMemory(tim_colormap);
206 ThrowReaderException(CorruptImageError,
207 "InsufficientImageDataInFile");
208 }
cristy3ed852e2009-09-05 21:47:34 +0000209 p=tim_colormap;
cristybb503372010-05-27 20:51:26 +0000210 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000211 {
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;
Cristy090906c2018-02-04 15:49:44 -0500235 if (image_size > GetBlobSize(image))
236 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
cristy3ed852e2009-09-05 21:47:34 +0000237 bytes_per_line=width*2;
238 width=(width*16)/bits_per_pixel;
Cristy20668102018-02-04 15:54:36 -0500239 image->columns=width;
240 image->rows=height;
241 status=SetImageExtent(image,image->columns,image->rows,exception);
242 if (status == MagickFalse)
243 return(DestroyImageList(image));
Cristyf472c0a2018-03-09 06:59:39 -0500244 status=ResetImagePixels(image,exception);
245 if (status == MagickFalse)
246 return(DestroyImageList(image));
Cristyd1eccc82017-10-01 09:34:57 -0400247 tim_pixels=(unsigned char *) AcquireQuantumMemory(image_size,
248 sizeof(*tim_pixels));
249 if (tim_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000250 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
Cristyd1eccc82017-10-01 09:34:57 -0400251 count=ReadBlob(image,image_size,tim_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000252 if (count != (ssize_t) (image_size))
root423703c2017-09-15 10:27:41 +0000253 {
Cristyd1eccc82017-10-01 09:34:57 -0400254 tim_pixels=(unsigned char *) RelinquishMagickMemory(tim_pixels);
root423703c2017-09-15 10:27:41 +0000255 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
256 }
cristy3ed852e2009-09-05 21:47:34 +0000257 /*
cristy3ed852e2009-09-05 21:47:34 +0000258 Convert TIM raster image to pixel packets.
259 */
260 switch (bits_per_pixel)
261 {
262 case 4:
263 {
264 /*
265 Convert PseudoColor scanline.
266 */
cristybb503372010-05-27 20:51:26 +0000267 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000268 {
269 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000270 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000271 break;
Cristyd1eccc82017-10-01 09:34:57 -0400272 p=tim_pixels+y*bytes_per_line;
cristybb503372010-05-27 20:51:26 +0000273 for (x=0; x < ((ssize_t) image->columns-1); x+=2)
cristy3ed852e2009-09-05 21:47:34 +0000274 {
cristy4c08aed2011-07-01 19:47:50 +0000275 SetPixelIndex(image,(*p) & 0x0f,q);
cristyed231572011-07-14 02:18:59 +0000276 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000277 SetPixelIndex(image,(*p >> 4) & 0x0f,q);
cristy3ed852e2009-09-05 21:47:34 +0000278 p++;
cristyed231572011-07-14 02:18:59 +0000279 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000280 }
281 if ((image->columns % 2) != 0)
282 {
cristy4c08aed2011-07-01 19:47:50 +0000283 SetPixelIndex(image,(*p >> 4) & 0x0f,q);
cristy3ed852e2009-09-05 21:47:34 +0000284 p++;
cristyed231572011-07-14 02:18:59 +0000285 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000286 }
287 if (SyncAuthenticPixels(image,exception) == MagickFalse)
288 break;
289 if (image->previous == (Image *) NULL)
290 {
cristycee97112010-05-28 00:44:52 +0000291 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
292 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000293 if (status == MagickFalse)
294 break;
295 }
296 }
297 break;
298 }
299 case 8:
300 {
301 /*
302 Convert PseudoColor scanline.
303 */
cristybb503372010-05-27 20:51:26 +0000304 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000305 {
306 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000307 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000308 break;
Cristyd1eccc82017-10-01 09:34:57 -0400309 p=tim_pixels+y*bytes_per_line;
cristybb503372010-05-27 20:51:26 +0000310 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +0000311 {
312 SetPixelIndex(image,*p++,q);
cristyed231572011-07-14 02:18:59 +0000313 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000314 }
cristy3ed852e2009-09-05 21:47:34 +0000315 if (SyncAuthenticPixels(image,exception) == MagickFalse)
316 break;
317 if (image->previous == (Image *) NULL)
318 {
cristycee97112010-05-28 00:44:52 +0000319 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
320 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000321 if (status == MagickFalse)
322 break;
323 }
324 }
325 break;
326 }
327 case 16:
328 {
329 /*
330 Convert DirectColor scanline.
331 */
cristybb503372010-05-27 20:51:26 +0000332 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000333 {
Cristyd1eccc82017-10-01 09:34:57 -0400334 p=tim_pixels+y*bytes_per_line;
cristy3ed852e2009-09-05 21:47:34 +0000335 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000336 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000337 break;
cristybb503372010-05-27 20:51:26 +0000338 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000339 {
340 word=(*p++);
341 word|=(*p++ << 8);
cristy4c08aed2011-07-01 19:47:50 +0000342 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);
cristyed231572011-07-14 02:18:59 +0000348 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000349 }
350 if (SyncAuthenticPixels(image,exception) == MagickFalse)
351 break;
352 if (image->previous == (Image *) NULL)
353 {
cristycee97112010-05-28 00:44:52 +0000354 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
355 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000356 if (status == MagickFalse)
357 break;
358 }
359 }
360 break;
361 }
362 case 24:
363 {
364 /*
365 Convert DirectColor scanline.
366 */
cristybb503372010-05-27 20:51:26 +0000367 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000368 {
Cristyd1eccc82017-10-01 09:34:57 -0400369 p=tim_pixels+y*bytes_per_line;
cristy3ed852e2009-09-05 21:47:34 +0000370 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000371 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000372 break;
cristybb503372010-05-27 20:51:26 +0000373 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000374 {
cristy4c08aed2011-07-01 19:47:50 +0000375 SetPixelRed(image,ScaleCharToQuantum(*p++),q);
376 SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
377 SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
cristyed231572011-07-14 02:18:59 +0000378 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000379 }
380 if (SyncAuthenticPixels(image,exception) == MagickFalse)
381 break;
382 if (image->previous == (Image *) NULL)
383 {
cristycee97112010-05-28 00:44:52 +0000384 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
385 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000386 if (status == MagickFalse)
387 break;
388 }
389 }
390 break;
391 }
392 default:
Cristyd1eccc82017-10-01 09:34:57 -0400393 {
394 tim_pixels=(unsigned char *) RelinquishMagickMemory(tim_pixels);
395 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
396 }
cristy3ed852e2009-09-05 21:47:34 +0000397 }
398 if (image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +0000399 (void) SyncImage(image,exception);
Cristyd1eccc82017-10-01 09:34:57 -0400400 tim_pixels=(unsigned char *) RelinquishMagickMemory(tim_pixels);
cristy3ed852e2009-09-05 21:47:34 +0000401 if (EOFBlob(image) != MagickFalse)
402 {
403 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
404 image->filename);
405 break;
406 }
407 /*
408 Proceed to next image.
409 */
Cristy4e62cb72018-03-31 20:04:44 -0400410 if (image_info->number_scenes != 0)
411 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
412 break;
cristy3ed852e2009-09-05 21:47:34 +0000413 tim_info.id=ReadBlobLSBLong(image);
414 if (tim_info.id == 0x00000010)
415 {
416 /*
417 Allocate next image structure.
418 */
cristy9950d572011-10-01 18:22:35 +0000419 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000420 if (GetNextImageInList(image) == (Image *) NULL)
421 {
Cristy3b48d202018-07-01 17:11:51 -0400422 status=MagickFalse;
423 break;
cristy3ed852e2009-09-05 21:47:34 +0000424 }
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);
Cristy3b48d202018-07-01 17:11:51 -0400433 if (status == MagickFalse)
434 return(DestroyImageList(image));
cristy3ed852e2009-09-05 21:47:34 +0000435 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%
cristybb503372010-05-27 20:51:26 +0000458% size_t RegisterTIMImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000459%
460*/
cristybb503372010-05-27 20:51:26 +0000461ModuleExport size_t RegisterTIMImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000462{
463 MagickInfo
464 *entry;
465
dirk06b627a2015-04-06 18:59:17 +0000466 entry=AcquireMagickInfo("TIM","TIM","PSX TIM");
cristy3ed852e2009-09-05 21:47:34 +0000467 entry->decoder=(DecodeImageHandler *) ReadTIMImage;
cristy3ed852e2009-09-05 21:47:34 +0000468 (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*/
491ModuleExport void UnregisterTIMImage(void)
492{
493 (void) UnregisterMagickInfo("TIM");
494}