root/OpenSceneGraph/trunk/src/osgPlugins/nvtt/NVTTImageProcessor.cpp @ 12912

Revision 12912, 10.9 kB (checked in by robert, 2 years ago)

Added support for using GL_UNPACK_ROW_LENGTH in conjunction with texture's + osg::Image via new RowLength?
parameter in osg::Image. To support this Image::setData(..) now has a new optional rowLength parameter which
defaults to 0, which provides the original behaviour, Image::setRowLength(int) and int Image::getRowLength() are also provided.

With the introduction of RowLength? support in osg::Image it is now possible to create a sub image where
the t size of the image are smaller than the row length, useful for when you have a large image on the CPU
and which to use a small portion of it on the GPU. However, when these sub images are created the data
within the image is no longer contiguous so data access can no longer assume that all the data is in
one block. The new method Image::isDataContiguous() enables the user to check whether the data is contiguous,
and if not one can either access the data row by row using Image::data(column,row,image) accessor, or use the
new Image::DataIterator? for stepping through each block on memory assocatied with the image.

To support the possibility of non contiguous osg::Image usage of image objects has had to be updated to
check DataContiguous? and handle the case or use access via the DataIerator? or by row by row. To achieve
this a relatively large number of files has had to be modified, in particular the texture classes and
image plugins that doing writing.

  • Property svn:eol-style set to native
