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