diff --git a/src/osgPlugins/CMakeLists.txt b/src/osgPlugins/CMakeLists.txt index 8902fa466..e66d65d5c 100644 --- a/src/osgPlugins/CMakeLists.txt +++ b/src/osgPlugins/CMakeLists.txt @@ -77,6 +77,7 @@ ADD_SUBDIRECTORY(tga) ADD_SUBDIRECTORY(hdr) ADD_SUBDIRECTORY(dot) ADD_SUBDIRECTORY(vtf) +ADD_SUBDIRECTORY(ktx) IF(JPEG_FOUND) ADD_SUBDIRECTORY(jpeg) diff --git a/src/osgPlugins/ktx/CMakeLists.txt b/src/osgPlugins/ktx/CMakeLists.txt new file mode 100644 index 000000000..348792cf5 --- /dev/null +++ b/src/osgPlugins/ktx/CMakeLists.txt @@ -0,0 +1,5 @@ +SET(TARGET_SRC ReaderWriterKTX.cpp ) +SET(TARGET_H ReaderWriterKTX.h) + +#### end var setup ### +SETUP_PLUGIN(ktx) diff --git a/src/osgPlugins/ktx/ReaderWriterKTX.cpp b/src/osgPlugins/ktx/ReaderWriterKTX.cpp new file mode 100644 index 000000000..0fd3fe047 --- /dev/null +++ b/src/osgPlugins/ktx/ReaderWriterKTX.cpp @@ -0,0 +1,225 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 2012 APX Labs, LLC + * + * 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. +*/ + +#include "ReaderWriterKTX.h" +#include +#include +#include +#include + +const unsigned char ReaderWriterKTX::FileSignature[12] = { + 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A +}; + +ReaderWriterKTX::ReaderWriterKTX() +{ + supportsExtension("ktx", "KTX image format"); +} + +const char* ReaderWriterKTX::className() const { return "KTX Image Reader/Writer"; } + +bool ReaderWriterKTX::correctByteOrder(KTXTexHeader& header) const +{ + if (header.endianness == MyEndian) + return true; + + if (header.endianness != NotMyEndian) + return false; + + for (uint32_t* ptr = &header.glType; ptr <= &header.bytesOfKeyValueData; ++ptr) { + osg::swapBytes4(reinterpret_cast(ptr)); + } + + return true; +} + +osgDB::ReaderWriter::ReadResult ReaderWriterKTX::readKTXStream(std::istream& fin) const +{ + KTXTexHeader header; + fin.seekg(0, std::ios::end); + uint32_t fileLength = fin.tellg(); + fin.seekg(0, std::ios::beg); + + //read in the data for the header and store it + fin.read((char*)&header, sizeof(KTXTexHeader)); + if(!fin.good()) + { + OSG_WARN << "Failed to read KTX header." << std::endl; + return ReadResult(ReadResult::ERROR_IN_READING_FILE); + } + + //verify that the file is a ktx file from its identifier + if (memcmp(header.identifier, FileSignature, sizeof(FileSignature))) + { + OSG_WARN << "Failed to verify KTX header." << std::endl; + return ReadResult(ReadResult::FILE_NOT_HANDLED); + } + + //correct the byte order if the endianess doesn't match + if(correctByteOrder(header) == false) + { + OSG_WARN << "Corrupt KTX header (invalid endianness marker)" << std::endl; + return ReadResult(ReadResult::FILE_NOT_HANDLED); + } + + if(header.glFormat == 0) + header.glFormat = header.glInternalFormat; + + // KTX sets height to 0 for 1D textures, and depth to 0 for both 1D and 2D textures. + // OpenSceneGraph expects textures to have non-zero dimensions + if (header.pixelHeight == 0) + header.pixelHeight = 1; + if (header.pixelDepth == 0) + header.pixelDepth = 1; + + if(header.numberOfArrayElements != 0) + { + OSG_WARN << "Array textures in KTX files are not supported." << std::endl; + return ReadResult(ReadResult::FILE_NOT_HANDLED); + } + + if(header.numberOfFaces != 1) //if this is a cube map + { + OSG_WARN << "Cube maps cannot be read directly from KTX files." << std::endl; + return ReadResult(ReadResult::FILE_NOT_HANDLED); + } + + if (header.numberOfMipmapLevels == 0) + header.numberOfMipmapLevels = 1; + + //read keyvalue data. Will be ignoring for now + fin.ignore(header.bytesOfKeyValueData); + + uint32_t imageSize; + uint32_t totalImageSize = fileLength - + (sizeof(KTXTexHeader) + header.bytesOfKeyValueData + + (sizeof(imageSize) * header.numberOfMipmapLevels)); + + unsigned char* totalImageData = new unsigned char[totalImageSize]; + if (!totalImageData) + return ReadResult::INSUFFICIENT_MEMORY_TO_LOAD; + + char* imageData = (char*)totalImageData; + bool byteswapImageData = (header.glTypeSize > 1) && (header.endianness != MyEndian); + + uint32_t totalOffset = 0; + osg::Image::MipmapDataType mipmapData; + + for(uint32_t mipmapLevel = 0; mipmapLevel < header.numberOfMipmapLevels; mipmapLevel++) + { + fin.read((char*)&imageSize, sizeof(imageSize)); + if(!fin.good()) + { + OSG_WARN << "Failed to read Image Data." << std::endl; + delete[] totalImageData; + return ReadResult::ERROR_IN_READING_FILE; + } + if (header.endianness != MyEndian) + osg::swapBytes4(reinterpret_cast(&imageSize)); + + fin.read(imageData, imageSize); + if(!fin.good()) + { + OSG_WARN << "Failed to read Image Data." << std::endl; + delete[] totalImageData; + return ReadResult::ERROR_IN_READING_FILE; + } + + if (byteswapImageData) + { + char* endData = imageData + imageSize; + if (header.glTypeSize == 4) + { + for(char* longData = imageData; longData < endData; longData += 4) + { + osg::swapBytes4(longData); + } + } + else if (header.glTypeSize == 2) + { + for(char* shortData = imageData; shortData < endData; shortData += 2) + { + osg::swapBytes2(shortData); + } + } + } + + if(mipmapLevel > 0) + mipmapData.push_back(totalOffset); + + //move the offset to the next imageSize data + totalOffset += imageSize; + + // advance buffer pointer to read next mipmap level + imageData += imageSize; + + if (mipmapLevel < (header.numberOfMipmapLevels - 1)) + { + uint32_t mipPadding = 3 - (imageSize + 3) % 4; + if (mipPadding > 0) + { + fin.read(imageData, mipPadding); + imageData += mipPadding; + totalOffset += mipPadding; + } + } + } + + osg::ref_ptr image = new osg::Image; + if (!image.valid()) + { + delete[] totalImageData; + return ReadResult::INSUFFICIENT_MEMORY_TO_LOAD; + } + + image->setImage(header.pixelWidth, header.pixelHeight, header.pixelDepth, + header.glInternalFormat, header.glFormat, + header.glType, totalImageData, osg::Image::USE_NEW_DELETE); + + if (header.numberOfMipmapLevels > 1) + image->setMipmapLevels(mipmapData); + + return image.get(); +} + + +osgDB::ReaderWriter::ReadResult ReaderWriterKTX::readImage(std::istream& fin,const osgDB::ReaderWriter::Options*) const +{ + return readKTXStream(fin); +} + + +osgDB::ReaderWriter::ReadResult ReaderWriterKTX::readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const +{ + std::string ext = osgDB::getLowerCaseFileExtension(file); + if(!acceptsExtension(ext)) + return ReadResult::FILE_NOT_HANDLED; + + std::string fileName = osgDB::findDataFile(file, options); + if(fileName.empty()) + return ReadResult::FILE_NOT_FOUND; + + std::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary); + if(!istream) + return ReadResult::ERROR_IN_READING_FILE; + + ReadResult rr = readKTXStream(istream); + if(rr.validImage()) + rr.getImage()->setFileName(file); + + return rr; +} + +// now register with Registry to instantiate the above +// reader/writer. +REGISTER_OSGPLUGIN(ktx, ReaderWriterKTX) diff --git a/src/osgPlugins/ktx/ReaderWriterKTX.h b/src/osgPlugins/ktx/ReaderWriterKTX.h new file mode 100644 index 000000000..ed85b5cbf --- /dev/null +++ b/src/osgPlugins/ktx/ReaderWriterKTX.h @@ -0,0 +1,52 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 2012 APX Labs, LLC + * + * 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. +*/ + +#include +#include + +struct KTXTexHeader +{ + uint8_t identifier[12]; + uint32_t endianness; + uint32_t glType; + uint32_t glTypeSize; + uint32_t glFormat; + uint32_t glInternalFormat; + uint32_t glBaseInternalFormat; + uint32_t pixelWidth; + uint32_t pixelHeight; + uint32_t pixelDepth; + uint32_t numberOfArrayElements; + uint32_t numberOfFaces; + uint32_t numberOfMipmapLevels; + uint32_t bytesOfKeyValueData; +}; + + +class ReaderWriterKTX : public osgDB::ReaderWriter +{ +public: + static const unsigned char FileSignature[12]; + static const uint32_t MyEndian = 0x04030201; + static const uint32_t NotMyEndian = 0x01020304; + + ReaderWriterKTX(); + + virtual const char* className() const; + virtual ReadResult readImage(std::istream& fin,const osgDB::ReaderWriter::Options* =NULL) const; + virtual ReadResult readKTXStream(std::istream& fin) const; + virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const; + +private: + bool correctByteOrder(KTXTexHeader& header) const; +};