diff --git a/src/osgPlugins/ktx/ReaderWriterKTX.cpp b/src/osgPlugins/ktx/ReaderWriterKTX.cpp index 5aa3371bf..dee4ccddd 100644 --- a/src/osgPlugins/ktx/ReaderWriterKTX.cpp +++ b/src/osgPlugins/ktx/ReaderWriterKTX.cpp @@ -17,6 +17,24 @@ #include #include +// Macro similar to what's in FLT/TRP plugins (except it uses wide char under Windows if OSG_USE_UTF8_FILENAME) +#if defined(_WIN32) +#include +#include +#include +#ifdef OSG_USE_UTF8_FILENAME +#define DELETEFILE(file) DeleteFileW(osgDB::convertUTF8toUTF16((file)).c_str()) +#else +#define DELETEFILE(file) DeleteFileA((file)) +#endif + +#else // Unix + +#include +#define DELETEFILE(file) remove((file)) + +#endif + const unsigned char ReaderWriterKTX::FileSignature[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }; @@ -127,6 +145,12 @@ osgDB::ReaderWriter::ReadResult ReaderWriterKTX::readKTXStream(std::istream& fin if (header.endianness != MyEndian) osg::swapBytes4(reinterpret_cast(&imageSize)); + if (totalOffset + imageSize > totalImageSize) { + OSG_WARN << "Failed to read mipmap: " << mipmapLevel << " not enough bytes in file." << std::endl; + delete[] totalImageData; + return ReadResult::ERROR_IN_READING_FILE; + } + fin.read(imageData, imageSize); if(!fin.good()) { @@ -192,7 +216,64 @@ osgDB::ReaderWriter::ReadResult ReaderWriterKTX::readKTXStream(std::istream& fin return image.get(); } +bool ReaderWriterKTX::writeKTXStream(const osg::Image *img, std::ostream& fout) const { + KTXTexHeader header; + memcpy(header.identifier, FileSignature, sizeof(FileSignature)); + header.endianness = MyEndian; + header.glType = img->getDataType(); + header.glTypeSize = 1; + if (!img->isCompressed()) { + header.glTypeSize = img->getPixelSizeInBits() / 8; + } + header.glFormat = img->getPixelFormat(); + header.glInternalFormat = img->getInternalTextureFormat(); + header.glBaseInternalFormat = img->computePixelFormat(header.glType); + header.pixelWidth =img->s(); + header.pixelHeight = img->t() > 1 ? img->t() : 0; + header.pixelDepth = img->r() > 1 ? img->r() : 0; + header.numberOfArrayElements = 0; + header.numberOfFaces = 1; + header.numberOfMipmapLevels = img->getNumMipmapLevels(); + header.bytesOfKeyValueData = 0; + fout.write(reinterpret_cast(&header), sizeof(header)); /* write file header */ + uint32_t imageSize; + int s = img->s(); + int t = img->t(); + int r = img->r(); + + osg::Image::DataIterator imgData(img); + unsigned int imgDataOffset = 0; + //write main image: imageSize bytes + for (uint32_t mipmapLevel = 0; mipmapLevel < header.numberOfMipmapLevels; mipmapLevel++) + { + imageSize = osg::Image::computeImageSizeInBytes(s, t, r, img->getPixelFormat(), img->getDataType(), img->getPacking()); + fout.write(reinterpret_cast(&imageSize), sizeof(imageSize)); + { + unsigned int bytesWritten = 0; + unsigned int bytesToWrite = imageSize - bytesWritten; + while (imgData.valid() && (bytesWritten < imageSize)) { + unsigned int blockSize = osg::minimum(imgData.size() - imgDataOffset, bytesToWrite); + fout.write(reinterpret_cast(imgData.data()), blockSize); + bytesWritten += blockSize; + imgDataOffset += blockSize; + if (imgData.size() == imgDataOffset) { + ++imgData; + imgDataOffset = 0; + } + } + } + if (s > 1) s >>= 1; + if (t > 1) t >>= 1; + if (r > 1) r >>= 1; + } + // Check for correct saving + if (fout.fail()) + return false;//report failure + + // If we get that far the file was saved properly + return true; +} osgDB::ReaderWriter::ReadResult ReaderWriterKTX::readImage(std::istream& fin,const osgDB::ReaderWriter::Options*) const { return readKTXStream(fin); @@ -219,7 +300,53 @@ osgDB::ReaderWriter::ReadResult ReaderWriterKTX::readImage(const std::string& fi return rr; } +/////////////////// +osgDB::ReaderWriter::WriteResult ReaderWriterKTX::writeObject(const osg::Object& object, const std::string& file, const osgDB::ReaderWriter::Options* options) const +{ + const osg::Image* image = dynamic_cast(&object); + if (!image) return WriteResult::FILE_NOT_HANDLED; + return writeImage(*image, file, options); +} + +osgDB::ReaderWriter::WriteResult ReaderWriterKTX::writeObject(const osg::Object& object, std::ostream& fout, const Options* options) const +{ + const osg::Image* image = dynamic_cast(&object); + if (!image) return WriteResult::FILE_NOT_HANDLED; + + return writeImage(*image, fout, options); +} + + +osgDB::ReaderWriter::WriteResult ReaderWriterKTX::writeImage(const osg::Image &image, const std::string& file, const osgDB::ReaderWriter::Options* options) const +{ + std::string ext = osgDB::getFileExtension(file); + if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED; + + osgDB::ofstream fout(file.c_str(), std::ios::out | std::ios::binary); + if (!fout) return WriteResult::ERROR_IN_WRITING_FILE; + + WriteResult res(writeImage(image, fout, options)); + if (!res.success()) { + // Remove file on failure + fout.close(); + DELETEFILE(file.c_str()); + OSG_WARN << "ReaderWriterKTX::writeImage Failed to write " << file << "." << std::endl; + } + OSG_INFO << "ReaderWriterKTX::writeImage write " << file << " sucess;" << image.s() << "x" << image.t() << "x" << image.r() << std::endl; + return res; +} + +osgDB::ReaderWriter::WriteResult ReaderWriterKTX::writeImage(const osg::Image& image, std::ostream& fout, const Options* options) const +{ +// bool noAutoFlipDDSWrite = options && options->getOptionString().find("ddsNoAutoFlipWrite") != std::string::npos; //maybe for ktx too? + bool success = writeKTXStream(&image, fout); + + if (success) + return WriteResult::FILE_SAVED; + else + return WriteResult::ERROR_IN_WRITING_FILE; +} // 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 index 71bd4d899..d640344a2 100644 --- a/src/osgPlugins/ktx/ReaderWriterKTX.h +++ b/src/osgPlugins/ktx/ReaderWriterKTX.h @@ -44,9 +44,15 @@ public: 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; + virtual WriteResult writeObject(const osg::Object& object, const std::string& file, const osgDB::ReaderWriter::Options* options) const; + virtual WriteResult writeObject(const osg::Object& object, std::ostream& fout, const Options* options) const; + virtual WriteResult writeImage(const osg::Image &image, const std::string& file, const osgDB::ReaderWriter::Options* options) const; + virtual WriteResult writeImage(const osg::Image& image, std::ostream& fout, const Options* options) const; + + ReadResult readKTXStream(std::istream& fin) const; + bool writeKTXStream(const osg::Image *img, std::ostream& fout) const; private: bool correctByteOrder(KTXTexHeader& header) const; };