/* * Copyright (c) 1993-1995 by Argonaut Technologies Limited. All rights reserved. * * $Id: loadbmp.c 1.4 1995/08/31 16:28:14 sam Exp $ * $Locker: $ * * Loader for Windows .BMP format * support for <=8 bit index palette based, 24 bit true colour * TODO 16,32 bit BI_BITFIELD compression (no compression!) */ #include #include #include #include #include #include "brender.h" #include "fmt.h" #include "fw.h" static char rscid[] = "$Id: loadbmp.c 1.4 1995/08/31 16:28:14 sam Exp $"; int FileNumCol(void *tmp); #define RED 2 #define GRN 1 #define BLU 0 #define PAD 3 #define BI_RGB 0L #define BI_RLE8 1L #define BI_RLE4 2L #define BI_BITFIELDS 3L #define WIDTHBYTES(i) ((i+31)/32*4) #define RLE_ESCAPE 0 #define RLE_EOL 0 #define RLE_EOF 1 #define RLE_JMP 2 static void *(*compressed_line_func)(br_pixelmap *pm,int line,void *fh); static void *Uncompressed_4(br_pixelmap *pm,int line,void *fh); static void *Uncompressed_8(br_pixelmap *pm,int line,void *fh); static void *Uncompressed_24(br_pixelmap *pm,int line,void *fh); static void *Compressed_8(br_pixelmap *pm,int line,void *fh); static void *Compressed_4(br_pixelmap *pm,int line,void *fh); #pragma pack (1) typedef struct { short bfType; int bfSize; /* size of image file */ short bfReserved1; short bfReserved2; int bfOffBits; /* size of header (including palette) */ } BITMAPFILEHEADER; typedef struct { int biSize; /* size of BITMAPINFOHEADER always 0x28 */ int biWidth; /* width in pixels */ int biHeight; /* depth in pixels */ short biPlanes; /* always 1 */ short biBitCount; /* bits per pixel: 1,4,8,16,24,32 */ int biCompression; /* always 0, however spec exists for compression*/ int biSizeImage; /* number of bytes in image */ int biXPelsPerMeter; /* resolution */ int biYPelsPerMeter; /* resolution */ int biClrUsed; /* Number of colours users, 0=all ie. 8 bit=256 */ int biClrImportant; /* Number of important colours? 0=all */ } BITMAPINFOHEADER, *ptrBITMAPINFOHEADER; typedef struct { int bcSize; /* size of BITMAPINFOHEADER always 0x0c */ short bcWidth; short bcHeight; short bcPlanes; short bcBitCount; } BITMAPCOREHEADER, *ptrBITMAPCOREHEADER; static struct { char control; char data; } RLEcontrol; static struct { char blue,green,red; } RGB3; static struct { char blue,green,red,pad; } RGB4; #pragma pack () br_pixelmap * BR_PUBLIC_ENTRY BrFmtBMPLoad(char *name,br_uint_32 flags) { void *fh; int open_mode = BR_FS_MODE_BINARY; br_pixelmap *pm,*palette; int headersize,palettesize; BITMAPINFOHEADER bi; BITMAPCOREHEADER bc; BITMAPFILEHEADER bf; int size; int NumColours; int dwWidth =0; int dwHeight=0; short wPlanes,wBitCount; br_uint_8 type; int i,j; br_uint_32 bytes_per_line; fh = BrFileOpenRead(name,0,NULL,&open_mode); if(fh == NULL) BR_ERROR1("Could not open '%s' for reading",name); if(BrFileRead(&bf,1,sizeof(bf),fh) != sizeof(bf)) BR_ERROR1("Unable to read header information from '%s'",name); /* * load header, check for sig. */ if(memcmp(&bf.bfType,"BM",2)) BR_ERROR1("'%s' is not a valid BMP file",name); if(BrFileRead(&bi,1,sizeof(bi),fh) != sizeof(bi)) BR_ERROR1("Unable to read bitmap info header from '%s'",name); NumColours = FileNumCol(&bi); palettesize = 0; /* * check for old/new header type */ switch(size = bi.biSize) { case(sizeof(BITMAPINFOHEADER)): headersize = sizeof(BITMAPFILEHEADER) + bi.biSize; break; case(sizeof(BITMAPCOREHEADER)): bc = *(BITMAPCOREHEADER*)&bi; headersize = sizeof(BITMAPFILEHEADER) + bc.bcSize; dwWidth = (int)bc.bcWidth; dwHeight = (int)bc.bcHeight; wPlanes = bc.bcPlanes; wBitCount = bc.bcBitCount; bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = dwWidth; bi.biHeight = dwHeight; bi.biPlanes = wPlanes; bi.biBitCount = wBitCount; bi.biCompression = BI_RGB; bi.biSizeImage = 0; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrUsed = NumColours; bi.biClrImportant = NumColours; BrFileAdvance(sizeof(BITMAPCOREHEADER) - sizeof(BITMAPINFOHEADER),fh); break; default:BR_ERROR1("'%s' is not a valid BMP file",name); } if(bi.biSizeImage == 0) bi.biSizeImage = WIDTHBYTES(bi.biWidth * bi.biBitCount) * bi.biHeight; if(bi.biClrUsed == 0) bi.biClrUsed = FileNumCol(&bi); /* * read palette */ switch(bi.biBitCount) { case(1):BR_ERROR1("1 bit BMP '%s' not supported",name); break; case(4): /* promote 4 to 8 bit */ case(8):switch(bi.biCompression) { case(BI_RGB):compressed_line_func = (bi.biBitCount == 4)?Uncompressed_4:Uncompressed_8; break; case(BI_RLE4):compressed_line_func = Compressed_4; break; case(BI_RLE8):compressed_line_func = Compressed_8; break; case(BI_BITFIELDS):BR_ERROR1("BMP compression type BITFIELDS not supported in '%s'",name); break; } type = BR_PMT_INDEX_8; palette=BrPixelmapAllocate(BR_PMT_RGBX_888,1,256,NULL,0); palette->identifier = BrResStrDup(palette, name); if(size == sizeof(BITMAPCOREHEADER)) for(i=0; ipixels))[i*4+RED] = RGB3.red; ((char *)(palette->pixels))[i*4+GRN] = RGB3.green; ((char *)(palette->pixels))[i*4+BLU] = RGB3.blue; ((char *)(palette->pixels))[i*4+PAD] = 0; } else for(i=0; ipixels))[i*4+RED] = RGB4.red; ((char *)(palette->pixels))[i*4+GRN] = RGB4.green; ((char *)(palette->pixels))[i*4+BLU] = RGB4.blue; ((char *)(palette->pixels))[i*4+PAD] = RGB4.pad; } break; case(16):BR_ERROR1("16 bit BMP '%s' not supported",name); break; case(24):if(bi.biCompression == BI_RGB) compressed_line_func = Uncompressed_24; else BR_ERROR1("24 bit compressed file '%s' not supported",name); type = BR_PMT_RGB_888; break; case(32):BR_ERROR1("32 bit BMP '%s' not supported",name); break; } if(bf.bfOffBits != 0) BrFileAdvance(bf.bfOffBits-headersize-palettesize,fh); pm = BrPixelmapAllocate(type,bi.biWidth,bi.biHeight,NULL,0); pm->identifier = BrResStrDup(pm, name); pm->map = (type == BR_PMT_INDEX_8)?palette:NULL; switch(bi.biCompression) { /* * No compression */ case(BI_RGB):for(i=bi.biHeight-1;i>=0;i--) if(compressed_line_func(pm,i,fh) == NULL) BR_ERROR1("Unable to read image data from '%s'",name); break; /* * Run length encoding */ case(BI_RLE4): case(BI_RLE8):if(compressed_line_func(pm,0,fh) == NULL) BR_ERROR1("Unable to read compressed image data from '%s'",name); break; case(BI_BITFIELDS): break; } BrFileClose(fh); return pm; } int FileNumCol(void *tmp) { int bits; ptrBITMAPINFOHEADER bi; ptrBITMAPCOREHEADER bc; bi = ((ptrBITMAPINFOHEADER)tmp); bc = ((ptrBITMAPCOREHEADER)tmp); if (bi->biSize != sizeof(BITMAPCOREHEADER)) { if(bi->biClrUsed != 0) return bi->biClrUsed; bits = bi->biBitCount; } else bits = bc->bcBitCount; switch(bits) { case(1):return 2; break; case(4):return 16; break; case(8):return 256; break; default:return 0; /* 24 bit has no palette */ } } static void *Uncompressed_4(br_pixelmap *pm,int line,void *fh) { int i,j; char c; for(i=0;iwidth;) { c = BrFileGetChar(fh); ((char *)(pm->pixels))[(line*pm->row_bytes)+i++] = (c >>4) & 0x0f; ((char *)(pm->pixels))[(line*pm->row_bytes)+i++] = (c & 0x0f); } if(((pm->width+1)/2) %4) BrFileAdvance(4-(((pm->width+1)/2) %4),fh); return pm; } static void *Uncompressed_8(br_pixelmap *pm,int line,void *fh) { if(BrFileRead(((char *)(pm->pixels)+line*pm->row_bytes),1,pm->width,fh) != pm->width) return NULL; if(pm->width %4) BrFileAdvance(4-(pm->width %4),fh); return pm; } static void *Uncompressed_24(br_pixelmap *pm,int line,void *fh) { int i; for(i=0;iwidth;i++) { if(BrFileRead(&RGB3,1,sizeof(RGB3),fh) != sizeof(RGB3)) return NULL; ((char *)(pm->pixels))[line*pm->row_bytes+i*3+RED] = RGB3.red; ((char *)(pm->pixels))[line*pm->row_bytes+i*3+GRN] = RGB3.green; ((char *)(pm->pixels))[line*pm->row_bytes+i*3+BLU] = RGB3.blue; } if((pm->width*3)%4) BrFileAdvance(4-((pm->width*3)%4),fh); return pm; } static void *Compressed_8(br_pixelmap *pm,int line,void *fh) { char i; long dx,dy; char *mblock; mblock = BrMemAllocate(pm->width,BR_MEMORY_SCRATCH); for(dx=0,dy=pm->height-1;;) { if(BrFileRead(&RLEcontrol,1,sizeof(RLEcontrol),fh) != sizeof(RLEcontrol)) { BrMemFree(mblock); return NULL; } if(RLEcontrol.control == RLE_ESCAPE) { switch(RLEcontrol.data) { case(RLE_EOF):BrMemFree(mblock); return pm; break; case(RLE_EOL):dx = 0; memcpy((char *)pm->pixels+ dy-- * pm->row_bytes,mblock,pm->width); break; case(RLE_JMP):dx += BrFileGetChar(fh); dy += BrFileGetChar(fh); break; default:for(i=0;iwidth,BR_MEMORY_SCRATCH); for(dx=0,dy=pm->height-1,offset=0;;) { if(BrFileRead(&RLEcontrol,1,sizeof(RLEcontrol),fh) != sizeof(RLEcontrol)) { BrMemFree(mblock); return NULL; } offset+=sizeof(RLEcontrol); if(RLEcontrol.control == RLE_ESCAPE) { switch(RLEcontrol.data) { case(RLE_EOF):BrMemFree(mblock); return pm; break; case(RLE_EOL):dx = 0; memcpy((char *)pm->pixels+ dy-- * pm->row_bytes,mblock,pm->width); if(dy == 0) { BrMemFree(mblock); return pm; } break; case(RLE_JMP):dx += BrFileGetChar(fh); dy += BrFileGetChar(fh); offset += 2; break; default:i=RLEcontrol.data; do { /* absolute */ pixel = BrFileGetChar(fh); offset++; mblock[dx++] = (pixel >> 4) & 0x0f; i--; if(i>0) { mblock[dx++] = (pixel & 0x0f); i--; } } while(i>0); if(offset & 1) { BrFileAdvance(offset & 1,fh); offset++; } break; } } else { /* encoded */ i=RLEcontrol.control; do { mblock[dx++] = (RLEcontrol.data >> 4) & 0x0f; i--; if(i>0) { mblock[dx++] = (RLEcontrol.data & 0x0f); i--; } } while(i>0); } } }