Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added ImageProcessor interface class and plugin mechnanism for ImageP…

…rocessor implementations to osgDB::Registry.

Add NVidiaTextureTools based plugin that provides an ImageProcessor implementation within an nvtt plugin.
  • Loading branch information...
commit a14219204452624e68c7dbdeae685d47a3a65d60 1 parent 9c77274
Robert OSFIELD authored
1  CMakeLists.txt
View
@@ -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)
61 CMakeModules/FindNVTT.cmake
View
@@ -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)
54 include/osgDB/ImageProcessor
View
@@ -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
52 include/osgDB/Registry
View
@@ -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;
}
65 src/osgDB/Registry.cpp
View
@@ -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)
{
3  src/osgPlugins/CMakeLists.txt
View
@@ -215,6 +215,9 @@ IF(QTKIT_FOUND)
ADD_SUBDIRECTORY(QTKit)
ENDIF()
+IF(NVTT_FOUND)
+ ADD_SUBDIRECTORY(nvtt)
+ENDIF()
IF(FREETYPE_FOUND)
11 src/osgPlugins/nvtt/CMakeLists.txt
View
@@ -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)
329 src/osgPlugins/nvtt/NVTTImageProcessor.cpp
View
@@ -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)
Please sign in to comment.
Something went wrong with that request. Please try again.