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."
265 lines
7.9 KiB
C++
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)
|