blob: ee883074979a30b7a0a2a95a3a7757d677543b37 [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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% July 1992 %
18% %
19% %
Cristy7ce65e72015-12-12 18:03:16 -050020% Copyright 1999-2016 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);
cristye1c94d92015-06-28 12:16:33 +0000146 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000147 if (image_info->debug != MagickFalse)
148 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
149 image_info->filename);
150 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000151 assert(exception->signature == MagickCoreSignature);
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 }
cristy7a0fa422013-09-09 13:09:21 +0000181 image->depth=8;
cristy3ed852e2009-09-05 21:47:34 +0000182 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;
cristy018f07f2011-09-04 21:15:19 +0000197 if (AcquireImageColormap(image,pixel_mode == 1 ? 256UL : 16UL,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000198 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;
cristybb503372010-05-27 20:51:26 +0000207 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000208 {
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;
cristyacabb842014-12-14 23:36:33 +0000223 status=SetImageExtent(image,image->columns,image->rows,exception);
224 if (status == MagickFalse)
225 return(DestroyImageList(image));
cristy3ed852e2009-09-05 21:47:34 +0000226 /*
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);
cristyacd2ed22011-08-30 01:44:23 +0000263 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000264 break;
cristy3ed852e2009-09-05 21:47:34 +0000265 p=tim_pixels+y*bytes_per_line;
cristybb503372010-05-27 20:51:26 +0000266 for (x=0; x < ((ssize_t) image->columns-1); x+=2)
cristy3ed852e2009-09-05 21:47:34 +0000267 {
cristy4c08aed2011-07-01 19:47:50 +0000268 SetPixelIndex(image,(*p) & 0x0f,q);
cristyed231572011-07-14 02:18:59 +0000269 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000270 SetPixelIndex(image,(*p >> 4) & 0x0f,q);
cristy3ed852e2009-09-05 21:47:34 +0000271 p++;
cristyed231572011-07-14 02:18:59 +0000272 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000273 }
274 if ((image->columns % 2) != 0)
275 {
cristy4c08aed2011-07-01 19:47:50 +0000276 SetPixelIndex(image,(*p >> 4) & 0x0f,q);
cristy3ed852e2009-09-05 21:47:34 +0000277 p++;
cristyed231572011-07-14 02:18:59 +0000278 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000279 }
280 if (SyncAuthenticPixels(image,exception) == MagickFalse)
281 break;
282 if (image->previous == (Image *) NULL)
283 {
cristycee97112010-05-28 00:44:52 +0000284 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
285 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000286 if (status == MagickFalse)
287 break;
288 }
289 }
290 break;
291 }
292 case 8:
293 {
294 /*
295 Convert PseudoColor scanline.
296 */
cristybb503372010-05-27 20:51:26 +0000297 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000298 {
299 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000300 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000301 break;
cristy3ed852e2009-09-05 21:47:34 +0000302 p=tim_pixels+y*bytes_per_line;
cristybb503372010-05-27 20:51:26 +0000303 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +0000304 {
305 SetPixelIndex(image,*p++,q);
cristyed231572011-07-14 02:18:59 +0000306 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000307 }
cristy3ed852e2009-09-05 21:47:34 +0000308 if (SyncAuthenticPixels(image,exception) == MagickFalse)
309 break;
310 if (image->previous == (Image *) NULL)
311 {
cristycee97112010-05-28 00:44:52 +0000312 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
313 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000314 if (status == MagickFalse)
315 break;
316 }
317 }
318 break;
319 }
320 case 16:
321 {
322 /*
323 Convert DirectColor scanline.
324 */
cristybb503372010-05-27 20:51:26 +0000325 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000326 {
327 p=tim_pixels+y*bytes_per_line;
328 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000329 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000330 break;
cristybb503372010-05-27 20:51:26 +0000331 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000332 {
333 word=(*p++);
334 word|=(*p++ << 8);
cristy4c08aed2011-07-01 19:47:50 +0000335 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);
cristyed231572011-07-14 02:18:59 +0000341 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000342 }
343 if (SyncAuthenticPixels(image,exception) == MagickFalse)
344 break;
345 if (image->previous == (Image *) NULL)
346 {
cristycee97112010-05-28 00:44:52 +0000347 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
348 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000349 if (status == MagickFalse)
350 break;
351 }
352 }
353 break;
354 }
355 case 24:
356 {
357 /*
358 Convert DirectColor scanline.
359 */
cristybb503372010-05-27 20:51:26 +0000360 for (y=(ssize_t) image->rows-1; y >= 0; y--)
cristy3ed852e2009-09-05 21:47:34 +0000361 {
362 p=tim_pixels+y*bytes_per_line;
363 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000364 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000365 break;
cristybb503372010-05-27 20:51:26 +0000366 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000367 {
cristy4c08aed2011-07-01 19:47:50 +0000368 SetPixelRed(image,ScaleCharToQuantum(*p++),q);
369 SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
370 SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
cristyed231572011-07-14 02:18:59 +0000371 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000372 }
373 if (SyncAuthenticPixels(image,exception) == MagickFalse)
374 break;
375 if (image->previous == (Image *) NULL)
376 {
cristycee97112010-05-28 00:44:52 +0000377 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
378 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000379 if (status == MagickFalse)
380 break;
381 }
382 }
383 break;
384 }
385 default:
386 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
387 }
388 if (image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +0000389 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000390 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 */
cristy9950d572011-10-01 18:22:35 +0000406 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000407 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%
cristybb503372010-05-27 20:51:26 +0000443% size_t RegisterTIMImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000444%
445*/
cristybb503372010-05-27 20:51:26 +0000446ModuleExport size_t RegisterTIMImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000447{
448 MagickInfo
449 *entry;
450
dirk06b627a2015-04-06 18:59:17 +0000451 entry=AcquireMagickInfo("TIM","TIM","PSX TIM");
cristy3ed852e2009-09-05 21:47:34 +0000452 entry->decoder=(DecodeImageHandler *) ReadTIMImage;
cristy3ed852e2009-09-05 21:47:34 +0000453 (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*/
476ModuleExport void UnregisterTIMImage(void)
477{
478 (void) UnregisterMagickInfo("TIM");
479}