brender-v1.1.2/FMT/LOADBMP.C
2022-05-04 18:14:23 -07:00

492 lines
11 KiB
C

/*
* 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 <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#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; i<NumColours; i++, palettesize+=3)
{
if(BrFileRead(&RGB3,1,sizeof(RGB3),fh) != sizeof(RGB3))
BR_ERROR1("Unable to read palette from '%s'",name);
((char *)(palette->pixels))[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; i<NumColours; i++,palettesize+=4)
{
if(BrFileRead(&RGB4,1,sizeof(RGB4),fh) != sizeof(RGB4))
BR_ERROR1("Unable to read palette from '%s'",name);
((char *)(palette->pixels))[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;i<pm->width;)
{
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;i<pm->width;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;i<RLEcontrol.data;i++)
mblock[dx++] = BrFileGetChar(fh);
if(i & 1) BrFileAdvance(1,fh);
break;
}
}
else
for(i=0;i<RLEcontrol.control;i++)
mblock[dx++] = RLEcontrol.data;
}
}
static void *Compressed_4(br_pixelmap *pm,int line,void *fh)
{
char i,pixel;
long dx,dy;
char *mblock;
long offset;
mblock = BrMemAllocate(pm->width,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);
}
}
}