blob: 7d2f8b5f4dfb878eaf669a144c862da8fb86a7c4 [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 %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 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% %
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*/
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
cristybb503372010-05-27 20:51:26 +0000112 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000113 x;
114
cristy4c08aed2011-07-01 19:47:50 +0000115 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000116 *q;
117
cristybb503372010-05-27 20:51:26 +0000118 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000119 i;
120
121 register unsigned char
122 *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
136 *tim_data,
137 *tim_pixels;
138
139 unsigned short
140 word;
141
cristy3ed852e2009-09-05 21:47:34 +0000142 /*
143 Open image file.
144 */
145 assert(image_info != (const ImageInfo *) NULL);
146 assert(image_info->signature == MagickSignature);
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 == MagickSignature);
cristy9950d572011-10-01 18:22:35 +0000152 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000153 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 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))
204 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
205 p=tim_colormap;
cristybb503372010-05-27 20:51:26 +0000206 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000207 {
208 word=(*p++);
209 word|=(unsigned short) (*p++ << 8);
210 image->colormap[i].blue=ScaleCharToQuantum(
211 ScaleColor5to8(1UL*(word >> 10) & 0x1f));
212 image->colormap[i].green=ScaleCharToQuantum(
213 ScaleColor5to8(1UL*(word >> 5) & 0x1f));
214 image->colormap[i].red=ScaleCharToQuantum(
215 ScaleColor5to8(1UL*word & 0x1f));
216 }
217 tim_colormap=(unsigned char *) RelinquishMagickMemory(tim_colormap);
218 }
219 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
220 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
221 break;
222 /*
223 Read image data.
224 */
225 (void) ReadBlobLSBLong(image);
226 (void) ReadBlobLSBShort(image);
227 (void) ReadBlobLSBShort(image);
228 width=ReadBlobLSBShort(image);
229 height=ReadBlobLSBShort(image);
230 image_size=2*width*height;
231 bytes_per_line=width*2;
232 width=(width*16)/bits_per_pixel;
233 tim_data=(unsigned char *) AcquireQuantumMemory(image_size,
234 sizeof(*tim_data));
235 if (tim_data == (unsigned char *) NULL)
236 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
237 count=ReadBlob(image,image_size,tim_data);
238 if (count != (ssize_t) (image_size))
239 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
240 tim_pixels=tim_data;
241 /*
242 Initialize image structure.
243 */
244 image->columns=width;
245 image->rows=height;
246 /*
247 Convert TIM raster image to pixel packets.
248 */
249 switch (bits_per_pixel)
250 {
251 case 4:
252 {
253 /*
254 Convert PseudoColor scanline.
255 */
cristybb503372010-05-27 20:51:26 +0000256 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000257 {
258 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000259 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000260 break;
cristy3ed852e2009-09-05 21:47:34 +0000261 p=tim_pixels+y*bytes_per_line;
cristybb503372010-05-27 20:51:26 +0000262 for (x=0; x < ((ssize_t) image->columns-1); x+=2)
cristy3ed852e2009-09-05 21:47:34 +0000263 {
cristy4c08aed2011-07-01 19:47:50 +0000264 SetPixelIndex(image,(*p) & 0x0f,q);
cristyed231572011-07-14 02:18:59 +0000265 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000266 SetPixelIndex(image,(*p >> 4) & 0x0f,q);
cristy3ed852e2009-09-05 21:47:34 +0000267 p++;
cristyed231572011-07-14 02:18:59 +0000268 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000269 }
270 if ((image->columns % 2) != 0)
271 {
cristy4c08aed2011-07-01 19:47:50 +0000272 SetPixelIndex(image,(*p >> 4) & 0x0f,q);
cristy3ed852e2009-09-05 21:47:34 +0000273 p++;
cristyed231572011-07-14 02:18:59 +0000274 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000275 }
276 if (SyncAuthenticPixels(image,exception) == MagickFalse)
277 break;
278 if (image->previous == (Image *) NULL)
279 {
cristycee97112010-05-28 00:44:52 +0000280 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
281 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000282 if (status == MagickFalse)
283 break;
284 }
285 }
286 break;
287 }
288 case 8:
289 {
290 /*
291 Convert PseudoColor scanline.
292 */
cristybb503372010-05-27 20:51:26 +0000293 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000294 {
295 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000296 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000297 break;
cristy3ed852e2009-09-05 21:47:34 +0000298 p=tim_pixels+y*bytes_per_line;
cristybb503372010-05-27 20:51:26 +0000299 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +0000300 {
301 SetPixelIndex(image,*p++,q);
cristyed231572011-07-14 02:18:59 +0000302 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000303 }
cristy3ed852e2009-09-05 21:47:34 +0000304 if (SyncAuthenticPixels(image,exception) == MagickFalse)
305 break;
306 if (image->previous == (Image *) NULL)
307 {
cristycee97112010-05-28 00:44:52 +0000308 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
309 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000310 if (status == MagickFalse)
311 break;
312 }
313 }
314 break;
315 }
316 case 16:
317 {
318 /*
319 Convert DirectColor scanline.
320 */
cristybb503372010-05-27 20:51:26 +0000321 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000322 {
323 p=tim_pixels+y*bytes_per_line;
324 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000325 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000326 break;
cristybb503372010-05-27 20:51:26 +0000327 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000328 {
329 word=(*p++);
330 word|=(*p++ << 8);
cristy4c08aed2011-07-01 19:47:50 +0000331 SetPixelBlue(image,ScaleCharToQuantum(ScaleColor5to8(
332 (1UL*word >> 10) & 0x1f)),q);
333 SetPixelGreen(image,ScaleCharToQuantum(ScaleColor5to8(
334 (1UL*word >> 5) & 0x1f)),q);
335 SetPixelRed(image,ScaleCharToQuantum(ScaleColor5to8(
336 (1UL*word >> 0) & 0x1f)),q);
cristyed231572011-07-14 02:18:59 +0000337 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000338 }
339 if (SyncAuthenticPixels(image,exception) == MagickFalse)
340 break;
341 if (image->previous == (Image *) NULL)
342 {
cristycee97112010-05-28 00:44:52 +0000343 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
344 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000345 if (status == MagickFalse)
346 break;
347 }
348 }
349 break;
350 }
351 case 24:
352 {
353 /*
354 Convert DirectColor scanline.
355 */
cristybb503372010-05-27 20:51:26 +0000356 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000357 {
358 p=tim_pixels+y*bytes_per_line;
359 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000360 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000361 break;
cristybb503372010-05-27 20:51:26 +0000362 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000363 {
cristy4c08aed2011-07-01 19:47:50 +0000364 SetPixelRed(image,ScaleCharToQuantum(*p++),q);
365 SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
366 SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
cristyed231572011-07-14 02:18:59 +0000367 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000368 }
369 if (SyncAuthenticPixels(image,exception) == MagickFalse)
370 break;
371 if (image->previous == (Image *) NULL)
372 {
cristycee97112010-05-28 00:44:52 +0000373 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
374 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000375 if (status == MagickFalse)
376 break;
377 }
378 }
379 break;
380 }
381 default:
382 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
383 }
384 if (image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +0000385 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000386 tim_pixels=(unsigned char *) RelinquishMagickMemory(tim_pixels);
387 if (EOFBlob(image) != MagickFalse)
388 {
389 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
390 image->filename);
391 break;
392 }
393 /*
394 Proceed to next image.
395 */
396 tim_info.id=ReadBlobLSBLong(image);
397 if (tim_info.id == 0x00000010)
398 {
399 /*
400 Allocate next image structure.
401 */
cristy9950d572011-10-01 18:22:35 +0000402 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000403 if (GetNextImageInList(image) == (Image *) NULL)
404 {
405 image=DestroyImageList(image);
406 return((Image *) NULL);
407 }
408 image=SyncNextImageInList(image);
409 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
410 GetBlobSize(image));
411 if (status == MagickFalse)
412 break;
413 }
414 } while (tim_info.id == 0x00000010);
415 (void) CloseBlob(image);
416 return(GetFirstImageInList(image));
417}
418
419/*
420%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421% %
422% %
423% %
424% R e g i s t e r T I M I m a g e %
425% %
426% %
427% %
428%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
429%
430% RegisterTIMImage() adds attributes for the TIM image format to
431% the list of supported formats. The attributes include the image format
432% tag, a method to read and/or write the format, whether the format
433% supports the saving of more than one frame to the same file or blob,
434% whether the format supports native in-memory I/O, and a brief
435% description of the format.
436%
437% The format of the RegisterTIMImage method is:
438%
cristybb503372010-05-27 20:51:26 +0000439% size_t RegisterTIMImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000440%
441*/
cristybb503372010-05-27 20:51:26 +0000442ModuleExport size_t RegisterTIMImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000443{
444 MagickInfo
445 *entry;
446
447 entry=SetMagickInfo("TIM");
448 entry->decoder=(DecodeImageHandler *) ReadTIMImage;
449 entry->description=ConstantString("PSX TIM");
450 entry->module=ConstantString("TIM");
451 (void) RegisterMagickInfo(entry);
452 return(MagickImageCoderSignature);
453}
454
455/*
456%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
457% %
458% %
459% %
460% U n r e g i s t e r T I M I m a g e %
461% %
462% %
463% %
464%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
465%
466% UnregisterTIMImage() removes format registrations made by the
467% TIM module from the list of supported formats.
468%
469% The format of the UnregisterTIMImage method is:
470%
471% UnregisterTIMImage(void)
472%
473*/
474ModuleExport void UnregisterTIMImage(void)
475{
476 (void) UnregisterMagickInfo("TIM");
477}