Added ImageProcessor interface class and plugin mechnanism for ImageProcessor implementations to osgDB::Registry.

Add NVidiaTextureTools based plugin that provides an ImageProcessor implementation within an nvtt plugin.
This commit is contained in:
Robert Osfield 2011-01-13 14:59:29 +00:00
parent f61a6aa4e7
commit 1b67e3ad1f
8 changed files with 575 additions and 1 deletions

View File

@ -423,6 +423,7 @@ FIND_PACKAGE(Poppler-glib)
FIND_PACKAGE(RSVG)
FIND_PACKAGE(GtkGl)
FIND_PACKAGE(DirectInput)
FIND_PACKAGE(NVTT)
# Include macro utilities here
INCLUDE(OsgMacroUtils)

View File

@ -0,0 +1,61 @@
# Locate nvidia-texture-tools
# This module defines
# NVTT_LIBRARY
# NVTT_FOUND, if false, do not try to link to nvtt
# NVTT_INCLUDE_DIR, where to find the headers
#
FIND_PATH(NVTT_INCLUDE_DIR nvtt/nvtt.h
PATHS
/usr/local
/usr
$ENV{NVTT_DIR}
${3rdPartyRoot}
PATH_SUFFIXES include
)
FIND_LIBRARY(NVTT_LIBRARY
NAMES nvtt
PATHS
/usr/local
/usr
$ENV{NVTT_DIR}
${3rdPartyRoot}
PATH_SUFFIXES lib64 lib lib/shared lib/static lib64/static
)
FIND_LIBRARY(NVIMAGE_LIBRARY
NAMES nvimage
PATHS
/usr/local
/usr
$ENV{NVTT_DIR}
${3rdPartyRoot}
PATH_SUFFIXES lib64 lib lib/shared lib/static lib64/static
)
FIND_LIBRARY(NVMATH_LIBRARY
NAMES nvmath
PATHS
/usr/local
/usr
$ENV{NVTT_DIR}
${3rdPartyRoot}
PATH_SUFFIXES lib64 lib lib/shared lib/static lib64/static
)
FIND_LIBRARY(NVCORE_LIBRARY
NAMES nvcore
PATHS
/usr/local
/usr
$ENV{NVTT_DIR}
${3rdPartyRoot}
PATH_SUFFIXES lib64 lib lib/shared lib/static lib64/static
)
SET(NVTT_FOUND "NO")
IF(NVTT_LIBRARY AND NVTT_INCLUDE_DIR)
SET(NVTT_FOUND "YES")
ENDIF(NVTT_LIBRARY AND NVTT_INCLUDE_DIR)

View File

@ -0,0 +1,54 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#ifndef OSGDB_IMAGEPROCESSOR
#define OSGDB_IMAGEPROCESSOR 1
#include <osg/Object>
namespace osgDB {
class ImageProcessor : public osg::Object
{
public:
ImageProcessor():
osg::Object(true) {}
ImageProcessor(const ImageProcessor& rw,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY):
osg::Object(rw,copyop) {}
virtual ~ImageProcessor() {}
META_Object(osgDB,ImageProcessor);
enum CompressionMethod
{
USE_CPU, /// Use CPU for compression even when GPU compression is available
USE_GPU /// Use GPU for compression when available (i.e CUDA), otherwise fallback to CPU
};
enum CompressionQuality
{
FASTEST,
NORMAL,
PRODUCTION,
HIGHEST
};
virtual void compress(osg::Image& image, osg::Texture::InternalFormatMode compressedFormat, bool generateMipMap, bool resizeToPowerOfTwo, CompressionMethod method, CompressionQuality quality) {}
virtual void generateMipMap(osg::Image& image, bool resizeToPowerOfTwo, CompressionMethod method) {}
};
}
#endif

View File

