On 21/11/08 4:29 AM, Robert Osfield wrote:
I've just reviewed your bmp fixes and note that you've kept the old
code in place but effectively #ifdef'd out. Given the function that
you've replace is rather long I think it would be a easy to make
Attached is a cleaned-up version of the BMP reader/writer.
/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>
#include <assert.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.
*
**********************************************************************/
/* 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;
}
// find least-significant (lowest) bit position in 16-bit mask
static unsigned int findLeastSignificantBit(unsigned short mask)
{
unsigned int shift = 1;
while ((mask & 0x01) == 0)
{
mask >>= 1;
++shift;
}
return shift;
}
// find most-significant (highest) bit position in 16-bit mask
static unsigned int findMostSignificantBit(unsigned short mask)
{
unsigned int shift = 16;
while ((mask & 0x8000) == 0)
{
mask <<= 1;
--shift;
}
return shift;
}
/*
* BMP header
*/
const unsigned short BMP_MAGIC_BM = 0x424D; // 'BM'
const unsigned short BMP_MAGIC_MB = 0x4D42; // 'MB'
struct BMPHeader {
unsigned short magic; // 'BM'
unsigned int fileSize;
unsigned short reserved1;
unsigned short reserved2;
unsigned int imageOffset;
};
/*
* 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;
};
/*
* OS/2 v1 header
*/
struct BITMAPCOREHEADER {
//unsigned int hdrSize;
unsigned short width, height;
unsigned short colorPlanes;
unsigned short bitsPerPixel;
};
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;
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 != BMP_MAGIC_BM && bmp.magic != BMP_MAGIC_MB)
{
osg::notify(osg::WARN) << "Invalid BMP magic\n";
return 0;
}
swap = (bmp.magic == BMP_MAGIC_MB);
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 BMP fileSize=" <<
bmp.fileSize << " != actual=" << actFileSize << std::endl;
bmp.fileSize = actFileSize;
}
}
BITMAPINFOHEADER dib;
unsigned int dibHdrSize;
/*
* read DIB header
*/
fin.read((char*) &dibHdrSize, sizeof(dibHdrSize));
if (swap) swapbyte(&dibHdrSize);
if (dibHdrSize == 12)
{
/*
* OS/2 v1
*/
BITMAPCOREHEADER hdr;
unsigned int expectHdrSize = sizeof(hdr) + sizeof(dibHdrSize);
if (expectHdrSize != dibHdrSize)
{
osg::notify(osg::WARN) << "Invalid BMP OS/2 v1 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);
}
// store 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 BMP 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 BMP/DIB header size=" <<
dibHdrSize << std::endl;
return 0;
}
// sanity checks
if (dib.height < 0)
{
osg::notify(osg::DEBUG_INFO) << "BMP Image is upside-down\n";
dib.height *= -1;
}
if (dib.colorPlanes != 1)
{
osg::notify(osg::WARN) << "Invalid BMP number of color planes=" <<
dib.colorPlanes << std::endl;
return 0;
}
if (dib.bitsPerPixel == 0)
{
osg::notify(osg::WARN) << "Invalid BMP 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 masks
*/
unsigned int redMask, greenMask, blueMask;
unsigned int redMaskWidth, greenMaskWidth, blueMaskWidth;
unsigned int redShift, greenShift, blueShift; // greenShift? wtf? ;-)
if (dib.bitsPerPixel == 16)
{
if (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);
}
}
else
{
redMask = 0x7c00;
greenMask = 0x03e0;
blueMask = 0x001f;
}
// determine shift width...
redShift = findLeastSignificantBit(redMask) - 1;
greenShift = findLeastSignificantBit(greenMask) - 1;
blueShift = findLeastSignificantBit(blueMask) - 1;
// determine mask width
redMaskWidth = findMostSignificantBit(redMask) - redShift;
greenMaskWidth = findMostSignificantBit(greenMask) - greenShift;
blueMaskWidth = findMostSignificantBit(blueMask) - blueShift;
#if 0
printf("redMask=%04x/%d/%d greenMask=%04x/%d/%d blueMask=%04x/%d/%d\n",
redMask, redMaskWidth, redShift,
greenMask, greenMaskWidth, greenShift,
blueMask, blueMaskWidth, blueShift);
#endif
}
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/2 v1 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;
else
{
osg::notify(osg::WARN) << "Unsupported BMP bit depth " <<
dib.bitsPerPixel << std::endl;
return 0;
}
}
unsigned int where = fin.tellg();
if (where != bmp.imageOffset)
{
// this can happen because we don't fully parse v4/v5 headers
osg::notify(osg::DEBUG_INFO) << "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
}
/*
* 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 RGB -> 24-bit RGB
unsigned short rgb16 = (rowp[1] << 8) | rowp[0];
if (swap) swapbyte(&rgb16);
imgp[0] = (rgb16 & redMask) >> redShift;
imgp[1] = (rgb16 & greenMask) >> greenShift;
imgp[2] = (rgb16 & blueMask) >> blueShift;
// expand range
imgp[0] <<= (8-redMaskWidth);
imgp[1] <<= (8-greenMaskWidth);
imgp[2] <<= (8-blueMaskWidth);
}
else
{
// BGR -> RGB(A)
imgp[0] = rowp[2];
imgp[1] = rowp[1];
imgp[2] = rowp[0];
if (imageBytesPerPixel == 4)
{
imgp[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 += imageBytesPerPixel;
}
++rowp;
}
}
}
// return result
width_ret = dib.width;
height_ret = dib.height;
numComponents_ret = imageBytesPerPixel;
return imageBuffer;
}
static bool bmp_save(const osg::Image& img, std::ostream& fout)
{
BMPHeader bmp;
const unsigned int bmpHdrSize = 14;
BITMAPINFOHEADER dib;
assert(sizeof(dib) == 36);
const unsigned int dibHdrSize = sizeof(dib) + 4;
const unsigned int bytesPerRowAlign = ((img.s() * 3 + 3) / 4) * 4;
// BMP header
{
bmp.magic = htons(BMP_MAGIC_BM);
bmp.reserved1 = bmp.reserved2 = 0;
bmp.imageOffset = bmpHdrSize + dibHdrSize;
bmp.fileSize = bmp.imageOffset + bytesPerRowAlign * img.t();
//printf("sizeof(bmp)=%u sizeof(dib)=%u dibHdrSize=%u\n", sizeof(bmp),
sizeof(dib), dibHdrSize);
//printf("fileSize=%u imageOffset=%u\n", bmp.fileSize, bmp.imageOffset);
//printf("s=%u t=%u bytesPerRowAlign=%u\n", img.s(), img.t(),
bytesPerRowAlign);
fout.write((char*) &bmp.magic, sizeof(bmp.magic));
fout.write((char*) &bmp.fileSize, sizeof(bmp.fileSize));
fout.write((char*) &bmp.reserved1, sizeof(bmp.reserved1));
fout.write((char*) &bmp.reserved2, sizeof(bmp.reserved2));
fout.write((char*) &bmp.imageOffset, sizeof(bmp.imageOffset));
}
// DIB header
{
dib.width = img.s();
dib.height = img.t();
dib.colorPlanes = 1;
dib.bitsPerPixel = 24;
dib.compression = BI_RGB;
dib.imageSize = bytesPerRowAlign * img.t();
dib.horizontalPixelPerMeter = 1000;
dib.verticalPixelPerMeter = 1000;
dib.numColorsInPalette = 0;
dib.numImportantColors = 0;
fout.write((char*) &dibHdrSize, sizeof(dibHdrSize));
fout.write((char*) &dib, sizeof(dib));
}
unsigned int channelsPerPixel =
img.computeNumComponents(img.getPixelFormat());
//printf("channelsPerPixel=%u\n", channelsPerPixel);
std::vector<unsigned char> rowBuffer(bytesPerRowAlign);
for (unsigned int y = 0; y < img.t(); ++y)
{
const unsigned char* imgp = img.data() + img.s() * y * channelsPerPixel;
for (unsigned int x = 0; x < img.s(); ++x)
{
// RGB -> BGR
rowBuffer[x * 3 + 0] = imgp[x * channelsPerPixel + 2];
rowBuffer[x * 3 + 1] = imgp[x * channelsPerPixel + 1];
rowBuffer[x * 3 + 2] = imgp[x * channelsPerPixel + 0];
}
fout.write((char*) &*rowBuffer.begin(), rowBuffer.size());
}
return true;
}
class ReaderWriterBMP : public osgDB::ReaderWriter
{
public:
ReaderWriterBMP()
{
supportsExtension("bmp","BMP Image format");
}
virtual const char* className() const { return "BMP Image Reader"; }
virtual ReadResult readObject(std::istream& fin, const Options* options
= 0) const
{
return readImage(fin, options);
}
virtual ReadResult readObject(const std::string& file, const Options*
options = 0) const
{
return readImage(file, options);
}
virtual ReadResult readImage(std::istream& fin, const Options* = 0)
const
{
return readBMPStream(fin);
}
virtual ReadResult readImage(const std::string& file, const Options*
options = 0) 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;
}
virtual WriteResult writeImage(const osg::Image& image, std::ostream&
fout, const Options* = 0) const
{
if (bmp_save(image, fout))
return WriteResult::FILE_SAVED;
else
return WriteResult::ERROR_IN_WRITING_FILE;
}
virtual WriteResult writeImage(const osg::Image& img, const
std::string& fileName, const Options* options = 0) 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;
return writeImage(img, fout, options);
}
private:
static ReadResult readBMPStream(std::istream& fin)
{
int s, t;
int internalFormat;
unsigned char *imageData = bmp_load(fin, s, t, internalFormat);
if (imageData == 0) return ReadResult::ERROR_IN_READING_FILE;
unsigned int pixelFormat;
switch (internalFormat)
{
case 1:
pixelFormat = GL_LUMINANCE;
break;
case 2:
pixelFormat = GL_LUMINANCE_ALPHA;
break;
case 3:
pixelFormat = GL_RGB;
break;
default:
pixelFormat = GL_RGBA;
break;
}
unsigned int dataType = GL_UNSIGNED_BYTE;
osg::Image* pOsgImage = new osg::Image;
pOsgImage->setImage(s, t, 1,
internalFormat, pixelFormat, GL_UNSIGNED_BYTE, imageData,
osg::Image::USE_NEW_DELETE);
return pOsgImage;
}
};
// 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