Files
OpenSceneGraph/src/osgPlugins/pvr/ReaderWriterPVR.cpp
Robert Osfield 65aee287e7 From Mathias Froehlich, "Attached are most of the fixes that are required to build osg on solaris and
hpux. I have skipped irix this time as irix is too dead to keep osg building
there.

As usual, solaris does not like member templates in stl containers.
Some headers missing and link problems due to missing libraries."
2010-12-02 14:13:54 +00:00

265 lines
7.9 KiB
C++

// ReaderWriter for pvr images
#if defined(_MSC_VER)
#include <windows.h>
#endif
#include <osg/Image>
#include <osg/Notify>
#include <osg/Geode>
#include <osg/GL>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/Registry>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(_MSC_VER)
typedef UINT64 uint64_t;
typedef INT64 int64_t;
typedef UINT32 uint32_t;
typedef INT32 int32_t;
typedef UINT16 uint16_t;
typedef UINT8 uint8_t;
#else
#if defined __sun || defined __hpux
#include <inttypes.h>
#else
#include <stdint.h>
#endif
#endif
using namespace osg;
#define PVR_TEXTURE_FLAG_TYPE_MASK 0xff
static char gPVRTexIdentifier[5] = "PVR!";
enum
{
kPVRTextureFlagTypePVRTC_2 = 12,
kPVRTextureFlagTypePVRTC_4,
kPVRTextureFlagTypeOGLPVRTC_2 = 24,
kPVRTextureFlagTypeOGLPVRTC_4
};
typedef struct _PVRTexHeader
{
uint32_t headerLength;
uint32_t height;
uint32_t width;
uint32_t numMipmaps;
uint32_t flags;
uint32_t dataLength;
uint32_t bpp;
uint32_t bitmaskRed;
uint32_t bitmaskGreen;
uint32_t bitmaskBlue;
uint32_t bitmaskAlpha;
uint32_t pvrTag;
uint32_t numSurfs;
typedef unsigned char * BytePtr;
bool needsBytesSwapped()
{
union {
int testWord;
char testByte[sizeof(int)];
}endianTest;
endianTest.testWord = 1;
if( endianTest.testByte[0] == 1 )
return false;
else
return true;
}
template <class T>
inline void swapBytes( T &s )
{
if( sizeof( T ) == 1 )
return;
T d = s;
BytePtr sptr = (BytePtr)&s;
BytePtr dptr = &(((BytePtr)&d)[sizeof(T)-1]);
for( unsigned int i = 0; i < sizeof(T); i++ )
*(sptr++) = *(dptr--);
}
void swapBytes()
{
swapBytes(headerLength);
swapBytes(height);
swapBytes(width);
swapBytes(numMipmaps);
swapBytes(flags);
swapBytes(dataLength);
swapBytes(bpp);
swapBytes(bitmaskRed);
swapBytes(bitmaskGreen);
swapBytes(bitmaskBlue);
swapBytes(bitmaskAlpha);
swapBytes(pvrTag);
swapBytes(numSurfs);
}
} PVRTexHeader;
class ReaderWriterPVR : public osgDB::ReaderWriter
{
public:
ReaderWriterPVR()
{
supportsExtension("pvr","PVR image format");
}
virtual const char* className() const { return "PVR Image Reader/Writer"; }
ReadResult readPVRStream(std::istream& fin) const
{
PVRTexHeader header;
fin.read((char*)&header, sizeof(PVRTexHeader));
if(!fin.good()){
osg::notify(osg::WARN) << "Failed to read pvr header." << std::endl;
return ReadResult::ERROR_IN_READING_FILE;
}
if(header.needsBytesSwapped())
header.swapBytes();
if(gPVRTexIdentifier[0] != static_cast<char>((header.pvrTag >> 0) & 0xff) ||
gPVRTexIdentifier[1] != static_cast<char>((header.pvrTag >> 8) & 0xff) ||
gPVRTexIdentifier[2] != static_cast<char>((header.pvrTag >> 16) & 0xff) ||
gPVRTexIdentifier[3] != static_cast<char>((header.pvrTag >> 24) & 0xff))
{
osg::notify(osg::WARN) << "Failed to verify pvr header: " << ((header.pvrTag >> 0) & 0xff) << ", " << ((header.pvrTag >> 8) & 0xff) << ", " << ((header.pvrTag >> 16) & 0xff) << ", " << ((header.pvrTag >> 24) & 0xff) << std::endl;
return ReadResult::FILE_NOT_HANDLED;
}
uint32_t formatFlags = header.flags & PVR_TEXTURE_FLAG_TYPE_MASK;
GLenum internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
uint32_t width, height;
bool hasAlpha;
if(formatFlags == kPVRTextureFlagTypePVRTC_4 || formatFlags == kPVRTextureFlagTypePVRTC_2 ||
formatFlags == kPVRTextureFlagTypeOGLPVRTC_4 || formatFlags == kPVRTextureFlagTypeOGLPVRTC_2){
if(formatFlags == kPVRTextureFlagTypePVRTC_4 || formatFlags == kPVRTextureFlagTypeOGLPVRTC_4)
internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
else if(formatFlags == kPVRTextureFlagTypePVRTC_2 || formatFlags == kPVRTextureFlagTypeOGLPVRTC_2)
internalFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
width = header.width;
height = header.height;
if(header.bitmaskAlpha)
hasAlpha = true;
else
hasAlpha = false;
osg::Image *image = new osg::Image;
unsigned char *imageData = new unsigned char[header.dataLength];
fin.read((char*)imageData, header.dataLength);
if(!fin.good())
return ReadResult::ERROR_IN_READING_FILE;
image->setImage(header.width, header.height, 1,
internalFormat, internalFormat,
GL_UNSIGNED_BYTE,
imageData,
osg::Image::USE_NEW_DELETE);
uint32_t dataOffset = 0;
uint32_t blockSize = 0, widthBlocks = 0, heightBlocks = 0;
uint32_t bpp = 4;
osg::Image::MipmapDataType mipmapdata;
// Calculate the data size for each texture level and respect the minimum number of blocks
while(dataOffset < header.dataLength){
if(formatFlags == kPVRTextureFlagTypePVRTC_4 || formatFlags == kPVRTextureFlagTypeOGLPVRTC_4){
blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
widthBlocks = width / 4;
heightBlocks = height / 4;
bpp = 4;
}else{
blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
widthBlocks = width / 8;
heightBlocks = height / 4;
bpp = 2;
}
// Clamp to minimum number of blocks
if(widthBlocks < 2)
widthBlocks = 2;
if(heightBlocks < 2)
heightBlocks = 2;
if(dataOffset > 0)
mipmapdata.push_back(dataOffset);
dataOffset += widthBlocks * heightBlocks * ((blockSize * bpp) / 8);
width = osg::maximum(width >> 1, (uint32_t)1);
height = osg::maximum(height >> 1, (uint32_t)1);
}
if(!mipmapdata.empty())
image->setMipmapLevels(mipmapdata);
return image;
}
osg::notify(osg::WARN) << "Failed to read pvr data." << std::endl;
return ReadResult::FILE_NOT_HANDLED;
}
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 osgDB::ReaderWriter::Options* =NULL) const
{
return readPVRStream(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;
std::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary);
if(!istream) return ReadResult::FILE_NOT_HANDLED;
ReadResult rr = readPVRStream(istream);
if(rr.validImage()) rr.getImage()->setFileName(file);
return rr;
}
};
// now register with Registry to instantiate the above
// reader/writer.
REGISTER_OSGPLUGIN(pvr, ReaderWriterPVR)