@ -27,6 +27,7 @@
#include <osgDB/ObjectWrapper>
#include <osgDB/FileCache>
#include <osgDB/SharedStateManager>
#include <osgDB/ImageProcessor>
#include <vector>
#include <map>
@ -59,7 +60,6 @@ struct type_wrapper: basic_type_wrapper {
}
};
/**
Registry is a singleton factory which stores
the reader/writers which are linked in
@ -101,6 +101,9 @@ class OSGDB_EXPORT Registry : public osg::Referenced
void addReaderWriter(ReaderWriter* rw);
void removeReaderWriter(ReaderWriter* rw);
void addImageProcessor(ImageProcessor* ip);
void removeImageProcessor(ImageProcessor* ip);
/** create the platform specific library name associated with file.*/
std::string createLibraryNameForFile(const std::string& fileName);
@ -142,6 +145,21 @@ class OSGDB_EXPORT Registry : public osg::Referenced
const ReaderWriterList& getReaderWriterList() const { return _rwList; }
typedef std::vector< osg::ref_ptr<ImageProcessor> > ImageProcessorList;
/** get a image processor if available.*/
ImageProcessor* getImageProcessor();
/** get a image processor which is associated specified extension.*/
ImageProcessor* getImageProcessorForExtension(const std::string& ext);
/** get list of all registered ImageProcessors.*/
ImageProcessorList& getImageProcessorList() { return _ipList; }
/** get const list of all registered ImageProcessors.*/
const ImageProcessorList& getImageProcessorList() const { return _ipList; }
typedef class osgDB::FindFileCallback FindFileCallback;
typedef class osgDB::ReadFileCallback ReadFileCallback;
typedef class osgDB::WriteFileCallback WriteFileCallback;
@ -555,6 +573,7 @@ class OSGDB_EXPORT Registry : public osg::Referenced
OpenThreads::ReentrantMutex _pluginMutex;
ReaderWriterList _rwList;
ImageProcessorList _ipList;
DynamicLibraryList _dlList;
bool _openingLibrary;
@ -624,6 +643,34 @@ class RegisterReaderWriterProxy
};
/** Proxy class for automatic registration of reader/writers with the Registry.*/
template<class T>
class RegisterImageProcessorProxy
{
public:
RegisterImageProcessorProxy()
{
if (Registry::instance())
{
_rw = new T;
Registry::instance()->addImageProcessor(_rw.get());
}
}
~RegisterImageProcessorProxy()
{
if (Registry::instance())
{
Registry::instance()->removeImageProcessor(_rw.get());
}
}
T* get() { return _rw.get(); }
protected:
osg::ref_ptr<T> _rw;
};
struct PluginFunctionProxy
{
PluginFunctionProxy(CPluginFunction function) { (function)(); }
@ -657,6 +704,9 @@ struct PluginFunctionProxy
extern "C" void osgdb_##ext(void) {} \
static osgDB::RegisterReaderWriterProxy<classname> g_proxy_##classname;
#define REGISTER_OSGIMAGEPROCESSOR(ext, classname) \
extern "C" void osgdb_##ext(void) {} \
static osgDB::RegisterImageProcessorProxy<classname> g_proxy_##classname;
}

View File

