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-2019 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 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_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