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/* 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*/ 90static Image *ReadTIMImage(const ImageInfo *image_info,ExceptionInfo *exception) 91{ 92 typedef struct _TIMInfo 93 { 94 size_t 95 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 109 MagickBooleanType 110 status; 111 112 register ssize_t 113 x; 114 115 register Quantum 116 *q; 117 118 register ssize_t 119 i; 120 121 register unsigned char 122 *p; 123 124 size_t 125 bytes_per_line, 126 height, 127 image_size, 128 pixel_mode, 129 width; 130 131 ssize_t 132 count, 133 y; 134 135 unsigned char 136 *tim_data, 137 *tim_pixels; 138 139 unsigned short 140 word; 141 142 /* 143 Open image file. 144 */ 145 assert(image_info != (const ImageInfo *) NULL); 146 assert(image_info->signature == MagickCoreSignature); 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 == MagickCoreSignature); 152 image=AcquireImage(image_info,exception); 153 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 image->depth=8; 182 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; 197 if (AcquireImageColormap(image,pixel_mode == 1 ? 256UL : 16UL,exception) == MagickFalse) 198 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; 207 for (i=0; i < (ssize_t) image->colors; i++) 208 { 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; 223 status=SetImageExtent(image,image->columns,image->rows,exception); 224 if (status == MagickFalse) 225 return(DestroyImageList(image)); 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 */ 260 for (y=(ssize_t) image->rows-1; y >= 0; y--) 261 { 262 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 263 if (q == (Quantum *) NULL) 264 break; 265 p=tim_pixels+y*bytes_per_line; 266 for (x=0; x < ((ssize_t) image->columns-1); x+=2) 267 { 268 SetPixelIndex(image,(*p) & 0x0f,q); 269 q+=GetPixelChannels(image); 270 SetPixelIndex(image,(*p >> 4) & 0x0f,q); 271 p++; 272 q+=GetPixelChannels(image); 273 } 274 if ((image->columns % 2) != 0) 275 { 276 SetPixelIndex(image,(*p >> 4) & 0x0f,q); 277 p++; 278 q+=GetPixelChannels(image); 279 } 280 if (SyncAuthenticPixels(image,exception) == MagickFalse) 281 break; 282 if (image->previous == (Image *) NULL) 283 { 284 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, 285 image->rows); 286 if (status == MagickFalse) 287 break; 288 } 289 } 290 break; 291 } 292 case 8: 293 { 294 /* 295 Convert PseudoColor scanline. 296 */ 297 for (y=(ssize_t) image->rows-1; y >= 0; y--) 298 { 299 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 300 if (q == (Quantum *) NULL) 301 break; 302 p=tim_pixels+y*bytes_per_line; 303 for (x=0; x < (ssize_t) image->columns; x++) 304 { 305 SetPixelIndex(image,*p++,q); 306 q+=GetPixelChannels(image); 307 } 308 if (SyncAuthenticPixels(image,exception) == MagickFalse) 309 break; 310 if (image->previous == (Image *) NULL) 311 { 312 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, 313 image->rows); 314 if (status == MagickFalse) 315 break; 316 } 317 } 318 break; 319 } 320 case 16: 321 { 322 /* 323 Convert DirectColor scanline. 324 */ 325 for (y=(ssize_t) image->rows-1; y >= 0; y--) 326 { 327 p=tim_pixels+y*bytes_per_line; 328 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 329 if (q == (Quantum *) NULL) 330 break; 331 for (x=0; x < (ssize_t) image->columns; x++) 332 { 333 word=(*p++); 334 word|=(*p++ << 8); 335 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); 341 q+=GetPixelChannels(image); 342 } 343 if (SyncAuthenticPixels(image,exception) == MagickFalse) 344 break; 345 if (image->previous == (Image *) NULL) 346 { 347 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, 348 image->rows); 349 if (status == MagickFalse) 350 break; 351 } 352 } 353 break; 354 } 355 case 24: 356 { 357 /* 358 Convert DirectColor scanline. 359 */ 360 for (y=(ssize_t) image->rows-1; y >= 0; y--) 361 { 362 p=tim_pixels+y*bytes_per_line; 363 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 364 if (q == (Quantum *) NULL) 365 break; 366 for (x=0; x < (ssize_t) image->columns; x++) 367 { 368 SetPixelRed(image,ScaleCharToQuantum(*p++),q); 369 SetPixelGreen(image,ScaleCharToQuantum(*p++),q); 370 SetPixelBlue(image,ScaleCharToQuantum(*p++),q); 371 q+=GetPixelChannels(image); 372 } 373 if (SyncAuthenticPixels(image,exception) == MagickFalse) 374 break; 375 if (image->previous == (Image *) NULL) 376 { 377 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, 378 image->rows); 379 if (status == MagickFalse) 380 break; 381 } 382 } 383 break; 384 } 385 default: 386 ThrowReaderException(CorruptImageError,"ImproperImageHeader"); 387 } 388 if (image->storage_class == PseudoClass) 389 (void) SyncImage(image,exception); 390 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 */ 406 AcquireNextImage(image_info,image,exception); 407 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% 443% size_t RegisterTIMImage(void) 444% 445*/ 446ModuleExport size_t RegisterTIMImage(void) 447{ 448 MagickInfo 449 *entry; 450 451 entry=AcquireMagickInfo("TIM","TIM","PSX TIM"); 452 entry->decoder=(DecodeImageHandler *) ReadTIMImage; 453 (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} 480