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 */
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_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 */
RegisterTIMImage(void)446 ModuleExport 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 */
UnregisterTIMImage(void)476 ModuleExport void UnregisterTIMImage(void)
477 {
478 (void) UnregisterMagickInfo("TIM");
479 }
480