Line 
1
2#include <osg/Texture>
3#include <osgDB/Registry>
4
5#include <nvtt/nvtt.h>
6#include <string.h>
7
8class NVTTProcessor : public osgDB::ImageProcessor
9{
10public:
11    virtual void compress(osg::Image& image, osg::Texture::InternalFormatMode compressedFormat, bool generateMipMap, bool resizeToPowerOfTwo, CompressionMethod method, CompressionQuality quality);
12    virtual void generateMipMap(osg::Image& image, bool resizeToPowerOfTwo, CompressionMethod method);
13
14protected:
15
16    void process( osg::Image& texture, nvtt::Format format, bool generateMipMap, bool resizeToPowerOfTwo, CompressionMethod method, CompressionQuality quality);
17
18    struct VPBErrorHandler : public nvtt::ErrorHandler
19    {
20        virtual void error(nvtt::Error e);
21    };
22
23    struct OSGImageOutputHandler : public nvtt::OutputHandler
24    {
25        typedef std::vector<unsigned char> MipMapData;
26
27        std::vector<MipMapData*> _mipmaps;
28        int _width;
29        int _height;
30        int _currentMipLevel;
31        int _currentNumberOfWritenBytes;
32        nvtt::Format _format;
33        bool _discardAlpha;
34
35        OSGImageOutputHandler(nvtt::Format format, bool discardAlpha);
36        virtual ~OSGImageOutputHandler();
37
38        // create the osg image from the given format
39        bool assignImage(osg::Image& image);
40
41        /// Indicate the start of a new compressed image that's part of the final texture.
42        virtual void beginImage(int size, int width, int height, int depth, int face, int miplevel);
43
44        /// Output data. Compressed data is output as soon as it's generated to minimize memory allocations.
45        virtual bool writeData(const void * data, int size);
46    };
47
48    // Convert RGBA to BGRA : nvtt only accepts BGRA pixel format
49    void convertRGBAToBGRA( std::vector<unsigned char>& outputData, const osg::Image& image );
50
51    // Convert RGB to BGRA : nvtt only accepts BGRA pixel format
52    void convertRGBToBGRA( std::vector<unsigned char>& outputData, const osg::Image& image );
53
54};
55
56/// Error handler.
57void NVTTProcessor::VPBErrorHandler::error(nvtt::Error e)
58{
59    switch (e)
60    {
61    case nvtt::Error_Unknown:
62        OSG_WARN<<" NVTT : unknown error"<<std::endl;
63        break;
64    case nvtt::Error_InvalidInput:
65        OSG_WARN<<" NVTT : invalid input"<<std::endl;
66        break;
67    case nvtt::Error_UnsupportedFeature:
68        OSG_WARN<<" NVTT : unsupported feature"<<std::endl;
69        break;
70    case nvtt::Error_CudaError:
71        OSG_WARN<<" NVTT : cuda error"<<std::endl;
72        break;
73        case nvtt::Error_FileOpen:
74        OSG_WARN<<" NVTT : file open error"<<std::endl;
75        break;
76        case nvtt::Error_FileWrite:
77        OSG_WARN<<" NVTT : file write error"<<std::endl;
78        break;
79    }
80}
81
82/// Output handler.
83NVTTProcessor::OSGImageOutputHandler::OSGImageOutputHandler(nvtt::Format format, bool discardAlpha)
84    : _format(format), _discardAlpha(discardAlpha)
85{
86}
87
88NVTTProcessor::OSGImageOutputHandler::~OSGImageOutputHandler()
89{
90    for (unsigned int n=0; n<_mipmaps.size(); n++)
91    {
92        delete _mipmaps[n];
93    }
94    _mipmaps.clear();
95}
96
97// create the osg image from the given format
98bool NVTTProcessor::OSGImageOutputHandler::assignImage(osg::Image& image)
99{
100    // convert nvtt format to OpenGL pixel format
101    GLint pixelFormat;
102    switch (_format)
103    {
104    case nvtt::Format_RGBA:
105        pixelFormat = _discardAlpha ? GL_RGB : GL_RGBA;
106        break;
107    case nvtt::Format_DXT1:
108        pixelFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
109        break;
110    case nvtt::Format_DXT1a:
111        pixelFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
112        break;
113    case nvtt::Format_DXT3:
114        pixelFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
115        break;
116    case nvtt::Format_DXT5:
117        pixelFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
118        break;
119    default:
120        OSG_WARN<<" Invalid or not supported format"<<std::endl;
121        return false;
122    }
123
124    // Compute the total size and the mipmap offsets
125    osg::Image::MipmapDataType mipmapOffsets(_mipmaps.size()-1);
126    unsigned int totalSize = _mipmaps[0]->size();
127    for (unsigned int n=1; n<_mipmaps.size(); n++)
128    {
129        mipmapOffsets[n-1] = totalSize;
130        totalSize += _mipmaps[n]->size();
131    }
132
133    // Allocate data and copy it
134    unsigned char* data = new unsigned char[ totalSize ];
135    unsigned char* ptr = data;
136    for (unsigned int n=0; n<_mipmaps.size(); n++)
137    {
138        memcpy( ptr, &(*_mipmaps[n])[0], _mipmaps[n]->size() );
139        ptr += _mipmaps[n]->size();
140    }
141
142    image.setImage(_width,_height,1,pixelFormat,pixelFormat,GL_UNSIGNED_BYTE,data,osg::Image::USE_NEW_DELETE);
143    image.setMipmapLevels(mipmapOffsets);
144
145    return true;
146}
147
148/// Indicate the start of a new compressed image that's part of the final texture.
149void NVTTProcessor::OSGImageOutputHandler::beginImage(int size, int width, int height, int depth, int face, int miplevel)
150{
151    // store the new width/height of the texture
152    if (miplevel == 0)
153    {
154        _width = width;
155        _height = height;
156    }
157    // prepare to receive mipmap data
158    if (miplevel >= static_cast<int>(_mipmaps.size()))
159    {
160        _mipmaps.resize(miplevel+1);
161    }
162    _mipmaps[miplevel] = new MipMapData(size);
163    _currentMipLevel = miplevel;
164    _currentNumberOfWritenBytes = 0;
165}
166
167/// Output data. Compressed data is output as soon as it's generated to minimize memory allocations.
168bool NVTTProcessor::OSGImageOutputHandler::writeData(const void * data, int size)
169{
170    // Copy mipmap data
171    std::vector<unsigned char>& dstData = *_mipmaps[_currentMipLevel];
172    memcpy( &dstData[_currentNumberOfWritenBytes], data, size );
173    _currentNumberOfWritenBytes += size;
174    return true;
175}
176
177// Convert RGBA to BGRA : nvtt only accepts BGRA pixel format
178void NVTTProcessor::convertRGBAToBGRA( std::vector<unsigned char>& outputData, const osg::Image& image )
179{
180    unsigned int n=0;
181    for(int row=0; row<image.t(); ++row)
182    {
183        const unsigned char* data = image.data(0,row);
184        for(int column=0; column<image.s(); ++column)
185        {
186            outputData[n] = data[column*4+2];
187            outputData[n+1] = data[column*4+1];
188            outputData[n+2] = data[column*4+n];
189            outputData[n+3] = data[column*4+3];
190            n+=4;
191        }
192    }
193}
194
195// Convert RGB to BGRA : nvtt only accepts BGRA pixel format
196void NVTTProcessor::convertRGBToBGRA( std::vector<unsigned char>& outputData, const osg::Image& image )
197{
198    unsigned int n=0;
199    for(int row=0; row<image.t(); ++row)
200    {
201        const unsigned char* data = image.data(0,row);
202        for(int column=0; column<image.s(); ++column)
203        {
204            outputData[n] = data[column*3+2];
205            outputData[n+1] = data[column*3+1];
206            outputData[n+2] = data[column*3+n];
207            outputData[n+3] = 255;
208            n+=4;
209        }
210    }
211}
212
213// Main interface with NVTT
214void NVTTProcessor::process( osg::Image& image, nvtt::Format format, bool generateMipMap, bool resizeToPowerOfTwo, CompressionMethod method, CompressionQuality quality)
215{
216    // Fill input options
217    nvtt::InputOptions inputOptions;
218    inputOptions.setTextureLayout(nvtt::TextureType_2D, image.s(), image.t() );
219    inputOptions.setNormalMap(false);
220    inputOptions.setConvertToNormalMap(false);
221    inputOptions.setGamma(2.2f, 2.2f);
222    inputOptions.setNormalizeMipmaps(false);
223    inputOptions.setWrapMode(nvtt::WrapMode_Clamp);
224    if (resizeToPowerOfTwo)
225    {
226        inputOptions.setRoundMode(nvtt::RoundMode_ToNearestPowerOfTwo);
227    }
228    inputOptions.setMipmapGeneration(generateMipMap);
229
230    if (image.getPixelFormat() == GL_RGBA)
231    {
232        inputOptions.setAlphaMode( nvtt::AlphaMode_Transparency );
233    }
234    else
235    {
236        inputOptions.setAlphaMode( nvtt::AlphaMode_None );
237    }
238    std::vector<unsigned char> imageData( image.s() * image.t() * 4 );
239    if (image.getPixelFormat() == GL_RGB)
240    {
241        convertRGBToBGRA( imageData, image );
242    }
243    else
244    {
245        convertRGBAToBGRA( imageData, image );
246    }
247    inputOptions.setMipmapData(&imageData[0],image.s(),image.t());
248
249    // Fill compression options
250    nvtt::CompressionOptions compressionOptions;
251    switch(quality)
252    {
253      case FASTEST:
254        compressionOptions.setQuality( nvtt::Quality_Fastest );
255        break;
256      case NORMAL:
257        compressionOptions.setQuality( nvtt::Quality_Normal );
258        break;
259      case PRODUCTION:
260        compressionOptions.setQuality( nvtt::Quality_Production);
261        break;
262      case HIGHEST:
263        compressionOptions.setQuality( nvtt::Quality_Highest);
264        break;
265    }
266    compressionOptions.setFormat( format );
267    //compressionOptions.setQuantization(false,false,false);
268    if (format == nvtt::Format_RGBA)
269    {
270        if (image.getPixelFormat() == GL_RGB)
271        {
272            compressionOptions.setPixelFormat(24,0xff,0xff00,0xff0000,0);
273        }
274        else
275        {
276            compressionOptions.setPixelFormat(32,0xff,0xff00,0xff0000,0xff000000);
277        }
278    }
279
280    // Handler
281    OSGImageOutputHandler outputHandler(format,image.getPixelFormat() == GL_RGB);
282    VPBErrorHandler errorHandler;
283
284    // Fill output options
285    nvtt::OutputOptions outputOptions;
286    outputOptions.setOutputHandler(&outputHandler);
287    outputOptions.setErrorHandler(&errorHandler);
288    outputOptions.setOutputHeader(false);
289
290    // Process the compression now
291    nvtt::Compressor compressor;
292    if(method == USE_GPU)
293    {
294        compressor.enableCudaAcceleration(true);
295        if(!compressor.isCudaAccelerationEnabled())
296        {
297            OSG_WARN<< "CUDA acceleration was enabled but it is not available. CPU will be used."<<std::endl;
298        }
299    }
300    else
301    {
302        compressor.enableCudaAcceleration(false);
303    }
304
305    compressor.process(inputOptions,compressionOptions,outputOptions);
306
307    outputHandler.assignImage(image);
308}
309
310void NVTTProcessor::compress(osg::Image& image, osg::Texture::InternalFormatMode compressedFormat, bool generateMipMap, bool resizeToPowerOfTwo, CompressionMethod method, CompressionQuality quality)
311{
312    nvtt::Format format;
313    switch (compressedFormat)
314    {
315    case osg::Texture::USE_S3TC_DXT1_COMPRESSION:
316        if (image.getPixelFormat() == GL_RGBA)
317            format = nvtt::Format_DXT1a;
318        else
319            format = nvtt::Format_DXT1;
320        break;
321    case osg::Texture::USE_S3TC_DXT1c_COMPRESSION:
322        format = nvtt::Format_DXT1;
323        break;
324    case osg::Texture::USE_S3TC_DXT1a_COMPRESSION:
325        format = nvtt::Format_DXT1a;
326        break;
327    case osg::Texture::USE_S3TC_DXT3_COMPRESSION:
328        format = nvtt::Format_DXT3;
329        break;
330    case osg::Texture::USE_S3TC_DXT5_COMPRESSION:
331        format = nvtt::Format_DXT5;
332        break;
333    default:
334        OSG_WARN<<" Invalid or not supported compress format"<<std::endl;
335        return;
336    }
337
338    process( image, format, generateMipMap, resizeToPowerOfTwo, method, quality );
339}
340
341void NVTTProcessor::generateMipMap(osg::Image& image, bool resizeToPowerOfTwo, CompressionMethod method)
342{
343    process( image, nvtt::Format_RGBA, true, resizeToPowerOfTwo, method, NORMAL);
344}
345
346REGISTER_OSGIMAGEPROCESSOR(nvtt, NVTTProcessor)
Note: See TracBrowser for help on using the browser.