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