/src/imagemagick/coders/tim.c
Line | Count | Source |
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 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/license/ % |
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 | | */ |
90 | | static Image *ReadTIMImage(const ImageInfo *image_info,ExceptionInfo *exception) |
91 | 740 | { |
92 | 740 | typedef struct _TIMInfo |
93 | 740 | { |
94 | 740 | size_t |
95 | 740 | id, |
96 | 740 | flag; |
97 | 740 | } TIMInfo; |
98 | | |
99 | 740 | TIMInfo |
100 | 740 | tim_info; |
101 | | |
102 | 740 | Image |
103 | 740 | *image; |
104 | | |
105 | 740 | int |
106 | 740 | bits_per_pixel, |
107 | 740 | has_clut; |
108 | | |
109 | 740 | MagickBooleanType |
110 | 740 | status; |
111 | | |
112 | 740 | ssize_t |
113 | 740 | x; |
114 | | |
115 | 740 | Quantum |
116 | 740 | *q; |
117 | | |
118 | 740 | ssize_t |
119 | 740 | i; |
120 | | |
121 | 740 | unsigned char |
122 | 740 | *p; |
123 | | |
124 | 740 | size_t |
125 | 740 | bytes_per_line, |
126 | 740 | height, |
127 | 740 | image_size, |
128 | 740 | pixel_mode, |
129 | 740 | width; |
130 | | |
131 | 740 | ssize_t |
132 | 740 | count, |
133 | 740 | y; |
134 | | |
135 | 740 | unsigned char |
136 | 740 | *tim_pixels; |
137 | | |
138 | 740 | unsigned short |
139 | 740 | word; |
140 | | |
141 | | /* |
142 | | Open image file. |
143 | | */ |
144 | 740 | assert(image_info != (const ImageInfo *) NULL); |
145 | 740 | assert(image_info->signature == MagickCoreSignature); |
146 | 740 | assert(exception != (ExceptionInfo *) NULL); |
147 | 740 | assert(exception->signature == MagickCoreSignature); |
148 | 740 | if (IsEventLogging() != MagickFalse) |
149 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", |
150 | 0 | image_info->filename); |
151 | 740 | image=AcquireImage(image_info,exception); |
152 | 740 | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
153 | 740 | if (status == MagickFalse) |
154 | 0 | { |
155 | 0 | image=DestroyImageList(image); |
156 | 0 | return((Image *) NULL); |
157 | 0 | } |
158 | | /* |
159 | | Determine if this a TIM file. |
160 | | */ |
161 | 740 | tim_info.id=ReadBlobLSBLong(image); |
162 | 740 | do |
163 | 1.95k | { |
164 | | /* |
165 | | Verify TIM identifier. |
166 | | */ |
167 | 1.95k | if (tim_info.id != 0x00000010) |
168 | 1.90k | ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
169 | 1.90k | tim_info.flag=ReadBlobLSBLong(image); |
170 | 1.90k | has_clut=tim_info.flag & (1 << 3) ? 1 : 0; |
171 | 1.90k | pixel_mode=tim_info.flag & 0x07; |
172 | 1.90k | switch ((int) pixel_mode) |
173 | 1.90k | { |
174 | 553 | case 0: bits_per_pixel=4; break; |
175 | 556 | case 1: bits_per_pixel=8; break; |
176 | 414 | case 2: bits_per_pixel=16; break; |
177 | 90 | case 3: bits_per_pixel=24; break; |
178 | 295 | default: bits_per_pixel=4; break; |
179 | 1.90k | } |
180 | 1.90k | image->depth=8; |
181 | 1.90k | if (has_clut) |
182 | 581 | { |
183 | 581 | unsigned char |
184 | 581 | *tim_colormap; |
185 | | |
186 | | /* |
187 | | Read TIM raster colormap. |
188 | | */ |
189 | 581 | (void)ReadBlobLSBLong(image); |
190 | 581 | (void)ReadBlobLSBShort(image); |
191 | 581 | (void)ReadBlobLSBShort(image); |
192 | 581 | width=ReadBlobLSBShort(image); |
193 | 581 | height=ReadBlobLSBShort(image); |
194 | 581 | image->columns=width; |
195 | 581 | image->rows=height; |
196 | 581 | if (AcquireImageColormap(image,pixel_mode == 1 ? 256UL : 16UL,exception) == MagickFalse) |
197 | 581 | ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); |
198 | 581 | tim_colormap=(unsigned char *) AcquireQuantumMemory(image->colors, |
199 | 581 | 2UL*sizeof(*tim_colormap)); |
200 | 581 | if (tim_colormap == (unsigned char *) NULL) |
201 | 581 | ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); |
202 | 581 | count=ReadBlob(image,2*image->colors,tim_colormap); |
203 | 581 | if (count != (ssize_t) (2*image->colors)) |
204 | 43 | { |
205 | 43 | tim_colormap=(unsigned char *) RelinquishMagickMemory(tim_colormap); |
206 | 43 | ThrowReaderException(CorruptImageError, |
207 | 43 | "InsufficientImageDataInFile"); |
208 | 0 | } |
209 | 538 | p=tim_colormap; |
210 | 35.5k | for (i=0; i < (ssize_t) image->colors; i++) |
211 | 35.0k | { |
212 | 35.0k | word=(*p++); |
213 | 35.0k | word|=(unsigned short) (*p++ << 8); |
214 | 35.0k | image->colormap[i].blue=ScaleCharToQuantum( |
215 | 35.0k | ScaleColor5to8(1UL*(word >> 10) & 0x1f)); |
216 | 35.0k | image->colormap[i].green=ScaleCharToQuantum( |
217 | 35.0k | ScaleColor5to8(1UL*(word >> 5) & 0x1f)); |
218 | 35.0k | image->colormap[i].red=ScaleCharToQuantum( |
219 | 35.0k | ScaleColor5to8(1UL*word & 0x1f)); |
220 | 35.0k | } |
221 | 538 | tim_colormap=(unsigned char *) RelinquishMagickMemory(tim_colormap); |
222 | 538 | } |
223 | 1.86k | if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0)) |
224 | 0 | if (image->scene >= (image_info->scene+image_info->number_scenes-1)) |
225 | 0 | break; |
226 | | /* |
227 | | Read image data. |
228 | | */ |
229 | 1.86k | (void) ReadBlobLSBLong(image); |
230 | 1.86k | (void) ReadBlobLSBShort(image); |
231 | 1.86k | (void) ReadBlobLSBShort(image); |
232 | 1.86k | width=ReadBlobLSBShort(image); |
233 | 1.86k | height=ReadBlobLSBShort(image); |
234 | 1.86k | if (HeapOverflowSanityCheckGetSize(2*width,height,&image_size) != MagickFalse) |
235 | 1.79k | ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
236 | 1.79k | if (image_size > GetBlobSize(image)) |
237 | 1.73k | ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); |
238 | 1.73k | bytes_per_line=width*2; |
239 | 1.73k | width=(size_t) (((ssize_t) width*16)/bits_per_pixel); |
240 | 1.73k | image->columns=width; |
241 | 1.73k | image->rows=height; |
242 | 1.73k | status=SetImageExtent(image,image->columns,image->rows,exception); |
243 | 1.73k | if (status == MagickFalse) |
244 | 35 | return(DestroyImageList(image)); |
245 | 1.70k | status=ResetImagePixels(image,exception); |
246 | 1.70k | if (status == MagickFalse) |
247 | 0 | return(DestroyImageList(image)); |
248 | 1.70k | tim_pixels=(unsigned char *) AcquireQuantumMemory(image_size, |
249 | 1.70k | sizeof(*tim_pixels)); |
250 | 1.70k | if (tim_pixels == (unsigned char *) NULL) |
251 | 1.70k | ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); |
252 | 1.70k | count=ReadBlob(image,image_size,tim_pixels); |
253 | 1.70k | if (count != (ssize_t) (image_size)) |
254 | 109 | { |
255 | 109 | tim_pixels=(unsigned char *) RelinquishMagickMemory(tim_pixels); |
256 | 109 | ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); |
257 | 0 | } |
258 | | /* |
259 | | Convert TIM raster image to pixel packets. |
260 | | */ |
261 | 1.59k | switch (bits_per_pixel) |
262 | 1.59k | { |
263 | 670 | case 4: |
264 | 670 | { |
265 | | /* |
266 | | Convert PseudoColor scanline. |
267 | | */ |
268 | 16.8k | for (y=(ssize_t) image->rows-1; y >= 0; y--) |
269 | 16.1k | { |
270 | 16.1k | q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
271 | 16.1k | if (q == (Quantum *) NULL) |
272 | 0 | break; |
273 | 16.1k | p=tim_pixels+y*(ssize_t) bytes_per_line; |
274 | 72.2k | for (x=0; x < ((ssize_t) image->columns-1); x+=2) |
275 | 56.0k | { |
276 | 56.0k | SetPixelIndex(image,(Quantum) ((*p) & 0x0f),q); |
277 | 56.0k | q+=(ptrdiff_t) GetPixelChannels(image); |
278 | 56.0k | SetPixelIndex(image,(Quantum) ((*p >> 4) & 0x0f),q); |
279 | 56.0k | p++; |
280 | 56.0k | q+=(ptrdiff_t) GetPixelChannels(image); |
281 | 56.0k | } |
282 | 16.1k | if ((image->columns % 2) != 0) |
283 | 0 | { |
284 | 0 | SetPixelIndex(image,(Quantum) ((*p >> 4) & 0x0f),q); |
285 | 0 | p++; |
286 | 0 | q+=(ptrdiff_t) GetPixelChannels(image); |
287 | 0 | } |
288 | 16.1k | if (SyncAuthenticPixels(image,exception) == MagickFalse) |
289 | 0 | break; |
290 | 16.1k | if (image->previous == (Image *) NULL) |
291 | 8.53k | { |
292 | 8.53k | status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, |
293 | 8.53k | image->rows); |
294 | 8.53k | if (status == MagickFalse) |
295 | 0 | break; |
296 | 8.53k | } |
297 | 16.1k | } |
298 | 670 | break; |
299 | 0 | } |
300 | 476 | case 8: |
301 | 476 | { |
302 | | /* |
303 | | Convert PseudoColor scanline. |
304 | | */ |
305 | 7.53k | for (y=(ssize_t) image->rows-1; y >= 0; y--) |
306 | 7.06k | { |
307 | 7.06k | q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
308 | 7.06k | if (q == (Quantum *) NULL) |
309 | 0 | break; |
310 | 7.06k | p=tim_pixels+y*(ssize_t) bytes_per_line; |
311 | 30.7k | for (x=0; x < (ssize_t) image->columns; x++) |
312 | 23.6k | { |
313 | 23.6k | SetPixelIndex(image,*p++,q); |
314 | 23.6k | q+=(ptrdiff_t) GetPixelChannels(image); |
315 | 23.6k | } |
316 | 7.06k | if (SyncAuthenticPixels(image,exception) == MagickFalse) |
317 | 0 | break; |
318 | 7.06k | if (image->previous == (Image *) NULL) |
319 | 6.51k | { |
320 | 6.51k | status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, |
321 | 6.51k | image->rows); |
322 | 6.51k | if (status == MagickFalse) |
323 | 0 | break; |
324 | 6.51k | } |
325 | 7.06k | } |
326 | 476 | break; |
327 | 0 | } |
328 | 373 | case 16: |
329 | 373 | { |
330 | | /* |
331 | | Convert DirectColor scanline. |
332 | | */ |
333 | 12.1k | for (y=(ssize_t) image->rows-1; y >= 0; y--) |
334 | 11.7k | { |
335 | 11.7k | p=tim_pixels+y*(ssize_t) bytes_per_line; |
336 | 11.7k | q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
337 | 11.7k | if (q == (Quantum *) NULL) |
338 | 0 | break; |
339 | 37.0k | for (x=0; x < (ssize_t) image->columns; x++) |
340 | 25.3k | { |
341 | 25.3k | word=(*p++); |
342 | 25.3k | word|=(*p++ << 8); |
343 | 25.3k | SetPixelBlue(image,ScaleCharToQuantum(ScaleColor5to8( |
344 | 25.3k | (1UL*word >> 10) & 0x1f)),q); |
345 | 25.3k | SetPixelGreen(image,ScaleCharToQuantum(ScaleColor5to8( |
346 | 25.3k | (1UL*word >> 5) & 0x1f)),q); |
347 | 25.3k | SetPixelRed(image,ScaleCharToQuantum(ScaleColor5to8( |
348 | 25.3k | (1UL*word >> 0) & 0x1f)),q); |
349 | 25.3k | q+=(ptrdiff_t) GetPixelChannels(image); |
350 | 25.3k | } |
351 | 11.7k | if (SyncAuthenticPixels(image,exception) == MagickFalse) |
352 | 0 | break; |
353 | 11.7k | if (image->previous == (Image *) NULL) |
354 | 11.2k | { |
355 | 11.2k | status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, |
356 | 11.2k | image->rows); |
357 | 11.2k | if (status == MagickFalse) |
358 | 0 | break; |
359 | 11.2k | } |
360 | 11.7k | } |
361 | 373 | break; |
362 | 0 | } |
363 | 75 | case 24: |
364 | 75 | { |
365 | | /* |
366 | | Convert DirectColor scanline. |
367 | | */ |
368 | 3.03k | for (y=(ssize_t) image->rows-1; y >= 0; y--) |
369 | 2.95k | { |
370 | 2.95k | p=tim_pixels+y*(ssize_t) bytes_per_line; |
371 | 2.95k | q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
372 | 2.95k | if (q == (Quantum *) NULL) |
373 | 0 | break; |
374 | 12.3k | for (x=0; x < (ssize_t) image->columns; x++) |
375 | 9.38k | { |
376 | 9.38k | SetPixelRed(image,ScaleCharToQuantum(*p++),q); |
377 | 9.38k | SetPixelGreen(image,ScaleCharToQuantum(*p++),q); |
378 | 9.38k | SetPixelBlue(image,ScaleCharToQuantum(*p++),q); |
379 | 9.38k | q+=(ptrdiff_t) GetPixelChannels(image); |
380 | 9.38k | } |
381 | 2.95k | if (SyncAuthenticPixels(image,exception) == MagickFalse) |
382 | 0 | break; |
383 | 2.95k | if (image->previous == (Image *) NULL) |
384 | 2.72k | { |
385 | 2.72k | status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, |
386 | 2.72k | image->rows); |
387 | 2.72k | if (status == MagickFalse) |
388 | 0 | break; |
389 | 2.72k | } |
390 | 2.95k | } |
391 | 75 | break; |
392 | 0 | } |
393 | 0 | default: |
394 | 0 | { |
395 | 0 | tim_pixels=(unsigned char *) RelinquishMagickMemory(tim_pixels); |
396 | 0 | ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
397 | 0 | } |
398 | 1.59k | } |
399 | 1.59k | if (image->storage_class == PseudoClass) |
400 | 478 | (void) SyncImage(image,exception); |
401 | 1.59k | tim_pixels=(unsigned char *) RelinquishMagickMemory(tim_pixels); |
402 | 1.59k | if (EOFBlob(image) != MagickFalse) |
403 | 0 | { |
404 | 0 | ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile", |
405 | 0 | image->filename); |
406 | 0 | break; |
407 | 0 | } |
408 | | /* |
409 | | Proceed to next image. |
410 | | */ |
411 | 1.59k | if (image_info->number_scenes != 0) |
412 | 0 | if (image->scene >= (image_info->scene+image_info->number_scenes-1)) |
413 | 0 | break; |
414 | 1.59k | tim_info.id=ReadBlobLSBLong(image); |
415 | 1.59k | if (tim_info.id == 0x00000010) |
416 | 1.21k | { |
417 | | /* |
418 | | Allocate next image structure. |
419 | | */ |
420 | 1.21k | AcquireNextImage(image_info,image,exception); |
421 | 1.21k | if (GetNextImageInList(image) == (Image *) NULL) |
422 | 0 | { |
423 | 0 | status=MagickFalse; |
424 | 0 | break; |
425 | 0 | } |
426 | 1.21k | image=SyncNextImageInList(image); |
427 | 1.21k | status=SetImageProgress(image,LoadImagesTag,TellBlob(image), |
428 | 1.21k | GetBlobSize(image)); |
429 | 1.21k | if (status == MagickFalse) |
430 | 0 | break; |
431 | 1.21k | } |
432 | 1.59k | } while (tim_info.id == 0x00000010); |
433 | 384 | if (CloseBlob(image) == MagickFalse) |
434 | 0 | status=MagickFalse; |
435 | 384 | if (status == MagickFalse) |
436 | 0 | return(DestroyImageList(image)); |
437 | 384 | return(GetFirstImageInList(image)); |
438 | 384 | } |
439 | | |
440 | | /* |
441 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
442 | | % % |
443 | | % % |
444 | | % % |
445 | | % R e g i s t e r T I M I m a g e % |
446 | | % % |
447 | | % % |
448 | | % % |
449 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
450 | | % |
451 | | % RegisterTIMImage() adds attributes for the TIM image format to |
452 | | % the list of supported formats. The attributes include the image format |
453 | | % tag, a method to read and/or write the format, whether the format |
454 | | % supports the saving of more than one frame to the same file or blob, |
455 | | % whether the format supports native in-memory I/O, and a brief |
456 | | % description of the format. |
457 | | % |
458 | | % The format of the RegisterTIMImage method is: |
459 | | % |
460 | | % size_t RegisterTIMImage(void) |
461 | | % |
462 | | */ |
463 | | ModuleExport size_t RegisterTIMImage(void) |
464 | 8 | { |
465 | 8 | MagickInfo |
466 | 8 | *entry; |
467 | | |
468 | 8 | entry=AcquireMagickInfo("TIM","TIM","PSX TIM"); |
469 | 8 | entry->decoder=(DecodeImageHandler *) ReadTIMImage; |
470 | 8 | (void) RegisterMagickInfo(entry); |
471 | 8 | return(MagickImageCoderSignature); |
472 | 8 | } |
473 | | |
474 | | /* |
475 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
476 | | % % |
477 | | % % |
478 | | % % |
479 | | % U n r e g i s t e r T I M I m a g e % |
480 | | % % |
481 | | % % |
482 | | % % |
483 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
484 | | % |
485 | | % UnregisterTIMImage() removes format registrations made by the |
486 | | % TIM module from the list of supported formats. |
487 | | % |
488 | | % The format of the UnregisterTIMImage method is: |
489 | | % |
490 | | % UnregisterTIMImage(void) |
491 | | % |
492 | | */ |
493 | | ModuleExport void UnregisterTIMImage(void) |
494 | 0 | { |
495 | 0 | (void) UnregisterMagickInfo("TIM"); |
496 | 0 | } |