@ -550,6 +550,71 @@ void Registry::removeReaderWriter(ReaderWriter* rw)
}
ImageProcessor* Registry::getImageProcessor()
{
{
OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(_pluginMutex);
if (!_ipList.empty())
{
return _ipList.front().get();
}
}
return getImageProcessorForExtension("nvtt");
}
ImageProcessor* Registry::getImageProcessorForExtension(const std::string& ext)
{
{
OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(_pluginMutex);
if (!_ipList.empty())
{
return _ipList.front().get();
}
}
std::string libraryName = createLibraryNameForExtension(ext);
OSG_NOTICE << "Now checking for plug-in "<<libraryName<< std::endl;
if (loadLibrary(libraryName)==LOADED)
{
OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(_pluginMutex);
if (!_ipList.empty())
{
OSG_NOTICE << "Loaded plug-in "<<libraryName<<" and located ImageProcessor"<< std::endl;
return _ipList.front().get();
}
}
return 0;
}
void Registry::addImageProcessor(ImageProcessor* ip)
{
if (ip==0L) return;
OSG_NOTIFY(NOTICE) << "osg::Registry::addImageProcessor("<<ip->className()<<")"<< std::endl;
OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(_pluginMutex);
_ipList.push_back(ip);
}
void Registry::removeImageProcessor(ImageProcessor* ip)
{
if (ip==0L) return;
OSG_NOTIFY(NOTICE) << "osg::Registry::removeImageProcessor();"<< std::endl;
OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(_pluginMutex);
ImageProcessorList::iterator ipitr = std::find(_ipList.begin(),_ipList.end(),ip);
if (ipitr!=_ipList.end())
{
_ipList.erase(ipitr);
}
}
void Registry::addFileExtensionAlias(const std::string mapExt, const std::string toExt)
{

View File

@ -215,6 +215,9 @@ IF(QTKIT_FOUND)
ADD_SUBDIRECTORY(QTKit)
ENDIF()
IF(NVTT_FOUND)
ADD_SUBDIRECTORY(nvtt)
ENDIF()
IF(FREETYPE_FOUND)

View File

@ -0,0 +1,11 @@
INCLUDE_DIRECTORIES( ${NVTT_INCLUDE_DIR} )
SET(TARGET_SRC
NVTTImageProcessor.cpp
)
SET(TARGET_LIBRARIES_VARS NVTT_LIBRARY)
#### end var setup ###
SETUP_PLUGIN(nvtt)

View File

@ -0,0 +1,329 @@
#include <osg/Texture>
#include <osgDB/Registry>
#include <nvtt/nvtt.h>
#include <string.h>
class NVTTProcessor : public osgDB::ImageProcessor
{
public:
virtual void compress(osg::Image& image, osg::Texture::InternalFormatMode compressedFormat, bool generateMipMap, bool resizeToPowerOfTwo, CompressionMethod method, CompressionQuality quality);
virtual void generateMipMap(osg::Image& image, bool resizeToPowerOfTwo, CompressionMethod method);
protected:
void process( osg::Image& texture, nvtt::Format format, bool generateMipMap, bool resizeToPowerOfTwo, CompressionMethod method, CompressionQuality quality);
struct VPBErrorHandler : public nvtt::ErrorHandler
{
virtual void error(nvtt::Error e);
};
struct OSGImageOutputHandler : public nvtt::OutputHandler
{
typedef std::vector<unsigned char> MipMapData;
std::vector<MipMapData*> _mipmaps;
int _width;
int _height;
int _currentMipLevel;
int _currentNumberOfWritenBytes;
nvtt::Format _format;
bool _discardAlpha;
OSGImageOutputHandler(nvtt::Format format, bool discardAlpha);
virtual ~OSGImageOutputHandler();
// create the osg image from the given format
bool assignImage(osg::Image& image);
/// Indicate the start of a new compressed image that's part of the final texture.
virtual void beginImage(int size, int width, int height, int depth, int face, int miplevel);
/// Output data. Compressed data is output as soon as it's generated to minimize memory allocations.
virtual bool writeData(const void * data, int size);
};
// Convert RGBA to BGRA : nvtt only accepts BGRA pixel format
void convertRGBAToBGRA( std::vector<unsigned char>& outputData, const unsigned char* inputData );
// Convert RGB to BGRA : nvtt only accepts BGRA pixel format
void convertRGBToBGRA( std::vector<unsigned char>& outputData, const unsigned char* inputData );
};
/// Error handler.
void NVTTProcessor::VPBErrorHandler::error(nvtt::Error e)
{
switch (e)
{
case nvtt::Error_Unknown:
OSG_WARN<<" NVTT : unknown error"<<std::endl;
break;
case nvtt::Error_InvalidInput:
OSG_WARN<<" NVTT : invalid input"<<std::endl;
break;
case nvtt::Error_UnsupportedFeature:
OSG_WARN<<" NVTT : unsupported feature"<<std::endl;
break;
case nvtt::Error_CudaError:
OSG_WARN<<" NVTT : cuda error"<<std::endl;
break;
case nvtt::Error_FileOpen:
OSG_WARN<<" NVTT : file open error"<<std::endl;
break;
case nvtt::Error_FileWrite:
OSG_WARN<<" NVTT : file write error"<<std::endl;
break;
}
}
/// Output handler.
NVTTProcessor::OSGImageOutputHandler::OSGImageOutputHandler(nvtt::Format format, bool discardAlpha)
: _format(format), _discardAlpha(discardAlpha)
{
}
NVTTProcessor::OSGImageOutputHandler::~OSGImageOutputHandler()
{
for (unsigned int n=0; n<_mipmaps.size(); n++)
{
delete _mipmaps[n];
}
_mipmaps.clear();
}
// create the osg image from the given format
bool NVTTProcessor::OSGImageOutputHandler::assignImage(osg::Image& image)
{
// convert nvtt format to OpenGL pixel format
GLint pixelFormat;
switch (_format)
{
case nvtt::Format_RGBA:
pixelFormat = _discardAlpha ? GL_RGB : GL_RGBA;
break;
case nvtt::Format_DXT1:
pixelFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
break;
case nvtt::Format_DXT1a:
pixelFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
break;
case nvtt::Format_DXT3:
pixelFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
break;
case nvtt::Format_DXT5:
pixelFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
break;
default:
OSG_WARN<<" Invalid or not supported format"<<std::endl;
return false;
}
// Compute the total size and the mipmap offsets
osg::Image::MipmapDataType mipmapOffsets(_mipmaps.size()-1);
unsigned int totalSize = _mipmaps[0]->size();
for (unsigned int n=1; n<_mipmaps.size(); n++)
{
mipmapOffsets[n-1] = totalSize;
totalSize += _mipmaps[n]->size();
}
// Allocate data and copy it
unsigned char* data = new unsigned char[ totalSize ];
unsigned char* ptr = data;
for (unsigned int n=0; n<_mipmaps.size(); n++)
{
memcpy( ptr, &(*_mipmaps[n])[0], _mipmaps[n]->size() );
ptr += _mipmaps[n]->size();
}
image.setImage(_width,_height,1,pixelFormat,pixelFormat,GL_UNSIGNED_BYTE,data,osg::Image::USE_NEW_DELETE);
image.setMipmapLevels(mipmapOffsets);
return true;
}
/// Indicate the start of a new compressed image that's part of the final texture.
void NVTTProcessor::OSGImageOutputHandler::beginImage(int size, int width, int height, int depth, int face, int miplevel)
{
// store the new width/height of the texture
if (miplevel == 0)
{
_width = width;
_height = height;
}
// prepare to receive mipmap data
if (miplevel >= static_cast<int>(_mipmaps.size()))
{
_mipmaps.resize(miplevel+1);
}
_mipmaps[miplevel] = new MipMapData(size);
_currentMipLevel = miplevel;
_currentNumberOfWritenBytes = 0;
}
/// Output data. Compressed data is output as soon as it's generated to minimize memory allocations.
bool NVTTProcessor::OSGImageOutputHandler::writeData(const void * data, int size)
{
// Copy mipmap data
std::vector<unsigned char>& dstData = *_mipmaps[_currentMipLevel];
memcpy( &dstData[_currentNumberOfWritenBytes], data, size );
_currentNumberOfWritenBytes += size;
return true;
}
// Convert RGBA to BGRA : nvtt only accepts BGRA pixel format
void NVTTProcessor::convertRGBAToBGRA( std::vector<unsigned char>& outputData, const unsigned char* inputData )
{
for (unsigned n=0; n<outputData.size(); n += 4)
{
outputData[n] = inputData[n+2];
outputData[n+1] = inputData[n+1];
outputData[n+2] = inputData[n];
outputData[n+3] = inputData[n+3];
}
}
// Convert RGB to BGRA : nvtt only accepts BGRA pixel format
void NVTTProcessor::convertRGBToBGRA( std::vector<unsigned char>& outputData, const unsigned char* inputData )
{
unsigned int numberOfPixels = outputData.size()/4;
for (unsigned n=0; n<numberOfPixels; n++)
{
outputData[4*n] = inputData[3*n+2];
outputData[4*n+1] = inputData[3*n+1];
outputData[4*n+2] = inputData[3*n];
outputData[4*n+3] = 255;
}
}
// Main interface with NVTT
void NVTTProcessor::process( osg::Image& image, nvtt::Format format, bool generateMipMap, bool resizeToPowerOfTwo, CompressionMethod method, CompressionQuality quality)
{
// Fill input options
nvtt::InputOptions inputOptions;
inputOptions.setTextureLayout(nvtt::TextureType_2D, image.s(), image.t() );
inputOptions.setNormalMap(false);
inputOptions.setConvertToNormalMap(false);
inputOptions.setGamma(2.2f, 2.2f);
inputOptions.setNormalizeMipmaps(false);
inputOptions.setWrapMode(nvtt::WrapMode_Clamp);
if (resizeToPowerOfTwo)
{
inputOptions.setRoundMode(nvtt::RoundMode_ToNearestPowerOfTwo);
}
inputOptions.setMipmapGeneration(generateMipMap);
if (image.getPixelFormat() == GL_RGBA)
{
inputOptions.setAlphaMode( nvtt::AlphaMode_Transparency );
}
else
{
inputOptions.setAlphaMode( nvtt::AlphaMode_None );
}
std::vector<unsigned char> imageData( image.s() * image.t() * 4 );
if (image.getPixelFormat() == GL_RGB)
{
convertRGBToBGRA( imageData, image.data() );
}
else
{
convertRGBAToBGRA( imageData, image.data() );
}
inputOptions.setMipmapData(&imageData[0],image.s(),image.t());
// Fill compression options
nvtt::CompressionOptions compressionOptions;
switch(quality)
{
case FASTEST:
compressionOptions.setQuality( nvtt::Quality_Fastest );
break;
case NORMAL:
compressionOptions.setQuality( nvtt::Quality_Normal );
break;
case PRODUCTION:
compressionOptions.setQuality( nvtt::Quality_Production);
break;
case HIGHEST:
compressionOptions.setQuality( nvtt::Quality_Highest);
break;
}
compressionOptions.setFormat( format );
//compressionOptions.setQuantization(false,false,false);
if (format == nvtt::Format_RGBA)
{
if (image.getPixelFormat() == GL_RGB)
{
compressionOptions.setPixelFormat(24,0xff,0xff00,0xff0000,0);
}
else
{
compressionOptions.setPixelFormat(32,0xff,0xff00,0xff0000,0xff000000);
}
}
// Handler
OSGImageOutputHandler outputHandler(format,image.getPixelFormat() == GL_RGB);
VPBErrorHandler errorHandler;
// Fill output options
nvtt::OutputOptions outputOptions;
outputOptions.setOutputHandler(&outputHandler);
outputOptions.setErrorHandler(&errorHandler);
outputOptions.setOutputHeader(false);
// Process the compression now
nvtt::Compressor compressor;
if(method == USE_GPU)
{
compressor.enableCudaAcceleration(true);
if(!compressor.isCudaAccelerationEnabled())
{
OSG_WARN<< "CUDA acceleration was enabled but it is not available. CPU will be used."<<std::endl;
}
}
else
{
compressor.enableCudaAcceleration(false);
}
compressor.process(inputOptions,compressionOptions,outputOptions);
outputHandler.assignImage(image);
}
void NVTTProcessor::compress(osg::Image& image, osg::Texture::InternalFormatMode compressedFormat, bool generateMipMap, bool resizeToPowerOfTwo, CompressionMethod method, CompressionQuality quality)
{
nvtt::Format format;
switch (compressedFormat)
{
case osg::Texture::USE_S3TC_DXT1_COMPRESSION:
if (image.getPixelFormat() == GL_RGBA)
format = nvtt::Format_DXT1a;
else
format = nvtt::Format_DXT1;
break;
case osg::Texture::USE_S3TC_DXT3_COMPRESSION:
format = nvtt::Format_DXT3;
break;
case osg::Texture::USE_S3TC_DXT5_COMPRESSION:
format = nvtt::Format_DXT5;
break;
default:
OSG_WARN<<" Invalid or not supported compress format"<<std::endl;
return;
}
process( image, format, generateMipMap, resizeToPowerOfTwo, method, quality );
}
void NVTTProcessor::generateMipMap(osg::Image& image, bool resizeToPowerOfTwo, CompressionMethod method)
{
process( image, nvtt::Format_RGBA, true, resizeToPowerOfTwo, method, NORMAL);
}
REGISTER_OSGIMAGEPROCESSOR(nvtt, NVTTProcessor)