blob: 24933878d2df910d331c1aa12a69a64983339bc7 [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% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 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*/
42#include "magick/studio.h"
43#include "magick/blob.h"
44#include "magick/blob-private.h"
45#include "magick/cache.h"
cristye7e40552010-04-24 21:34:22 +000046#include "magick/colormap.h"
cristy3ed852e2009-09-05 21:47:34 +000047#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/quantum-private.h"
57#include "magick/static.h"
58#include "magick/string_.h"
59#include "magick/module.h"
60
61/*
62%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
63% %
64% %
65% %
66% R e a d T I M I m a g e %
67% %
68% %
69% %
70%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
71%
72% ReadTIMImage() reads a PSX TIM image file and returns it. It
73% allocates the memory necessary for the new Image structure and returns a
74% pointer to the new image.
75%
76% Contributed by os@scee.sony.co.uk.
77%
78% The format of the ReadTIMImage method is:
79%
80% Image *ReadTIMImage(const ImageInfo *image_info,ExceptionInfo *exception)
81%
82% A description of each parameter follows:
83%
84% o image_info: the image info.
85%
86% o exception: return any errors or warnings in this structure.
87%
88*/
89static Image *ReadTIMImage(const ImageInfo *image_info,ExceptionInfo *exception)
90{
91 typedef struct _TIMInfo
92 {
cristybb503372010-05-27 20:51:26 +000093 size_t
cristy3ed852e2009-09-05 21:47:34 +000094 id,
95 flag;
96 } TIMInfo;
97
98 TIMInfo
99 tim_info;
100
101 Image
102 *image;
103
104 int
105 bits_per_pixel,
106 has_clut;
107
cristybb503372010-05-27 20:51:26 +0000108 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000109 y;
110
111 MagickBooleanType
112 status;
113
114 register IndexPacket
115 *indexes;
116
cristybb503372010-05-27 20:51:26 +0000117 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000118 x;
119
120 register PixelPacket
121 *q;
122
cristybb503372010-05-27 20:51:26 +0000123 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000124 i;
125
126 register unsigned char
127 *p;
128
129 ssize_t
130 count;
131
132 unsigned char
133 *tim_data,
134 *tim_pixels;
135
136 unsigned short
137 word;
138
cristybb503372010-05-27 20:51:26 +0000139 size_t
cristy3ed852e2009-09-05 21:47:34 +0000140 bytes_per_line,
141 height,
142 image_size,
143 pixel_mode,
144 width;
145
146 /*
147 Open image file.
148 */
149 assert(image_info != (const ImageInfo *) NULL);
150 assert(image_info->signature == MagickSignature);
151 if (image_info->debug != MagickFalse)
152 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
153 image_info->filename);
154 assert(exception != (ExceptionInfo *) NULL);
155 assert(exception->signature == MagickSignature);
156 image=AcquireImage(image_info);
157 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
158 if (status == MagickFalse)
159 {
160 image=DestroyImageList(image);
161 return((Image *) NULL);
162 }
163 /*
164 Determine if this a TIM file.
165 */
166 tim_info.id=ReadBlobLSBLong(image);
167 do
168 {
169 /*
170 Verify TIM identifier.
171 */
172 if (tim_info.id != 0x00000010)
173 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
174 tim_info.flag=ReadBlobLSBLong(image);
175 has_clut=tim_info.flag & (1 << 3) ? 1 : 0;
176 pixel_mode=tim_info.flag & 0x07;
177 switch ((int) pixel_mode)
178 {
179 case 0: bits_per_pixel=4; break;
180 case 1: bits_per_pixel=8; break;
181 case 2: bits_per_pixel=16; break;
182 case 3: bits_per_pixel=24; break;
183 default: bits_per_pixel=4; break;
184 }
185 if (has_clut)
186 {
187 unsigned char
188 *tim_colormap;
189
190 /*
191 Read TIM raster colormap.
192 */
193 (void)ReadBlobLSBLong(image);
194 (void)ReadBlobLSBShort(image);
195 (void)ReadBlobLSBShort(image);
196 width=ReadBlobLSBShort(image);
197 height=ReadBlobLSBShort(image);
198 image->columns=width;
199 image->rows=height;
200 if (AcquireImageColormap(image,pixel_mode == 1 ? 256UL : 16UL) == MagickFalse)
201 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
202 tim_colormap=(unsigned char *) AcquireQuantumMemory(image->colors,
203 2UL*sizeof(*tim_colormap));
204 if (tim_colormap == (unsigned char *) NULL)
205 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
206 count=ReadBlob(image,2*image->colors,tim_colormap);
207 if (count != (ssize_t) (2*image->colors))
208 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
209 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;
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 */
cristybb503372010-05-27 20:51:26 +0000260 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000261 {
262 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
263 if (q == (PixelPacket *) NULL)
264 break;
265 indexes=GetAuthenticIndexQueue(image);
266 p=tim_pixels+y*bytes_per_line;
cristybb503372010-05-27 20:51:26 +0000267 for (x=0; x < ((ssize_t) image->columns-1); x+=2)
cristy3ed852e2009-09-05 21:47:34 +0000268 {
269 indexes[x]=(IndexPacket) ((*p) & 0x0f);
270 indexes[x+1]=(IndexPacket) ((*p >> 4) & 0x0f);
271 p++;
272 }
273 if ((image->columns % 2) != 0)
274 {
275 indexes[x]=(IndexPacket) ((*p >> 4) & 0x0f);
276 p++;
277 }
278 if (SyncAuthenticPixels(image,exception) == MagickFalse)
279 break;
280 if (image->previous == (Image *) NULL)
281 {
cristycee97112010-05-28 00:44:52 +0000282 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
283 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000284 if (status == MagickFalse)
285 break;
286 }
287 }
288 break;
289 }
290 case 8:
291 {
292 /*
293 Convert PseudoColor scanline.
294 */
cristybb503372010-05-27 20:51:26 +0000295 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000296 {
297 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
298 if (q == (PixelPacket *) NULL)
299 break;
300 indexes=GetAuthenticIndexQueue(image);
301 p=tim_pixels+y*bytes_per_line;
cristybb503372010-05-27 20:51:26 +0000302 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000303 indexes[x]=(*p++);
304 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);
325 if (q == (PixelPacket *) NULL)
326 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);
331 q->blue=ScaleCharToQuantum(ScaleColor5to8((1UL*word >> 10) & 0x1f));
332 q->green=ScaleCharToQuantum(ScaleColor5to8((1UL*word >> 5) & 0x1f));
333 q->red=ScaleCharToQuantum(ScaleColor5to8(1UL*word & 0x1f));
334 q++;
335 }
336 if (SyncAuthenticPixels(image,exception) == MagickFalse)
337 break;
338 if (image->previous == (Image *) NULL)
339 {
cristycee97112010-05-28 00:44:52 +0000340 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
341 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000342 if (status == MagickFalse)
343 break;
344 }
345 }
346 break;
347 }
348 case 24:
349 {
350 /*
351 Convert DirectColor scanline.
352 */
cristybb503372010-05-27 20:51:26 +0000353 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000354 {
355 p=tim_pixels+y*bytes_per_line;
356 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
357 if (q == (PixelPacket *) NULL)
358 break;
cristybb503372010-05-27 20:51:26 +0000359 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000360 {
361 q->red=ScaleCharToQuantum(*p++);
362 q->green=ScaleCharToQuantum(*p++);
363 q->blue=ScaleCharToQuantum(*p++);
364 q++;
365 }
366 if (SyncAuthenticPixels(image,exception) == MagickFalse)
367 break;
368 if (image->previous == (Image *) NULL)
369 {
cristycee97112010-05-28 00:44:52 +0000370 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
371 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000372 if (status == MagickFalse)
373 break;
374 }
375 }
376 break;
377 }
378 default:
379 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
380 }
381 if (image->storage_class == PseudoClass)
382 (void) SyncImage(image);
383 tim_pixels=(unsigned char *) RelinquishMagickMemory(tim_pixels);
384 if (EOFBlob(image) != MagickFalse)
385 {
386 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
387 image->filename);
388 break;
389 }
390 /*
391 Proceed to next image.
392 */
393 tim_info.id=ReadBlobLSBLong(image);
394 if (tim_info.id == 0x00000010)
395 {
396 /*
397 Allocate next image structure.
398 */
399 AcquireNextImage(image_info,image);
400 if (GetNextImageInList(image) == (Image *) NULL)
401 {
402 image=DestroyImageList(image);
403 return((Image *) NULL);
404 }
405 image=SyncNextImageInList(image);
406 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
407 GetBlobSize(image));
408 if (status == MagickFalse)
409 break;
410 }
411 } while (tim_info.id == 0x00000010);
412 (void) CloseBlob(image);
413 return(GetFirstImageInList(image));
414}
415
416/*
417%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
418% %
419% %
420% %
421% R e g i s t e r T I M I m a g e %
422% %
423% %
424% %
425%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
426%
427% RegisterTIMImage() adds attributes for the TIM image format to
428% the list of supported formats. The attributes include the image format
429% tag, a method to read and/or write the format, whether the format
430% supports the saving of more than one frame to the same file or blob,
431% whether the format supports native in-memory I/O, and a brief
432% description of the format.
433%
434% The format of the RegisterTIMImage method is:
435%
cristybb503372010-05-27 20:51:26 +0000436% size_t RegisterTIMImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000437%
438*/
cristybb503372010-05-27 20:51:26 +0000439ModuleExport size_t RegisterTIMImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000440{
441 MagickInfo
442 *entry;
443
444 entry=SetMagickInfo("TIM");
445 entry->decoder=(DecodeImageHandler *) ReadTIMImage;
446 entry->description=ConstantString("PSX TIM");
447 entry->module=ConstantString("TIM");
448 (void) RegisterMagickInfo(entry);
449 return(MagickImageCoderSignature);
450}
451
452/*
453%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
454% %
455% %
456% %
457% U n r e g i s t e r T I M I m a g e %
458% %
459% %
460% %
461%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
462%
463% UnregisterTIMImage() removes format registrations made by the
464% TIM module from the list of supported formats.
465%
466% The format of the UnregisterTIMImage method is:
467%
468% UnregisterTIMImage(void)
469%
470*/
471ModuleExport void UnregisterTIMImage(void)
472{
473 (void) UnregisterMagickInfo("TIM");
474}