| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| % TTTTT IIIII M M 222 % |
| % T I MM MM 2 2 % |
| % T I M M M 2 % |
| % T I M M 2 % |
| % T IIIII M M 22222 % |
| % % |
| % % |
| % Read PSX TIM2 Image Format % |
| % % |
| % Software Design % |
| % Ramiro Balado Ordax % |
| % May 2019 % |
| % % |
| % % |
| % Copyright 2019-2019 ImageMagick Studio LLC, a non-profit organization % |
| % dedicated to making software imaging solutions freely available. % |
| % % |
| % You may not use this file except in compliance with the License. You may % |
| % obtain a copy of the License at % |
| % % |
| % https://imagemagick.org/script/license.php % |
| % % |
| % Unless required by applicable law or agreed to in writing, software % |
| % distributed under the License is distributed on an "AS IS" BASIS, % |
| % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % |
| % See the License for the specific language governing permissions and % |
| % limitations under the License. % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % |
| */ |
| |
| |
| /* |
| Include declarations. |
| */ |
| #include "MagickCore/studio.h" |
| #include "MagickCore/blob.h" |
| #include "MagickCore/blob-private.h" |
| #include "MagickCore/cache.h" |
| #include "MagickCore/colormap.h" |
| #include "MagickCore/channel.h" |
| #include "MagickCore/exception.h" |
| #include "MagickCore/exception-private.h" |
| #include "MagickCore/image.h" |
| #include "MagickCore/image-private.h" |
| #include "MagickCore/list.h" |
| #include "MagickCore/magick.h" |
| #include "MagickCore/memory_.h" |
| #include "MagickCore/monitor.h" |
| #include "MagickCore/monitor-private.h" |
| #include "MagickCore/pixel-accessor.h" |
| #include "MagickCore/quantum-private.h" |
| #include "MagickCore/static.h" |
| #include "MagickCore/string_.h" |
| #include "MagickCore/module.h" |
| |
| |
| /* |
| Typedef declarations |
| */ |
| typedef struct _TIM2FileHeader |
| { |
| unsigned int |
| magic_num; |
| |
| unsigned char |
| format_vers, |
| format_type; |
| |
| unsigned short |
| image_count; |
| |
| char |
| reserved[8]; |
| } TIM2FileHeader; |
| |
| typedef struct _TIM2ImageHeader |
| { |
| unsigned int |
| total_size, |
| clut_size, |
| image_size; |
| |
| unsigned short |
| header_size, |
| clut_color_count; |
| |
| unsigned char |
| img_format, |
| mipmap_count, |
| clut_type, |
| bpp_type; |
| |
| unsigned short |
| width, |
| height; |
| |
| MagickSizeType |
| GsTex0, |
| GsTex1; |
| |
| unsigned int |
| GsRegs, |
| GsTexClut; |
| } TIM2ImageHeader; |
| |
| typedef enum |
| { |
| CSM1=0, |
| CSM2=1, |
| } CSM; |
| |
| typedef enum |
| { |
| RGBA32=0, |
| RGB24=1, |
| RGBA16=2, |
| } TIM2ColorEncoding; |
| |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| % R e a d T I M 2 I m a g e % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % ReadTIM2Image() reads a PS2 TIM image file and returns it. It |
| % allocates the memory necessary for the new Image structure and returns a |
| % pointer to the new image. |
| % |
| % The format of the ReadTIM2Image method is: |
| % |
| % Image *ReadTIM2Image(const ImageInfo *image_info,ExceptionInfo *exception) |
| % |
| % A description of each parameter follows: |
| % |
| % o image_info: the image info. |
| % |
| % o exception: return any errors or warnings in this structure. |
| % |
| */ |
| static inline void ReadTIM2ImageHeader(Image *image,TIM2ImageHeader *header) |
| { |
| header->total_size=ReadBlobLSBLong(image); |
| header->clut_size=ReadBlobLSBLong(image); |
| header->image_size=ReadBlobLSBLong(image); |
| header->header_size=ReadBlobLSBShort(image); |
| |
| header->clut_color_count=ReadBlobLSBShort(image); |
| header->img_format=(unsigned char) ReadBlobByte(image); |
| header->mipmap_count=(unsigned char) ReadBlobByte(image); |
| header->clut_type=(unsigned char) ReadBlobByte(image); |
| header->bpp_type=(unsigned char) ReadBlobByte(image); |
| |
| header->width=ReadBlobLSBShort(image); |
| header->height=ReadBlobLSBShort(image); |
| |
| header->GsTex0=ReadBlobMSBLongLong(image); |
| header->GsTex1=ReadBlobMSBLongLong(image); |
| header->GsRegs=ReadBlobMSBLong(image); |
| header->GsTexClut=ReadBlobMSBLong(image); |
| } |
| |
| static inline Quantum GetChannelValue(unsigned int word,unsigned char channel, |
| TIM2ColorEncoding ce) |
| { |
| switch(ce) |
| { |
| case RGBA16: |
| /* Documentation specifies padding with zeros for converting from 5 to 8 bits. */ |
| return ScaleCharToQuantum((word>>channel*5 & ~(~0x0U<<5))<<3); |
| case RGB24: |
| case RGBA32: |
| return ScaleCharToQuantum(word>>channel*8 & ~(~0x0U<<8)); |
| default: |
| return -1; |
| } |
| } |
| |
| static inline Quantum GetAlpha(unsigned int word,TIM2ColorEncoding ce) |
| { |
| switch(ce) |
| { |
| case RGBA16: |
| return ScaleCharToQuantum((word>>3*5&0x1F)==0?0:0xFF); |
| case RGBA32: |
| /* 0x80 -> 1.0 alpha. Multiply by 2 and clamp to 0xFF */ |
| return ScaleCharToQuantum(MagickMin((word>>3*8&0xFF)<<1,0xFF)); |
| default: |
| return 0xFF; |
| } |
| } |
| |
| static inline void deshufflePalette(Image *image,PixelInfo* oldColormap) |
| { |
| const size_t |
| pages=image->colors/32, /* Pages per CLUT */ |
| blocks=4, /* Blocks per page */ |
| colors=8; /* Colors per block */ |
| |
| int |
| page; |
| |
| size_t |
| i=0; |
| |
| (void) memcpy(oldColormap,image->colormap,(size_t)image->colors* |
| sizeof(*oldColormap)); |
| |
| /* |
| * Swap the 2nd and 3rd block in each page |
| */ |
| for (page=0; page < (ssize_t) pages; page++) |
| { |
| memcpy(&(image->colormap[i+1*colors]),&(oldColormap[i+2*colors]),colors* |
| sizeof(PixelInfo)); |
| memcpy(&(image->colormap[i+2*colors]),&(oldColormap[i+1*colors]),colors* |
| sizeof(PixelInfo)); |
| |
| i+=blocks*colors; |
| } |
| } |
| |
| static MagickBooleanType ReadTIM2ImageData(const ImageInfo *image_info, |
| Image *image,TIM2ImageHeader *header,char clut_depth,char bits_per_pixel, |
| ExceptionInfo *exception) |
| { |
| MagickBooleanType |
| status; |
| |
| ssize_t |
| x; |
| |
| Quantum |
| *q; |
| |
| unsigned char |
| *p; |
| |
| size_t |
| bits_per_line, |
| bytes_per_line; |
| |
| ssize_t |
| count, |
| y; |
| |
| unsigned char |
| *row_data; |
| |
| unsigned int |
| word; |
| |
| status=SetImageExtent(image,image->columns,image->rows,exception); |
| if (status == MagickFalse) |
| return(MagickFalse); |
| /* |
| * User data |
| */ |
| status=DiscardBlobBytes(image,header->header_size-48); |
| if (status == MagickFalse) |
| return(MagickFalse); |
| /* |
| * Image data |
| */ |
| bits_per_line=image->columns*bits_per_pixel; |
| bytes_per_line=bits_per_line/8 + ((bits_per_line%8==0) ? 0 : 1); |
| row_data=(unsigned char*) AcquireQuantumMemory(1,bytes_per_line); |
| if (row_data == (unsigned char *) NULL) |
| ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
| image_info->filename); |
| if (clut_depth != 0) |
| { |
| image->colors=header->clut_color_count; |
| if (AcquireImageColormap(image,image->colors,exception) == MagickFalse) |
| { |
| row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
| ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
| image_info->filename); |
| } |
| switch (bits_per_pixel) |
| { |
| case 4: |
| { |
| for (y=0; y<(ssize_t) image->rows; y++) |
| { |
| q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
| if (q == (Quantum *) NULL) |
| break; |
| count=ReadBlob(image,bytes_per_line,row_data); |
| if (count != (ssize_t) bytes_per_line) |
| { |
| row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
| ThrowBinaryException(CorruptImageError, |
| "InsufficientImageDataInFile",image_info->filename); |
| } |
| p=row_data; |
| for (x=0; x < ((ssize_t) image->columns-1); x+=2) |
| { |
| SetPixelIndex(image,(*p >> 0) & 0x0F,q); |
| q+=GetPixelChannels(image); |
| SetPixelIndex(image,(*p >> 4) & 0x0F,q); |
| p++; |
| q+=GetPixelChannels(image); |
| } |
| if ((image->columns % 2) != 0) |
| { |
| SetPixelIndex(image,(*p >> 4) & 0x0F,q); |
| p++; |
| q+=GetPixelChannels(image); |
| } |
| if (SyncAuthenticPixels(image,exception) == MagickFalse) |
| break; |
| if (image->previous == (Image *) NULL) |
| { |
| status=SetImageProgress(image,LoadImageTag, |
| (MagickOffsetType) y,image->rows); |
| if (status == MagickFalse) |
| break; |
| } |
| } |
| break; |
| } |
| case 8: |
| { |
| for (y=0;y<(ssize_t) image->rows; y++) |
| { |
| q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
| if (q == (Quantum *) NULL) |
| break; |
| count=ReadBlob(image,bytes_per_line,row_data); |
| if (count != (ssize_t) bytes_per_line) |
| { |
| row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
| ThrowBinaryException(CorruptImageError, |
| "InsufficientImageDataInFile",image_info->filename); |
| } |
| p=row_data; |
| for (x=0; x < (ssize_t) image->columns; x++) |
| { |
| SetPixelIndex(image,*p,q); |
| p++; |
| q+=GetPixelChannels(image); |
| } |
| if (SyncAuthenticPixels(image,exception) == MagickFalse) |
| break; |
| if (image->previous == (Image *) NULL) |
| { |
| status=SetImageProgress(image,LoadImageTag, |
| (MagickOffsetType) y,image->rows); |
| if (status == MagickFalse) |
| break; |
| } |
| } |
| break; |
| } |
| default: |
| { |
| row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
| ThrowBinaryException(CorruptImageError,"ImproperImageHeader", |
| image_info->filename); |
| } |
| } |
| } |
| else /* has_clut==false */ |
| { |
| switch (bits_per_pixel) |
| { |
| case 16: |
| { |
| for (y=0; y<(ssize_t) image->rows; y++) |
| { |
| q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
| if (q == (Quantum *) NULL) |
| break; |
| count=ReadBlob(image,bytes_per_line,row_data); |
| if (count != (ssize_t) bytes_per_line) |
| { |
| row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
| ThrowBinaryException(CorruptImageError, |
| "InsufficientImageDataInFile",image_info->filename); |
| } |
| p=row_data; |
| for (x=0; x < (ssize_t) image->columns; x++) |
| { |
| word = ((unsigned int)* p )<<0*8 | |
| ((unsigned int)*(p+1))<<1*8; |
| |
| SetPixelRed(image,GetChannelValue(word,0,RGBA16),q); |
| SetPixelGreen(image,GetChannelValue(word,1,RGBA16),q); |
| SetPixelBlue(image,GetChannelValue(word,2,RGBA16),q); |
| SetPixelAlpha(image,GetAlpha(word,RGBA16),q); |
| q+=GetPixelChannels(image); |
| p+=sizeof(unsigned short); |
| } |
| if (SyncAuthenticPixels(image,exception) == MagickFalse) |
| break; |
| if (image->previous == (Image *) NULL) |
| { |
| status=SetImageProgress(image,LoadImageTag, |
| (MagickOffsetType) y,image->rows); |
| if (status == MagickFalse) |
| break; |
| } |
| } |
| break; |
| } |
| case 24: |
| { |
| for (y = 0; y<(ssize_t) image->rows; y++) |
| { |
| q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
| if (q == (Quantum *) NULL) |
| break; |
| count=ReadBlob(image,bytes_per_line,row_data); |
| if (count != (ssize_t) bytes_per_line) |
| { |
| row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
| ThrowBinaryException(CorruptImageError, |
| "InsufficientImageDataInFile",image_info->filename); |
| } |
| p=row_data; |
| for (x=0; x < (ssize_t) image->columns; x++) |
| { |
| word = (unsigned int)(* p )<<0*8 | |
| (unsigned int)(*(p+1))<<1*8 | |
| (unsigned int)(*(p+2))<<2*8; |
| |
| SetPixelRed(image,GetChannelValue(word,0,RGB24),q); |
| SetPixelGreen(image,GetChannelValue(word,1,RGB24),q); |
| SetPixelBlue(image,GetChannelValue(word,2,RGB24),q); |
| q+=GetPixelChannels(image); |
| p+=3; |
| } |
| if (SyncAuthenticPixels(image,exception) == MagickFalse) |
| break; |
| if (image->previous == (Image *) NULL) |
| { |
| status=SetImageProgress(image,LoadImageTag, |
| (MagickOffsetType) y,image->rows); |
| if (status == MagickFalse) |
| break; |
| } |
| } |
| break; |
| } |
| case 32: |
| { |
| for (y = 0; y<(ssize_t) image->rows; y++) |
| { |
| q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
| if (q == (Quantum *) NULL) |
| break; |
| count=ReadBlob(image,bytes_per_line,row_data); |
| if (count != (ssize_t) bytes_per_line) |
| { |
| row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
| ThrowBinaryException(CorruptImageError, |
| "InsufficientImageDataInFile",image_info->filename); |
| } |
| p=row_data; |
| for (x=0; x < (ssize_t) image->columns; x++) |
| { |
| word = ((unsigned int)* p )<<0*8 | |
| ((unsigned int)*(p+1))<<1*8 | |
| ((unsigned int)*(p+2))<<2*8 | |
| ((unsigned int)*(p+3))<<3*8; |
| |
| SetPixelRed(image,GetChannelValue(word,0,RGBA32),q); |
| SetPixelGreen(image,GetChannelValue(word,1,RGBA32),q); |
| SetPixelBlue(image,GetChannelValue(word,2,RGBA32),q); |
| SetPixelAlpha(image,GetAlpha(word,RGBA32),q); |
| q+=GetPixelChannels(image); |
| p+=4; |
| } |
| if (SyncAuthenticPixels(image,exception) == MagickFalse) |
| break; |
| if (image->previous == (Image *) NULL) |
| { |
| status=SetImageProgress(image,LoadImageTag, |
| (MagickOffsetType) y,image->rows); |
| if (status == MagickFalse) |
| break; |
| } |
| } |
| break; |
| } |
| default: |
| { |
| row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
| ThrowBinaryException(CorruptImageError,"ImproperImageHeader", |
| image_info->filename); |
| } |
| } |
| } |
| row_data=(unsigned char *) RelinquishMagickMemory(row_data); |
| if ((status != MagickFalse) && (clut_depth != 0)) |
| { |
| CSM |
| csm; |
| |
| ssize_t |
| i; |
| |
| unsigned char |
| *clut_data; |
| |
| /* |
| * ### Read CLUT Data ### |
| */ |
| clut_data=(unsigned char *) AcquireQuantumMemory(1,header->clut_size); |
| if (clut_data == (unsigned char *) NULL) |
| ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
| image_info->filename); |
| count=ReadBlob(image,header->clut_size,clut_data); |
| if (count != (ssize_t) (header->clut_size)) |
| { |
| clut_data=(unsigned char *) RelinquishMagickMemory(clut_data); |
| ThrowBinaryException(CorruptImageError,"InsufficientImageDataInFile", |
| image_info->filename); |
| } |
| /* |
| * ### Process CLUT Data ### |
| */ |
| p=clut_data; |
| switch(clut_depth) |
| { |
| case 16: |
| { |
| for (i=0; i < (ssize_t) image->colors; i++) |
| { |
| word = ((unsigned short)* p )<<0*8 | |
| ((unsigned short)*(p+1))<<1*8; |
| |
| image->colormap[i].red=GetChannelValue(word,0,RGBA16); |
| image->colormap[i].green=GetChannelValue(word,1,RGBA16); |
| image->colormap[i].blue=GetChannelValue(word,2,RGBA16); |
| image->colormap[i].alpha=GetAlpha(word,RGBA16); |
| p+=2; |
| } |
| break; |
| } |
| case 24: |
| { |
| for (i=0; i < (ssize_t) image->colors; i++) |
| { |
| word = ((unsigned int)* p )<<0*8 | |
| ((unsigned int)*(p+1))<<1*8 | |
| ((unsigned int)*(p+2))<<2*8; |
| |
| image->colormap[i].red=GetChannelValue(word,0,RGB24); |
| image->colormap[i].green=GetChannelValue(word,1,RGB24); |
| image->colormap[i].blue=GetChannelValue(word,2,RGB24); |
| p+=3; |
| } |
| break; |
| } |
| case 32: |
| { |
| for (i=0; i < (ssize_t) image->colors; i++) |
| { |
| word = ((unsigned int)* p )<<0*8 | |
| ((unsigned int)*(p+1))<<1*8 | |
| ((unsigned int)*(p+2))<<2*8 | |
| ((unsigned int)*(p+3))<<3*8; |
| |
| image->colormap[i].red=GetChannelValue(word,0,RGBA32); |
| image->colormap[i].green=GetChannelValue(word,1,RGBA32); |
| image->colormap[i].blue=GetChannelValue(word,2,RGBA32); |
| image->colormap[i].alpha=GetAlpha(word,RGBA32); |
| p+=4; |
| } |
| break; |
| } |
| } |
| clut_data=(unsigned char *) RelinquishMagickMemory(clut_data); |
| /* CSM: CLUT Storage Mode */ |
| switch ((int) header->clut_type>>4) /* High 4 bits */ |
| { |
| case 0: |
| csm=CSM1; |
| break; |
| case 1: |
| csm=CSM2; |
| break; |
| default: |
| ThrowBinaryException(CorruptImageError,"ImproperImageHeader", |
| image_info->filename); |
| break; |
| } |
| if (csm==CSM1) |
| { |
| PixelInfo |
| *oldColormap; |
| |
| oldColormap=(PixelInfo *) AcquireQuantumMemory((size_t)(image->colors)+1, |
| sizeof(*image->colormap)); |
| if (oldColormap == (PixelInfo *) NULL) |
| ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
| image_info->filename); |
| deshufflePalette(image,oldColormap); |
| RelinquishMagickMemory(oldColormap); |
| } |
| } |
| return(status); |
| } |
| |
| static Image *ReadTIM2Image(const ImageInfo *image_info,ExceptionInfo *exception) |
| { |
| Image |
| *image; |
| |
| MagickBooleanType |
| status; |
| |
| ssize_t |
| str_read; |
| |
| TIM2FileHeader |
| file_header; |
| |
| /* |
| * Open image file. |
| */ |
| assert(image_info != (const ImageInfo *) NULL); |
| assert(image_info->signature == MagickCoreSignature); |
| |
| if (image_info->debug != MagickFalse) |
| (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", |
| image_info->filename); |
| assert(exception != (ExceptionInfo *) NULL); |
| assert(exception->signature == MagickCoreSignature); |
| image=AcquireImage(image_info,exception); |
| status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
| if (status == MagickFalse) |
| { |
| image=DestroyImageList(image); |
| return((Image *) NULL); |
| } |
| /* |
| * Verify TIM2 magic number. |
| */ |
| file_header.magic_num=ReadBlobMSBLong(image); |
| if (file_header.magic_num != 0x54494D32) /* "TIM2" */ |
| ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
| /* |
| * #### Read File Header #### |
| */ |
| file_header.format_vers=ReadBlobByte(image); |
| if (file_header.format_vers != 0x04) |
| ThrowReaderException(CoderError,"ImageTypeNotSupported"); |
| file_header.format_type=ReadBlobByte(image); |
| file_header.image_count=ReadBlobLSBShort(image); |
| ReadBlobStream(image,8,&(file_header.reserved),&str_read); |
| /* |
| * Jump to first image header |
| */ |
| switch(file_header.format_type) |
| { |
| case 0x00: |
| if (DiscardBlobBytes(image,16) == MagickFalse) |
| ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); |
| break; |
| case 0x01: |
| if (DiscardBlobBytes(image,128) == MagickFalse) |
| ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); |
| break; |
| default: |
| ThrowReaderException(CoderError,"ImageTypeNotSupported"); |
| } |
| /* |
| * Process each image. Only one image supported for now |
| */ |
| if (file_header.image_count != 1) |
| ThrowReaderException(CoderError,"NumberOfImagesIsNotSupported"); |
| for (int i=0; i < file_header.image_count; ++i) |
| { |
| char |
| clut_depth, |
| bits_per_pixel; |
| |
| TIM2ImageHeader |
| image_header; |
| |
| ReadTIM2ImageHeader(image,&image_header); |
| if (image_header.mipmap_count != 1) |
| ThrowReaderException(CoderError,"NumberOfImagesIsNotSupported"); |
| if (image_header.header_size < 48) |
| ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
| if ((MagickSizeType) image_header.image_size > GetBlobSize(image)) |
| ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); |
| if ((MagickSizeType) image_header.clut_size > GetBlobSize(image)) |
| ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); |
| image->columns=image_header.width; |
| image->rows=image_header.height; |
| clut_depth=0; |
| if (image_header.clut_type !=0) |
| { |
| switch((int) image_header.clut_type&0x0F) /* Low 4 bits */ |
| { |
| case 1: |
| clut_depth=16; |
| break; |
| case 2: |
| clut_depth=24; |
| break; |
| case 3: |
| clut_depth=32; |
| break; |
| default: |
| ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
| break; |
| } |
| } |
| switch ((int) image_header.bpp_type) |
| { |
| case 1: |
| bits_per_pixel=16; |
| break; |
| case 2: |
| bits_per_pixel=24; |
| break; |
| case 3: |
| bits_per_pixel=32; |
| break; |
| case 4: |
| bits_per_pixel=4; /* Implies CLUT */ |
| break; |
| case 5: |
| bits_per_pixel=8; /* Implies CLUT */ |
| break; |
| default: |
| ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
| break; |
| } |
| image->depth=(clut_depth != 0) ? clut_depth : bits_per_pixel; |
| if ((image->depth == 16) || (image->depth == 32)) |
| image->alpha_trait=BlendPixelTrait; |
| if (image->ping != MagickFalse) |
| { |
| status=ReadTIM2ImageData(image_info,image,&image_header,clut_depth, |
| bits_per_pixel,exception); |
| if (status==MagickFalse) |
| break; |
| } |
| if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0)) |
| if (image->scene >= (image_info->scene+image_info->number_scenes-1)) |
| break; |
| if ((image->storage_class == PseudoClass) && (EOFBlob(image) != MagickFalse)) |
| { |
| ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile", |
| image->filename); |
| break; |
| } |
| /* |
| Proceed to next image. |
| */ |
| if (image_info->number_scenes != 0) |
| if (image->scene >= (image_info->scene+image_info->number_scenes-1)) |
| break; |
| /* |
| Allocate next image structure. |
| */ |
| AcquireNextImage(image_info,image,exception); |
| if (GetNextImageInList(image) == (Image *) NULL) |
| { |
| status=MagickFalse; |
| break; |
| } |
| image=SyncNextImageInList(image); |
| status=SetImageProgress(image,LoadImagesTag,image->scene-1, |
| image->scene); |
| if (status == MagickFalse) |
| break; |
| } |
| (void) CloseBlob(image); |
| if (status == MagickFalse) |
| return(DestroyImageList(image)); |
| return(GetFirstImageInList(image)); |
| } |
| |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| % R e g i s t e r T I M 2 I m a g e % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % RegisterTIM2Image() adds attributes for the TIM2 image format to |
| % the list of supported formats. The attributes include the image format |
| % tag, a method to read and/or write the format, whether the format |
| % supports the saving of more than one frame to the same file or blob, |
| % whether the format supports native in-memory I/O, and a brief |
| % description of the format. |
| % |
| % The format of the RegisterTIM2Image method is: |
| % |
| % size_t RegisterTIM2Image(void) |
| % |
| */ |
| ModuleExport size_t RegisterTIM2Image(void) |
| { |
| MagickInfo |
| *entry; |
| |
| entry=AcquireMagickInfo("TIM2","TM2","PS2 TIM2"); |
| entry->decoder=(DecodeImageHandler *) ReadTIM2Image; |
| (void) RegisterMagickInfo(entry); |
| return(MagickImageCoderSignature); |
| } |
| |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| % U n r e g i s t e r T I M 2 I m a g e % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % UnregisterTIM2Image() removes format registrations made by the |
| % TIM2 module from the list of supported formats. |
| % |
| % The format of the UnregisterTIM2Image method is: |
| % |
| % UnregisterTIM2Image(void) |
| % |
| */ |
| ModuleExport void UnregisterTIM2Image(void) |
| { |
| (void) UnregisterMagickInfo("TM2"); |
| } |