Hi all,
on the weekend I noticed some regression in the BMP loader - some 8-bit (paletted) files
wouldn't load anymore but simply crash.
After taking a look at the current state of the BMP loader I decided it might be worth a
shot at reimplementing that part. For example: the current loader doesn't properly handle
1- and 4-bit files, incorrectly loads 16-bit files as intensity-alpha (they are RGB555),
is full of dead code, and generally not in very good shape.
Attached is my re-implementation for review.
I've checked it against the test images from http://wvnvaxa.wvnet.edu/vmswww/bmp.html and
models that use BMP files.
Cheers,
/ulrich
#include <osg/Image>
#include <osg/Notify>
#include <osg/Geode>
#include <osg/Image>
#include <osg/GL>
#include <osgDB/Registry>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/fstream>
#include <vector>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef int int32;
typedef unsigned int uint32;
/****************************************************************************
*
* Follows is code written by GWM and translated to fit with the OSG Ethos.
*
*
* Ported into the OSG as a plugin, Geoff Michel October 2001.
* For patches, bugs and new features
* please send them direct to the OSG dev team.
*
**********************************************************************/
enum BMPError
{
ERROR_NO_ERROR = 0,
ERROR_READING_HEADER,
ERROR_READING_PALETTE,
ERROR_MEMORY,
ERROR_READ_ERROR,
ERROR_NO_FILE,
ERROR_READING_COLORS
};
// BMP format bits - at start of file is 512 bytes of pure garbage
enum ftype {MB=19778}; // magic number identifies a bmp file; actually chars
'B''M'
// allowed ftypes are 'BM' for windoze; OS2 allows:
//'BA' - Bitmap Array
//'CI' - Color Icon
//'CP' - Color Pointer (mouse cursor)
//'IC' - Icon
//'PT' - Pointer (mouse cursor)
enum ncol { BW=1, IA, RGB, RGBA};
struct bmpheader {
short FileType; //always MB
unsigned short siz[2]; // a dword for whole file size - make unsigned Feb
2002
short Reserved1, Reserved2; //reserved for future purposes
unsigned short offset[2]; //offset to image in bytes
};
struct BMPInfo {
int32 width; //width of the image in pixels
int32 height; // height of the image in pixels
short planes; //:word: number of planes (always 1)
short Colorbits; //word: number of bits used to describe color in
each pixel
int32 compression; //compression used
int32 ImageSize; //image size in bytes
int32 XpixPerMeter; //pixels per meter in X
int32 YpixPerMeter; //pixels per meter in Y
int32 ColorUsed; //number of colors used
int32 Important; //number of "important" colors
//unsigned char rgbquad[4];
//long os2stuff[6]; // storage for os2.1 with 64 bytes to be read. Dont
know what these are yet.
};
static int bmpErrorString(int bmperr, char *buffer, int bufferlen)
{
switch (bmperr)
{
case ERROR_READING_COLORS:
strncpy(buffer, "BMP loader: Error reading colours", bufferlen);
break;
case ERROR_READING_HEADER:
strncpy(buffer, "BMP loader: Error reading header", bufferlen);
break;
case ERROR_READING_PALETTE:
strncpy(buffer, "BMP loader: Error reading palette", bufferlen);
break;
case ERROR_MEMORY:
strncpy(buffer, "BMP loader: Out of memory error", bufferlen);
break;
case ERROR_READ_ERROR:
strncpy(buffer, "BMP loader: Read error", bufferlen);
break;
default:
*buffer = '\0';
}
return bmperr;
}
/* byte order workarounds *sigh* */
static void swapbyte(int32 *i)
{
char *vv=(char *)i;
char tmp=vv[0];
vv[0]=vv[3];
vv[3]=tmp;
tmp=vv[1];
vv[1]=vv[2];
vv[2]=tmp;
}
static void swapbyte(uint32 *i)
{
char *vv=(char *)i;
char tmp=vv[0];
vv[0]=vv[3];
vv[3]=tmp;
tmp=vv[1];
vv[1]=vv[2];
vv[2]=tmp;
}
static void swapbyte(float *i)
{
char *vv=(char *)i;
char tmp=vv[0];
vv[0]=vv[3];
vv[3]=tmp;
tmp=vv[1];
vv[1]=vv[2];
vv[2]=tmp;
}
static void swapbyte(unsigned short *i)
{
char *vv=(char *)i;
char tmp=vv[0];
vv[0]=vv[1];
vv[1]=tmp;
}
static void swapbyte(short *i)
{
char *vv=(char *)i;
char tmp=vv[0];
vv[0]=vv[1];
vv[1]=tmp;
}
#if 1
static unsigned char *bmp_load(std::istream& fin,
int *width_ret,
int *height_ret,
int *numComponents_ret)
{
// actual file size
fin.seekg(0, std::ios::end);
size_t actFileSize = fin.tellg();
fin.seekg(0, std::ios::beg);
bool swap;
/*
* BMP header
*/
struct BMPHeader {
unsigned short magic; // 'BM' aka 0x424D
unsigned int fileSize;
unsigned short reserved1;
unsigned short reserved2;
unsigned int imageOffset;
};
BMPHeader bmp;
{
/*
* read each part individually to avoid struct packing issues (and
#pragma)
*/
fin.read((char*) &bmp.magic, sizeof(bmp.magic));
fin.read((char*) &bmp.fileSize, sizeof(bmp.fileSize));
fin.read((char*) &bmp.reserved1, sizeof(bmp.reserved1));
fin.read((char*) &bmp.reserved2, sizeof(bmp.reserved2));
fin.read((char*) &bmp.imageOffset, sizeof(bmp.imageOffset));
bmp.magic = ntohs(bmp.magic);
if (bmp.magic != 0x424D && bmp.magic != 0x4D42) {
osg::notify(osg::WARN) << "Not a BMP file\n";
return 0;
}
swap = (bmp.magic == 0x4D42);
if (swap) {
osg::notify(osg::DEBUG_INFO) << "swap=" << swap << std::endl;
swapbyte(&bmp.fileSize);
swapbyte(&bmp.imageOffset);
}
if (bmp.fileSize != actFileSize) {
osg::notify(osg::DEBUG_INFO) << "Stored fileSize=" << bmp.fileSize
<< " != actual=" << actFileSize << std::endl;
bmp.fileSize = actFileSize;
}
}
/*
* Windows V3 header
*/
enum BMPCOMPRESSION {
BI_RGB = 0,
BI_RLE8,
BI_RLE4,
BI_BITFIELDS,
BI_JPEG,
BI_PNG
};
struct BITMAPINFOHEADER {
//unsigned int hdrSize;
int width, height;
unsigned short colorPlanes;
unsigned short bitsPerPixel;
unsigned int compression;
unsigned int imageSize;
int horizontalPixelPerMeter;
int verticalPixelPerMeter;
unsigned int numColorsInPalette;
unsigned int numImportantColors;
};
BITMAPINFOHEADER dib;
unsigned int dibHdrSize;
/*
* read DIB header
*/
fin.read((char*) &dibHdrSize, sizeof(dibHdrSize));
if (swap) swapbyte(&dibHdrSize);
if (dibHdrSize == 12) {
/*
* OS/2 V1 header
*/
struct BITMAPCOREHEADER {
//unsigned int hdrSize;
unsigned short width, height;
unsigned short colorPlanes;
unsigned short bitsPerPixel;
};
BITMAPCOREHEADER hdr;
unsigned int expectHdrSize = sizeof(hdr) + sizeof(dibHdrSize);
if (expectHdrSize != dibHdrSize) {
osg::notify(osg::WARN) << "Invalid header size " << expectHdrSize
<< " != " << dibHdrSize << std::endl;
return 0;
}
fin.read((char*) &hdr, sizeof(hdr));
if (swap) {
swapbyte(&hdr.width);
swapbyte(&hdr.height);
swapbyte(&hdr.colorPlanes);
swapbyte(&hdr.bitsPerPixel);
}
// convert to BITMAPINFOHEADER
memset(&dib, 0, sizeof(dib));
dib.width = hdr.width;
dib.height = hdr.height;
dib.colorPlanes = hdr.colorPlanes;
dib.bitsPerPixel = hdr.bitsPerPixel;
} else if (dibHdrSize == 40 || dibHdrSize == 108 || dibHdrSize == 124) {
/*
* Windows V3/V4/V5 header
* reads only the common part (i.e. V3) and skips over the rest
*/
#if 0
unsigned int expectHdrSize = sizeof(dib) + sizeof(dibHdrSize);
if (expectHdrSize != dibHdrSize) {
osg::notify(osg::WARN) << "Invalid header size " << expectHdrSize
<< " != " << dibHdrSize << std::endl;
return 0;
}
#endif
fin.read((char*) &dib, sizeof(dib));
if (swap) {
swapbyte(&dib.width);
swapbyte(&dib.height);
swapbyte(&dib.colorPlanes);
swapbyte(&dib.bitsPerPixel);
swapbyte(&dib.compression);
swapbyte(&dib.imageSize);
swapbyte(&dib.numColorsInPalette);
swapbyte(&dib.numImportantColors);
}
} else {
osg::notify(osg::WARN) << "Unsupported header size=" << dibHdrSize <<
std::endl;
return 0;
}
// sanity checks
if (dib.height < 0) {
osg::notify(osg::WARN) << "Image is upside-down\n";
dib.height *= -1;
}
if (dib.colorPlanes != 1) {
osg::notify(osg::WARN) << "Invalid number of color planes=" <<
dib.colorPlanes << std::endl;
return 0;
}
if (dib.bitsPerPixel == 0) {
osg::notify(osg::WARN) << "Invalid bits/pixel=" << dib.bitsPerPixel <<
std::endl;
return 0;
}
if (dib.compression != BI_RGB && dib.compression != BI_BITFIELDS) {
osg::notify(osg::WARN) << "Unsupported BMP compression=" <<
dib.compression << std::endl;
return 0;
}
/*
* color mask - UNUSED
*/
unsigned int redMask, greenMask, blueMask;
if (dib.bitsPerPixel == 16 && dib.compression == BI_BITFIELDS) {
fin.read((char*) &redMask, sizeof(redMask));
fin.read((char*) &greenMask, sizeof(greenMask));
fin.read((char*) &blueMask, sizeof(blueMask));
if (swap) {
swapbyte(&redMask);
swapbyte(&greenMask);
swapbyte(&blueMask);
}
//printf("redMask=%04x greenMask=%04x blueMask=%04x\n", redMask,
greenMask, blueMask);
}
unsigned int imageBytesPerPixel = 0;
/*
* color palette
*/
std::vector<unsigned char> colorPalette;
if (dib.bitsPerPixel < 16) {
// defaults to 2^n
if (dib.numColorsInPalette == 0) {
dib.numColorsInPalette = 1 << dib.bitsPerPixel;
}
// allocate/read color palette
imageBytesPerPixel = (dibHdrSize == 12 ? 3 : 4); // OS/2v1 stores RGB,
else RGBA
colorPalette.resize(dib.numColorsInPalette * imageBytesPerPixel);
fin.read((char*) &*colorPalette.begin(), colorPalette.size());
} else {
if (dib.bitsPerPixel == 16) {
imageBytesPerPixel = 3;
} else if (dib.bitsPerPixel == 24 || dib.bitsPerPixel == 32) {
imageBytesPerPixel = dib.bitsPerPixel / 8;
}
}
unsigned int where = fin.tellg();
if (where != bmp.imageOffset) {
osg::notify(osg::WARN) << "BMP streampos out-of-sync where=" << where
<< " imageOffset=" << bmp.imageOffset << std::endl;
fin.seekg(bmp.imageOffset, std::ios::beg); // seek to imageOffset and
hope for the best
//return 0;
}
/*
* image data
*/
const unsigned int imageBytesPerRow = dib.width * imageBytesPerPixel;
const unsigned int imageBufferSize = imageBytesPerRow * dib.height;
unsigned char* imageBuffer = new unsigned char[imageBufferSize];
//printf("imageBytesPerPixel=%u imageBytesPerRow=%u\n", imageBytesPerPixel,
imageBytesPerRow);
// byte/row in BMP image data
unsigned int bytesPerPixel;
unsigned int bytesPerRow;
if (dib.bitsPerPixel >= 8) {
bytesPerPixel = dib.bitsPerPixel / 8;
bytesPerRow = dib.width * bytesPerPixel;
} else {
bytesPerPixel = 1;
bytesPerRow = (unsigned int) (dib.width * (dib.bitsPerPixel / 8.0f));
}
const unsigned int bytesPerRowAlign = (unsigned int) ceilf(bytesPerRow /
4.0f) * 4;
//printf("bytesPerPixel=%u bytesPerRow=%u bytesPerRowAlign=%u\n",
bytesPerPixel, bytesPerRow, bytesPerRowAlign);
std::vector<unsigned char> rowBuffer;
rowBuffer.resize(bytesPerRowAlign);
if (dib.bitsPerPixel >= 16) {
unsigned char* imgp = imageBuffer;
for (unsigned int i = 0; i < dib.height; ++i) {
// read row
unsigned char* rowp = &*rowBuffer.begin();
fin.read((char*) rowp, rowBuffer.size());
// copy to image buffer, swap/unpack BGR to RGB(A)
for (unsigned int j = 0; j < bytesPerRow; j += bytesPerPixel) {
if (dib.bitsPerPixel == 16) {
// 16-bit RGB555 -> 24-bit RGB
unsigned short rgb555 = (rowp[1] << 8) | rowp[0];
if (swap) swapbyte(&rgb555);
imgp[0] = ((rgb555 >> 10) & 0x1f) << 3;
imgp[1] = ((rgb555 >> 5) & 0x1f) << 3;
imgp[2] = (rgb555 & 0x1f) << 3;
} else {
// BGR -> RGB(A)
imgp[0] = rowp[2];
imgp[1] = rowp[1];
imgp[2] = rowp[0];
if (imageBytesPerPixel == 4) {
imgp[3] = 0xff;
//imgp[3] = (bytesPerPixel == 4 ? rowp[3] : 0xff);
}
}
imgp += imageBytesPerPixel;
rowp += bytesPerPixel;
}
}
} else {
const unsigned int idxPerByte = 8 / dib.bitsPerPixel; // color indices
per byte
const unsigned int idxMask = (1 << dib.bitsPerPixel) - 1; // index mask
//printf("idxPerByte=%d idxMask=%02x\n", idxPerByte, idxMask);
unsigned char* imgp = imageBuffer;
for (unsigned int i = 0; i < dib.height; ++i) {
// read row
unsigned char* rowp = &*rowBuffer.begin();
fin.read((char*) rowp, rowBuffer.size());
unsigned int j = 0;
while (j < dib.width) {
// unpack bytes/indices to image buffer
unsigned char val = rowp[0];
for (unsigned int k = 0; k < idxPerByte && j < dib.width; ++k,
++j) {
unsigned int idx = (val >> ((idxPerByte-1-k) *
dib.bitsPerPixel)) & idxMask;
idx *= imageBytesPerPixel;
imgp[0] = colorPalette[idx+2];
imgp[1] = colorPalette[idx+1];
imgp[2] = colorPalette[idx+0];
if (imageBytesPerPixel == 4) {
imgp[3] = 0xff;
//imgp[3] = (bytesPerPixel == 4 ? colorPalette[idx+4] :
0xff);
}
imgp += imageBytesPerPixel;
}
++rowp;
}
}
}
// return result
*width_ret = dib.width;
*height_ret = dib.height;
*numComponents_ret = imageBytesPerPixel;
return imageBuffer;
}
#else
static unsigned char *bmp_load(std::istream& fin,
int *width_ret,
int *height_ret,
int *numComponents_ret)
{ // the main area of changes from the pic format loader.
// reads filename, and returns the buffer
// bmp is very very simple format
// even Master Gates could have invented it.
// It is extremely expensive on disk space - every RGB pixel uses 3 bytes
plus a header!
// BMP - sponsored by Seagate.
// unsigned char palette[256][3];
unsigned char *buffer=NULL; // returned to sender & as read from the disk
long filelen;
bmperror = ERROR_NO_FILE;
fin.seekg(0, std::ios::end);
filelen = fin.tellg(); // determine file size so we can fill it in later if
FileSize == 0
fin.seekg(0, std::ios::beg);
int ncolours;
int ncomp=0;
bool swap=false; // dont need to swap bytes
// actual size of the bitmap header; 12=os2; 40 = normal; 64=os2.1
struct bmpheader hd;
struct BMPInfo inf;
bmperror = ERROR_NO_ERROR;
fin.read((char*)&hd,sizeof(bmpheader));
if (hd.FileType != MB) {
swapbyte(&(hd.FileType));
swap=true;
if (hd.FileType != MB) {
bmperror=ERROR_READING_HEADER;
}
}
if (hd.FileType == MB) {
int32 infsize; //size of BMPinfo in bytes
unsigned char *cols=NULL; // dynamic colour palette
unsigned char *imbuff; // returned to sender & as read from the disk
fin.read((char*)&infsize,sizeof(int32)); // insert inside 'the file is
bmp' clause
if (swap) swapbyte(&infsize);
unsigned char *hdr=new unsigned char[infsize]; // to hold the new header
fin.read((char*)hdr,infsize-sizeof(int32));
int32 hsiz=sizeof(inf); // minimum of structure size &
if(infsize<=hsiz) hsiz=infsize;
memcpy(&inf,hdr, hsiz/*-sizeof(long)*/); // copy only the bytes I can
cope with
delete [] hdr;
osg::notify(osg::INFO) << "loading bmp file "<<swap<<" "<<infsize<< "
"<<sizeof(inf) << " "<<sizeof(bmpheader) << std::endl;
if (swap) { // inverse the field of the header which need swapping
swapbyte(&hd.siz[0]);
swapbyte(&hd.siz[1]);
swapbyte(&inf.Colorbits);
swapbyte(&inf.width);
swapbyte(&inf.height);
swapbyte(&inf.ImageSize);
swapbyte(&inf.ColorUsed);
}
if (infsize==12) { // os2, protect us from our friends ? || infsize==64
int wd = inf.width&0xffff; // shorts replace longs
int ht = inf.width>>16;
int npln = inf.height&0xffff; // number of planes
int cbits = inf.height>>16;
inf.width=wd;
inf.height=ht;
inf.planes=npln;
inf.Colorbits=cbits;
inf.ColorUsed=(int32)pow(2.0,(double)inf.Colorbits); // infer the
colours
}
osg::notify(osg::INFO) << "readbmp " <<inf.width<< " "<<inf.height <<
std::endl;
// previous size calculation, see new calcs below.
int32 size_prev = hd.siz[1]+hd.siz[0]*65536;
osg::notify(osg::INFO) << "previous size calc = "<<size_prev<<"
hd.siz[1]="<<hd.siz[1]<<" hd.siz[0]="<<hd.siz[0]<<std::endl;
// order of size calculation swapped, by Christo Zietsman to fix size
bug.
int32 size = hd.siz[1]*65536+hd.siz[0];
osg::notify(osg::INFO) << "new size calc = "<<size<<"
hd.siz[1]="<<hd.siz[1]<<" hd.siz[0]="<<hd.siz[0]<<std::endl;
// handle size==0 in uncompressed 24-bit BMPs -Eric Hammil
if (size==0) size = filelen;
osg::notify(osg::INFO) << "size after zero correction = "<<size<<"
hd.siz[1]="<<hd.siz[1]<<" hd.siz[0]="<<hd.siz[0]<<std::endl;
int ncpal=4; // default number of colours per palette entry
size -= sizeof(bmpheader)+infsize;
if (inf.ImageSize<size) inf.ImageSize=size;
imbuff = new unsigned char [ inf.ImageSize]; // read from disk
fin.read((char*)imbuff,sizeof(unsigned char)*inf.ImageSize);
ncolours=inf.Colorbits/8;
switch (ncolours) {
case 1:
ncomp = BW; // actually this is a 256 colour, paletted image
inf.Colorbits=8; // so this is how many bits there are per index
//inf.ColorUsed=256; // and number of colours used
if(!inf.ColorUsed) inf.ColorUsed=256; /*the bitmap has 256 colours if
ColorUsed = 0 otherwise as many as stored in ColorUsed*/
cols=imbuff; // colour palette address - uses 4 bytes/colour
break;
case 2:
ncomp = IA;
break;
case 3:
ncomp = RGB;
break;
case 4:
ncomp = RGBA;
break;
default:
cols=imbuff; // colour palette address - uses 4 bytes/colour
if (infsize==12 || infsize==64) ncpal=3; // OS2 - uses 3 colours
per palette entry
else ncpal=4; // Windoze uses 4!
}
if (ncomp>0) buffer = new unsigned char
[(ncomp==BW?3:ncomp)*inf.width*inf.height]; // to be returned
else buffer = new unsigned char [ 3*inf.width*inf.height]; // default
full colour to be returned
uint32 off=0;
uint32 rowbytes=ncomp*sizeof(unsigned char)*inf.width;
uint32 doff=(rowbytes)/4;
if ((rowbytes%4)) doff++; // round up if needed
doff*=4; // to find dword alignment
for(int j=0; j<inf.height; j++) {
if (ncomp>BW) memcpy(buffer+j*rowbytes, imbuff+off, rowbytes); //
pack bytes closely
else { // find from the palette..
unsigned char *imptr=imbuff+inf.ColorUsed*ncpal; // add size of
the palette- start of image
int npixperbyte=8/inf.Colorbits; // no of pixels per byte
for (int ii=0; ii<inf.width/npixperbyte; ii++) {
unsigned char mask=0x00; // masked with index to extract
colorbits bits
unsigned char byte=imptr[(j*doff/npixperbyte)+ii];
int jj;
for (jj=0; jj<inf.Colorbits; jj++) mask |= (0x80>>jj); //
fill N High end bits
for (jj=0; jj<npixperbyte; jj++) {
int
colidx=(byte&mask)>>((npixperbyte-1-jj)*inf.Colorbits);
buffer[3*(j*inf.width+ii*npixperbyte+jj)+0]=cols[ncpal*colidx+2];
buffer[3*(j*inf.width+ii*npixperbyte+jj)+1]=cols[ncpal*colidx+1];
buffer[3*(j*inf.width+ii*npixperbyte+jj)+2]=cols[ncpal*colidx];
mask>>=inf.Colorbits;
}
}
}
off+=doff;
if (ncomp>2) { // yes bill, colours are usually BGR aren't they
for(int i=0; i<inf.width; i++) {
int ijw=i+j*inf.width;
unsigned char blu=buffer[3*ijw+0];
buffer[3*ijw+0]=buffer[3*ijw+2]; // swap order of colours
buffer[3*ijw+2]=blu;
}
}
}
delete [] imbuff; // free the on-disk storage
}
else // else error in header
{
return NULL;
}
*width_ret = inf.width;
*height_ret = inf.height;
switch (ncomp) {
case BW:
*numComponents_ret = 3;
break;
case IA:
case RGB:
case RGBA:
*numComponents_ret = ncomp;
break;
default:
*numComponents_ret = 3;
break;
}
return buffer;
}
#endif
class ReaderWriterBMP : public osgDB::ReaderWriter
{
public:
ReaderWriterBMP()
{
supportsExtension("bmp","BMP Image format");
}
virtual const char* className() const { return "BMP Image Reader"; }
ReadResult readBMPStream(std::istream& fin) const
{
unsigned char *imageData = NULL;
int width_ret;
int height_ret;
int numComponents_ret;
imageData = bmp_load(fin,&width_ret,&height_ret,&numComponents_ret);
if (imageData==NULL) return ReadResult::FILE_NOT_HANDLED;
int s = width_ret;
int t = height_ret;
int r = 1;
int internalFormat = numComponents_ret;
unsigned int pixelFormat =
numComponents_ret == 1 ? GL_LUMINANCE :
numComponents_ret == 2 ? GL_LUMINANCE_ALPHA :
numComponents_ret == 3 ? GL_RGB :
numComponents_ret == 4 ? GL_RGBA : (GLenum)-1;
unsigned int dataType = GL_UNSIGNED_BYTE;
osg::Image* pOsgImage = new osg::Image;
pOsgImage->setImage(s,t,r,
internalFormat,
pixelFormat,
dataType,
imageData,
osg::Image::USE_NEW_DELETE);
return pOsgImage;
}
virtual ReadResult readObject(std::istream& fin,const
osgDB::ReaderWriter::Options* options =NULL) const
{
return readImage(fin, options);
}
virtual ReadResult readObject(const std::string& file, const
osgDB::ReaderWriter::Options* options =NULL) const
{
return readImage(file, options);
}
virtual ReadResult readImage(std::istream& fin,const Options* =NULL)
const
{
return readBMPStream(fin);
}
virtual ReadResult readImage(const std::string& file, const
osgDB::ReaderWriter::Options* options) const
{
std::string ext = osgDB::getLowerCaseFileExtension(file);
if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
std::string fileName = osgDB::findDataFile( file, options );
if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
osgDB::ifstream istream(fileName.c_str(), std::ios::in |
std::ios::binary);
if(!istream) return ReadResult::FILE_NOT_HANDLED;
ReadResult rr = readBMPStream(istream);
if(rr.validImage()) rr.getImage()->setFileName(file);
return rr;
}
bool WriteBMPStream(const osg::Image &img, std::ostream& fout, const
std::string &fileName) const
{
// its easier for me to write a binary write using stdio than
streams
struct bmpheader hd;
uint32 nx=img.s(),ny=img.t(); // unsigned long ndep=img.r();
uint32 size, wordsPerScan;
int32 infsize; //size of BMPinfo in bytes
wordsPerScan=(nx*3+3)/4; // rounds up to next 32 bit boundary
size=4*ny*wordsPerScan; // rounded to 4bytes * number of scan lines
hd.FileType=MB;
hd.Reserved1=hd.Reserved2=0;
hd.offset[0]=sizeof(int32)+sizeof(BMPInfo)+sizeof(hd); // 26; //
offset to image
hd.offset[1]=0; // offset to image
// new round to be consistent with the swap in the size calclation
in the reading code.
hd.siz[0]=(size&0xffff); // low word
hd.siz[1]=(size&0xffff0000)>>16; // high word
fout.write((const char*)&hd, sizeof(hd));
struct BMPInfo inf;
osg::notify(osg::INFO) << "sizes "<<size << " "<<sizeof(inf)<<
std::endl;
inf.width=nx; //width of the image in pixels
inf.height=ny; // height of the image in pixels
inf.planes=1; //:word: number of planes (always 1)
inf.Colorbits=24; //word: number of bits used to describe
color in each pixel
inf.compression=0; //compression used windows says 0= no
compression
inf.ImageSize=size; //nx*ny*3; //image size in bytes
inf.XpixPerMeter=1000; //pixels per meter in X
inf.YpixPerMeter=1000; //pixels per meter in Y
inf.ColorUsed=0; //number of colors used
inf.Important=0; //number of "important" colors
// inf.os2stuff[6]; // allows os2.1 with 64 bytes to be read. Dont
know what these are yet.
infsize=sizeof(BMPInfo)+sizeof(int32);
fout.write((const char*)&infsize,sizeof(int32));
fout.write((const char*)&inf,sizeof(inf)); // one dword shorter
than the structure defined by MS
// the infsize value (above) completes the structure.
osg::notify(osg::INFO) << "save screen "<<fileName <<inf.width<< "
"<<inf.height << std::endl;
osg::notify(osg::INFO) << "sizes "<<size << " "<<infsize <<"
"<<sizeof(inf)<< std::endl;
// now output the bitmap
// 1) swap Blue with Red - needed for Windoss.
const unsigned char* data = img.data();
unsigned char *dta=new unsigned char[size];
// we need to case between different number of components
switch(img.computeNumComponents(img.getPixelFormat()))
{
case(3) :
{
for(unsigned int i=0;i<ny;i++) { // per scanline
int ioff=4*wordsPerScan*i;
for(unsigned int j=0;j<nx;j++) {
// swap r with b, thanks to good ole Bill -
//"Let's use BGR it's more logical than rgb which
everyone else uses."
dta[3*j+ioff]=data[3*(j+i*nx)+2];
dta[3*j+ioff+1]=data[3*(j+i*nx)+1];
dta[3*j+ioff+2]=data[3*(j+i*nx)+0];
}
}
}
break;
case(4) :
{
for(unsigned int i=0;i<ny;i++) { // per scanline
int ioff=4*wordsPerScan*i;
for(unsigned int j=0;j<nx;j++) {
// swap r with b, thanks to good ole Bill -
//"Let's use BGR it's more logical than rgb which everyone
else uses."
dta[3*j+ioff]=dta[3*j+ioff+2];
dta[3*j+ioff+0]=data[4*(j+i*nx)+2];
dta[3*j+ioff+1]=data[4*(j+i*nx)+1];
dta[3*j+ioff+2]=data[4*(j+i*nx)+0];
}
}
}
break;
default:
osg::notify(osg::WARN) << "Cannot write images with other
number of components than 3 or 4" << std::endl;
break;
}
fout.write((const char*)dta,sizeof(unsigned char)*size);
delete [] dta;
return true;
}
virtual WriteResult writeImage(const osg::Image& image,std::ostream&
fout,const Options*) const
{
bool success = WriteBMPStream(image, fout, "<output stream>");
if(success)
return WriteResult::FILE_SAVED;
else
return WriteResult::ERROR_IN_WRITING_FILE;
}
virtual WriteResult writeImage(const osg::Image &img,const std::string&
fileName, const osgDB::ReaderWriter::Options*) const
{
std::string ext = osgDB::getFileExtension(fileName);
if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
osgDB::ofstream fout(fileName.c_str(), std::ios::out |
std::ios::binary);
if(!fout) return WriteResult::ERROR_IN_WRITING_FILE;
bool success = WriteBMPStream(img, fout, fileName);
if(success)
return WriteResult::FILE_SAVED;
else
return WriteResult::ERROR_IN_WRITING_FILE;
}
};
// now register with Registry to instantiate the above
// reader/writer.
REGISTER_OSGPLUGIN(bmp, ReaderWriterBMP)
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org