root/OpenSceneGraph/trunk/src/osgPlugins/pvr/ReaderWriterPVR.cpp @ 13041

Revision 13041, 8.2 kB (checked in by robert, 3 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
Line 
1// ReaderWriter for pvr images
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)
23typedef UINT64     uint64_t;
24typedef INT64      int64_t;
25typedef UINT32     uint32_t;
26typedef INT32      int32_t;
27typedef UINT16     uint16_t;
28typedef 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
37using namespace osg;
38
39#define PVR_TEXTURE_FLAG_TYPE_MASK    0xff
40
41static char gPVRTexIdentifier[5] = "PVR!";
42
43enum
44{
45  kPVRTextureFlagTypePVRTC_2 = 12,
46  kPVRTextureFlagTypePVRTC_4,
47  kPVRTextureFlagTypeOGLPVRTC_2 = 24,
48  kPVRTextureFlagTypeOGLPVRTC_4,
49  kPVRTextureFlagTypeETC = 54
50};
51
52typedef 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
117class ReaderWriterPVR : public osgDB::ReaderWriter
118{
119public:
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            // Calculate the data size for each texture level and respect the minimum number of blocks
195            while(dataOffset < header.dataLength){
196                if(formatFlags == kPVRTextureFlagTypePVRTC_4 || formatFlags == kPVRTextureFlagTypeOGLPVRTC_4){
197                    blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
198                    widthBlocks = width / 4;
199                    heightBlocks = height / 4;
200                    bpp = 4;
201                }else if(formatFlags == kPVRTextureFlagTypeETC){
202                    blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
203                    widthBlocks = width / 4;
204                    heightBlocks = height / 4;
205                    bpp = 4;
206                }else{
207                    blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
208                    widthBlocks = width / 8;
209                    heightBlocks = height / 4;
210                    bpp = 2;
211                }
212
213                // Clamp to minimum number of blocks
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// now register with Registry to instantiate the above
273// reader/writer.
274REGISTER_OSGPLUGIN(pvr, ReaderWriterPVR)
Note: See TracBrowser for help on using the browser.