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

Reply via email to