| 1 | |
|---|
| 2 | |
|---|
| 3 | #if defined(_MSC_VER) |
|---|
| 4 | #include <windows.h> |
|---|
| 5 | #endif |
|---|
| 6 | |
|---|
| 7 | #include <osg/Image> |
|---|
| 8 | #include <osg/Notify> |
|---|
| 9 | |
|---|
| 10 | #include <osg/Geode> |
|---|
| 11 | |
|---|
| 12 | #include <osg/GL> |
|---|
| 13 | |
|---|
| 14 | #include <osgDB/FileNameUtils> |
|---|
| 15 | #include <osgDB/FileUtils> |
|---|
| 16 | #include <osgDB/Registry> |
|---|
| 17 | |
|---|
| 18 | #include <stdio.h> |
|---|
| 19 | #include <stdlib.h> |
|---|
| 20 | #include <string.h> |
|---|
| 21 | |
|---|
| 22 | #if defined(_MSC_VER) |
|---|
| 23 | typedef UINT64 uint64_t; |
|---|
| 24 | typedef INT64 int64_t; |
|---|
| 25 | typedef UINT32 uint32_t; |
|---|
| 26 | typedef INT32 int32_t; |
|---|
| 27 | typedef UINT16 uint16_t; |
|---|
| 28 | typedef UINT8 uint8_t; |
|---|
| 29 | #else |
|---|
| 30 | #if defined __sun || defined __hpux |
|---|
| 31 | #include <inttypes.h> |
|---|
| 32 | #else |
|---|
| 33 | #include <stdint.h> |
|---|
| 34 | #endif |
|---|
| 35 | #endif |
|---|
| 36 | |
|---|
| 37 | using namespace osg; |
|---|
| 38 | |
|---|
| 39 | #define PVR_TEXTURE_FLAG_TYPE_MASK 0xff |
|---|
| 40 | |
|---|
| 41 | static char gPVRTexIdentifier[5] = "PVR!"; |
|---|
| 42 | |
|---|
| 43 | enum |
|---|
| 44 | { |
|---|
| 45 | kPVRTextureFlagTypePVRTC_2 = 12, |
|---|
| 46 | kPVRTextureFlagTypePVRTC_4, |
|---|
| 47 | kPVRTextureFlagTypeOGLPVRTC_2 = 24, |
|---|
| 48 | kPVRTextureFlagTypeOGLPVRTC_4, |
|---|
| 49 | kPVRTextureFlagTypeETC = 54 |
|---|
| 50 | }; |
|---|
| 51 | |
|---|
| 52 | typedef struct _PVRTexHeader |
|---|
| 53 | { |
|---|
| 54 | uint32_t headerLength; |
|---|
| 55 | uint32_t height; |
|---|
| 56 | uint32_t width; |
|---|
| 57 | uint32_t numMipmaps; |
|---|
| 58 | uint32_t flags; |
|---|
| 59 | uint32_t dataLength; |
|---|
| 60 | uint32_t bpp; |
|---|
| 61 | uint32_t bitmaskRed; |
|---|
| 62 | uint32_t bitmaskGreen; |
|---|
| 63 | uint32_t bitmaskBlue; |
|---|
| 64 | uint32_t bitmaskAlpha; |
|---|
| 65 | uint32_t pvrTag; |
|---|
| 66 | uint32_t numSurfs; |
|---|
| 67 | |
|---|
| 68 | typedef unsigned char * BytePtr; |
|---|
| 69 | |
|---|
| 70 | bool needsBytesSwapped() |
|---|
| 71 | { |
|---|
| 72 | union { |
|---|
| 73 | int testWord; |
|---|
| 74 | char testByte[sizeof(int)]; |
|---|
| 75 | }endianTest; |
|---|
| 76 | endianTest.testWord = 1; |
|---|
| 77 | if( endianTest.testByte[0] == 1 ) |
|---|
| 78 | return false; |
|---|
| 79 | else |
|---|
| 80 | return true; |
|---|
| 81 | } |
|---|
| 82 | |
|---|
| 83 | template <class T> |
|---|
| 84 | inline void swapBytes( T &s ) |
|---|
| 85 | { |
|---|
| 86 | if( sizeof( T ) == 1 ) |
|---|
| 87 | return; |
|---|
| 88 | |
|---|
| 89 | T d = s; |
|---|
| 90 | BytePtr sptr = (BytePtr)&s; |
|---|
| 91 | BytePtr dptr = &(((BytePtr)&d)[sizeof(T)-1]); |
|---|
| 92 | |
|---|
| 93 | for( unsigned int i = 0; i < sizeof(T); i++ ) |
|---|
| 94 | *(sptr++) = *(dptr--); |
|---|
| 95 | } |
|---|
| 96 | |
|---|
| 97 | void swapBytes() |
|---|
| 98 | { |
|---|
| 99 | swapBytes(headerLength); |
|---|
| 100 | swapBytes(height); |
|---|
| 101 | swapBytes(width); |
|---|
| 102 | swapBytes(numMipmaps); |
|---|
| 103 | swapBytes(flags); |
|---|
| 104 | swapBytes(dataLength); |
|---|
| 105 | swapBytes(bpp); |
|---|
| 106 | swapBytes(bitmaskRed); |
|---|
| 107 | swapBytes(bitmaskGreen); |
|---|
| 108 | swapBytes(bitmaskBlue); |
|---|
| 109 | swapBytes(bitmaskAlpha); |
|---|
| 110 | swapBytes(pvrTag); |
|---|
| 111 | swapBytes(numSurfs); |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | } PVRTexHeader; |
|---|
| 115 | |
|---|
| 116 | |
|---|
| 117 | class ReaderWriterPVR : public osgDB::ReaderWriter |
|---|
| 118 | { |
|---|
| 119 | public: |
|---|
| 120 | |
|---|
| 121 | ReaderWriterPVR() |
|---|
| 122 | { |
|---|
| 123 | supportsExtension("pvr","PVR image format"); |
|---|
| 124 | } |
|---|
| 125 | |
|---|
| 126 | virtual const char* className() const { return "PVR Image Reader/Writer"; } |
|---|
| 127 | |
|---|
| 128 | |
|---|
| 129 | ReadResult readPVRStream(std::istream& fin) const |
|---|
| 130 | { |
|---|
| 131 | PVRTexHeader header; |
|---|
| 132 | |
|---|
| 133 | fin.read((char*)&header, sizeof(PVRTexHeader)); |
|---|
| 134 | if(!fin.good()){ |
|---|
| 135 | osg::notify(osg::WARN) << "Failed to read pvr header." << std::endl; |
|---|
| 136 | return ReadResult::ERROR_IN_READING_FILE; |
|---|
| 137 | } |
|---|
| 138 | |
|---|
| 139 | if(header.needsBytesSwapped()) |
|---|
| 140 | header.swapBytes(); |
|---|
| 141 | |
|---|
| 142 | if(gPVRTexIdentifier[0] != static_cast<char>((header.pvrTag >> 0) & 0xff) || |
|---|
| 143 | gPVRTexIdentifier[1] != static_cast<char>((header.pvrTag >> 8) & 0xff) || |
|---|
| 144 | gPVRTexIdentifier[2] != static_cast<char>((header.pvrTag >> 16) & 0xff) || |
|---|
| 145 | gPVRTexIdentifier[3] != static_cast<char>((header.pvrTag >> 24) & 0xff)) |
|---|
| 146 | { |
|---|
| 147 | 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; |
|---|
| 148 | return ReadResult::FILE_NOT_HANDLED; |
|---|
| 149 | } |
|---|
| 150 | |
|---|
| 151 | |
|---|
| 152 | uint32_t formatFlags = header.flags & PVR_TEXTURE_FLAG_TYPE_MASK; |
|---|
| 153 | GLenum internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; |
|---|
| 154 | uint32_t width, height; |
|---|
| 155 | |
|---|
| 156 | if(formatFlags == kPVRTextureFlagTypePVRTC_4 || formatFlags == kPVRTextureFlagTypePVRTC_2 || |
|---|
| 157 | formatFlags == kPVRTextureFlagTypeOGLPVRTC_4 || formatFlags == kPVRTextureFlagTypeOGLPVRTC_2 || |
|---|
| 158 | formatFlags == kPVRTextureFlagTypeETC){ |
|---|
| 159 | if(formatFlags == kPVRTextureFlagTypePVRTC_4 || formatFlags == kPVRTextureFlagTypeOGLPVRTC_4) |
|---|
| 160 | internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; |
|---|
| 161 | else if(formatFlags == kPVRTextureFlagTypePVRTC_2 || formatFlags == kPVRTextureFlagTypeOGLPVRTC_2) |
|---|
| 162 | internalFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; |
|---|
| 163 | else if(formatFlags == kPVRTextureFlagTypeETC) |
|---|
| 164 | internalFormat = GL_ETC1_RGB8_OES; |
|---|
| 165 | |
|---|
| 166 | width = header.width; |
|---|
| 167 | height = header.height; |
|---|
| 168 | |
|---|
| 169 | osg::ref_ptr<osg::Image> image = new osg::Image; |
|---|
| 170 | if (!image) return ReadResult::INSUFFICIENT_MEMORY_TO_LOAD; |
|---|
| 171 | |
|---|
| 172 | unsigned char *imageData = new unsigned char[header.dataLength]; |
|---|
| 173 | if (!imageData) return ReadResult::INSUFFICIENT_MEMORY_TO_LOAD; |
|---|
| 174 | |
|---|
| 175 | fin.read((char*)imageData, header.dataLength); |
|---|
| 176 | if(!fin.good()) |
|---|
| 177 | { |
|---|
| 178 | delete [] imageData; |
|---|
| 179 | return ReadResult::ERROR_IN_READING_FILE; |
|---|
| 180 | } |
|---|
| 181 | |
|---|
| 182 | image->setImage(header.width, header.height, 1, |
|---|
| 183 | internalFormat, internalFormat, |
|---|
| 184 | GL_UNSIGNED_BYTE, |
|---|
| 185 | imageData, |
|---|
| 186 | osg::Image::USE_NEW_DELETE); |
|---|
| 187 | |
|---|
| 188 | uint32_t dataOffset = 0; |
|---|
| 189 | uint32_t blockSize = 0, widthBlocks = 0, heightBlocks = 0; |
|---|
| 190 | uint32_t bpp = 4; |
|---|
| 191 | |
|---|
| 192 | osg::Image::MipmapDataType mipmapdata; |
|---|
| 193 | |
|---|
| 194 | |
|---|
| 195 | while(dataOffset < header.dataLength){ |
|---|
| 196 | if(formatFlags == kPVRTextureFlagTypePVRTC_4 || formatFlags == kPVRTextureFlagTypeOGLPVRTC_4){ |
|---|
| 197 | blockSize = 4 * 4; |
|---|
| 198 | widthBlocks = width / 4; |
|---|
| 199 | heightBlocks = height / 4; |
|---|
| 200 | bpp = 4; |
|---|
| 201 | }else if(formatFlags == kPVRTextureFlagTypeETC){ |
|---|
| 202 | blockSize = 4 * 4; |
|---|
| 203 | widthBlocks = width / 4; |
|---|
| 204 | heightBlocks = height / 4; |
|---|
| 205 | bpp = 4; |
|---|
| 206 | }else{ |
|---|
| 207 | blockSize = 8 * 4; |
|---|
| 208 | widthBlocks = width / 8; |
|---|
| 209 | heightBlocks = height / 4; |
|---|
| 210 | bpp = 2; |
|---|
| 211 | } |
|---|
| 212 | |
|---|
| 213 | |
|---|
| 214 | if(widthBlocks < 2) |
|---|
| 215 | widthBlocks = 2; |
|---|
| 216 | if(heightBlocks < 2) |
|---|
| 217 | heightBlocks = 2; |
|---|
| 218 | |
|---|
| 219 | if(dataOffset > 0) |
|---|
| 220 | mipmapdata.push_back(dataOffset); |
|---|
| 221 | |
|---|
| 222 | dataOffset += widthBlocks * heightBlocks * ((blockSize * bpp) / 8); |
|---|
| 223 | |
|---|
| 224 | width = osg::maximum(width >> 1, (uint32_t)1); |
|---|
| 225 | height = osg::maximum(height >> 1, (uint32_t)1); |
|---|
| 226 | } |
|---|
| 227 | |
|---|
| 228 | if(!mipmapdata.empty()) |
|---|
| 229 | image->setMipmapLevels(mipmapdata); |
|---|
| 230 | |
|---|
| 231 | return image.get(); |
|---|
| 232 | } |
|---|
| 233 | |
|---|
| 234 | osg::notify(osg::WARN) << "Failed to read pvr data." << std::endl; |
|---|
| 235 | return ReadResult::FILE_NOT_HANDLED; |
|---|
| 236 | } |
|---|
| 237 | |
|---|
| 238 | virtual ReadResult readObject(std::istream& fin,const osgDB::ReaderWriter::Options* options =NULL) const |
|---|
| 239 | { |
|---|
| 240 | return readImage(fin, options); |
|---|
| 241 | } |
|---|
| 242 | |
|---|
| 243 | virtual ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options =NULL) const |
|---|
| 244 | { |
|---|
| 245 | return readImage(file, options); |
|---|
| 246 | } |
|---|
| 247 | |
|---|
| 248 | virtual ReadResult readImage(std::istream& fin,const osgDB::ReaderWriter::Options* =NULL) const |
|---|
| 249 | { |
|---|
| 250 | return readPVRStream(fin); |
|---|
| 251 | } |
|---|
| 252 | |
|---|
| 253 | virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const |
|---|
| 254 | { |
|---|
| 255 | std::string ext = osgDB::getLowerCaseFileExtension(file); |
|---|
| 256 | if(!acceptsExtension(ext)) |
|---|
| 257 | return ReadResult::FILE_NOT_HANDLED; |
|---|
| 258 | |
|---|
| 259 | std::string fileName = osgDB::findDataFile(file, options); |
|---|
| 260 | if(fileName.empty()) |
|---|
| 261 | return ReadResult::FILE_NOT_FOUND; |
|---|
| 262 | |
|---|
| 263 | std::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary); |
|---|
| 264 | if(!istream) return ReadResult::FILE_NOT_HANDLED; |
|---|
| 265 | ReadResult rr = readPVRStream(istream); |
|---|
| 266 | if(rr.validImage()) rr.getImage()->setFileName(file); |
|---|
| 267 | return rr; |
|---|
| 268 | } |
|---|
| 269 | |
|---|
| 270 | }; |
|---|
| 271 | |
|---|
| 272 | |
|---|
| 273 | |
|---|
| 274 | REGISTER_OSGPLUGIN(pvr, ReaderWriterPVR) |
|---|