/* Copyright (C) 2000-2011 by George Williams */ /* * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "gimage.h" #include "gimagebmpP.h" #include GImage *_GImage_Create(enum image_type type, int32 width, int32 height); static int getshort(FILE *file) { int temp = getc(file); return( (getc(file)<<8) | temp ); } static long getl(FILE *file) { long temp1 = getc(file); long temp2 = getc(file); long temp3 = getc(file); return( (getc(file)<<24) | (temp3<<16) | (temp2<<8) | temp1 ); } static int bitshift(unsigned long mask) { int off=0, len=0, bit; if ( mask==0 ) return( 0 ); for ( off=0; !(mask&1); mask>>=1, ++off ); for ( len=0, bit=1; (mask&bit) && len<32; ++len, bit<<=1 ); return( off+(8-len) ); } static int fillbmpheader(FILE *file,struct bmpheader *head) { int i; memset(head,'\0',sizeof(*head)); if ( getc(file)!='B' || getc(file)!='M' ) return 0; /* Bad format */ head->size = getl(file); head->mbz1 = getshort(file); head->mbz2 = getshort(file); head->offset = getl(file); head->headersize = getl(file); if ( head->headersize==12 ) { /* Windows 2.0 format, also OS/2 */ head->width = getshort(file); head->height = getshort(file); head->planes = getshort(file); head->bitsperpixel = getshort(file); head->colorsused = 0; head->compression = 0; } else { head->width = getl(file); head->height = getl(file); head->planes = getshort(file); head->bitsperpixel = getshort(file); head->compression = getl(file); head->imagesize = getl(file); head->ignore1 = getl(file); head->ignore2 = getl(file); head->colorsused = getl(file); head->colorsimportant = getl(file); } if ( head->height<0 ) head->height = -head->height; else head->invert = true; if ( head->bitsperpixel!=1 && head->bitsperpixel!=4 && head->bitsperpixel!=8 && head->bitsperpixel!=16 && head->bitsperpixel!=24 && head->bitsperpixel!=32 ) return( 0 ); if ( head->compression==3 && ( head->bitsperpixel==16 || head->bitsperpixel==32 )) /* Good */; else if ( head->compression==0 && ( head->bitsperpixel<=8 || head->bitsperpixel==24 || head->bitsperpixel==32 )) /* Good */; else if ( head->compression==1 && head->bitsperpixel==8 ) /* Good */; else if ( head->compression==2 && head->bitsperpixel==4 ) /* Good */; else return( 0 ); if ( head->colorsused==0 ) head->colorsused = 1<bitsperpixel; if ( head->bitsperpixel>=16 ) head->colorsused = 0; if ( head->colorsused>(1<bitsperpixel) ) return( 0 ); for ( i=0; icolorsused; ++i ) { int b = getc(file), g = getc(file), r=getc(file); head->clut[i] = COLOR_CREATE(r,g,b); if ( head->headersize!=12 ) getc(file); } if ( head->compression==3 || head->headersize==108 ) { head->red_mask = getl(file); head->green_mask = getl(file); head->blue_mask = getl(file); head->red_shift = bitshift(head->red_mask); head->green_shift = bitshift(head->green_mask); head->blue_shift = bitshift(head->blue_mask); } if ( head->headersize==108 ) { getl(file); /* alpha_mask */ getl(file); /* color space type */ getl(file); /* redx */ getl(file); /* redy */ getl(file); /* redz */ getl(file); /* greenx */ getl(file); /* greeny */ getl(file); /* greenz */ getl(file); /* bluex */ getl(file); /* bluey */ getl(file); /* bluez */ getl(file); /* gammared */ getl(file); /* gammagreen */ getl(file); /* gammablue */ } return( 1 ); } static int readpixels(FILE *file,struct bmpheader *head) { int i,ii,j,ll,excess; fseek(file,head->offset,0); if ( head->bitsperpixel>=16 ) { head->int32_pixels = galloc(head->height* 4*head->width ); if ( head->int32_pixels==NULL ) return( 0 ); } else if ( head->bitsperpixel!=1 ) { head->byte_pixels = galloc(head->height* head->width ); if ( head->byte_pixels==NULL ) return( 0 ); } else { head->byte_pixels = galloc(head->height* ((head->width+7)/8) ); if ( head->byte_pixels==NULL ) return( 0 ); } ll = head->width; if ( head->bitsperpixel==8 && head->compression==0 ) { excess = ((ll+3)/4)*4 -ll; for ( i=0; iheight; ++i ) { fread(head->byte_pixels+i*ll,1,ll,file); for ( j=0; jbitsperpixel==8 ) { /* 8 bit RLE */ int ii = 0; while ( iiheight*head->width ) { int cnt = getc(file); if ( cnt!=0 ) { int ch = getc(file); while ( --cnt>=0 ) head->byte_pixels[ii++] = ch; } else { cnt = getc(file); if ( cnt>= 3 ) { int odd = cnt&1; while ( --cnt>=0 ) head->byte_pixels[ii++] = getc(file); if ( odd ) getc(file); } else if ( cnt==0 ) { /* end of scan line */ ii = ((ii+head->width-1)/head->width) * head->width; } else if ( cnt==1 ) { break; } else if ( cnt==2 ) { int x=getc(file); int y = getc(file); y += ii/head->width; x += ii%head->width; ii = y*head->width + x; } } } } else if ( head->bitsperpixel==4 && head->compression==0 ) { excess = (ll+1)/2; excess = ((excess+3)/4)*4 -excess; for ( i=0; iheight; ++i ) { ii = i*ll; for ( j=0; j<((head->width+7)/8)*8; j+=8 ) { int b1 = getc(file); int b2 = getc(file); int b3 = getc(file); int b4 = getc(file); head->byte_pixels[ii+j] = (b1>>4); if ( j+1byte_pixels[ii+j+1] = (b1&0xf); if ( j+2byte_pixels[ii+j+2] = (b2>>4); if ( j+3byte_pixels[ii+j+3] = (b2&0xf); if ( j+4byte_pixels[ii+j+4] = (b3>>4); if ( j+5byte_pixels[ii+j+5] = (b3&0xf); if ( j+6byte_pixels[ii+j+6] = (b4>>4); if ( j+7byte_pixels[ii+j+7] = (b4&0xf); } for ( j=0; jbitsperpixel==4 ) { /* 4 bit RLE */ int ii = 0; while ( iiheight*head->width ) { int cnt = getc(file); if ( cnt!=0 ) { int ch = getc(file); while ( (cnt-=2)>=-1 ) { head->byte_pixels[ii++] = ch>>4; head->byte_pixels[ii++] = ch&0xf; } if ( cnt==-1 ) --ii; } else { cnt = getc(file); if ( cnt>= 3 ) { int odd = cnt&2; while ( (cnt-=2)>=-1 ) { int ch = getc(file); head->byte_pixels[ii++] = ch>>4; head->byte_pixels[ii++] = ch&0xf; } if ( cnt==-1 ) --ii; if ( odd ) getc(file); } else if ( cnt==0 ) { /* end of scan line */ ii = ((ii+head->width-1)/head->width) * head->width; } else if ( cnt==1 ) { break; } else if ( cnt==2 ) { int x=getc(file); int y = getc(file); y += ii/head->width; x += ii%head->width; ii = y*head->width + x; } } } } else if ( head->bitsperpixel==1 ) { excess = (ll+7)/8; excess = ((excess+3)/4)*4 -excess; for ( i=0; iheight; ++i ) { ii = i*((ll+7)/8); for ( j=0; j<((head->width+7)/8); ++j ) { head->byte_pixels[ii+j] = getc(file); } for ( j=0; jbitsperpixel==24 ) { excess = ((3*head->width+3)/4)*4 - 3*head->width; for ( i=0; iheight; ++i ) { ii = i*head->width; for ( j=0; jwidth; ++j ) { int b = getc(file); int g = getc(file); int r = getc(file); head->int32_pixels[ii+j] = COLOR_CREATE(r,g,b); } for ( j=0; jbitsperpixel==32 && head->compression==0 ) { for ( i=0; iheight; ++i ) { ii = i*head->width; for ( j=0; jwidth; ++j ) { int b,g,r; b = getc(file); g = getc(file); r = getc(file); (void) getc(file); /* Ignore the alpha channel */ head->int32_pixels[ii+j] = COLOR_CREATE(r,g,b); } } } else if ( head->bitsperpixel==16 ) { for ( i=0; iheight; ++i ) { ii = i*head->width; for ( j=0; jwidth; ++j ) { int pix = getshort(file); head->int32_pixels[ii+j] = COLOR_CREATE((pix&head->red_mask)>>head->red_shift, (pix&head->green_mask)>>head->green_shift, (pix&head->blue_mask)>>head->blue_shift); } } if ( head->width&1 ) getshort(file); } else if ( head->bitsperpixel==32 ) { for ( i=0; iheight; ++i ) { ii = i*head->width; for ( j=0; jwidth; ++j ) { int pix = getl(file); head->int32_pixels[ii+j] = COLOR_CREATE((pix&head->red_mask)>>head->red_shift, (pix&head->green_mask)>>head->green_shift, (pix&head->blue_mask)>>head->blue_shift); } } } if ( feof(file )) { /* Did we get an incomplete file? */ if ( head->bitsperpixel>=16 ) gfree( head->int32_pixels ); else gfree( head->byte_pixels ); return( 0 ); } return( 1 ); } GImage *GImageRead_Bmp(FILE *file) { struct bmpheader bmp; int i,l; GImage *ret; struct _GImage *base; if ( file==NULL ) return( NULL ); if ( !fillbmpheader(file,&bmp)) return( NULL ); if ( !readpixels(file,&bmp)) return( NULL ); if ( !bmp.invert ) { ret = _GImage_Create(bmp.bitsperpixel>=16?it_true:bmp.bitsperpixel!=1?it_index:it_mono, bmp.width, bmp.height); if ( bmp.bitsperpixel>=16 ) { ret->u.image->data = (uint8 *) bmp.int32_pixels; } else if ( bmp.bitsperpixel!=1 ) { ret->u.image->data = (uint8 *) bmp.byte_pixels; } } else { if ( bmp.bitsperpixel>=16 ) { ret = GImageCreate(it_true,bmp.width, bmp.height); base = ret->u.image; for ( i=0; idata+l*base->bytes_per_line,bmp.int32_pixels+i*bmp.width,bmp.width*sizeof(uint32)); } gfree( bmp.int32_pixels ); } else if ( bmp.bitsperpixel!=1 ) { ret = GImageCreate(it_index,bmp.width, bmp.height); base = ret->u.image; for ( i=0; idata+l*base->bytes_per_line,bmp.byte_pixels+i*bmp.width,bmp.width); } gfree( bmp.byte_pixels ); } else { ret = GImageCreate(it_mono,bmp.width, bmp.height); base = ret->u.image; for ( i=0; idata+l*base->bytes_per_line,bmp.byte_pixels+i*base->bytes_per_line,base->bytes_per_line); } gfree( bmp.byte_pixels ); } } if ( ret->u.image->image_type==it_index ) { ret->u.image->clut->clut_len = bmp.colorsused; memcpy(ret->u.image->clut->clut,bmp.clut,bmp.colorsused*sizeof(Color)); ret->u.image->clut->trans_index = COLOR_UNKNOWN; } else if ( ret->u.image->image_type==it_mono && bmp.colorsused!=0 ) { ret->u.image->clut = gcalloc(1,sizeof(GClut)); ret->u.image->clut->clut_len = bmp.colorsused; memcpy(ret->u.image->clut->clut,bmp.clut,bmp.colorsused*sizeof(Color)); ret->u.image->clut->trans_index = COLOR_UNKNOWN; } return( ret ); } GImage *GImageReadBmp(char *filename) { FILE *file = fopen(filename,"rb"); GImage *ret; if ( file==NULL ) return( NULL ); ret = GImageRead_Bmp(file); fclose(file); return( ret ); }