#include "imagemanager.hpp" #include #include #include #include #include #include "objectcache.hpp" #ifdef OSG_LIBRARY_STATIC // This list of plugins should match with the list in the top-level CMakelists.txt. USE_OSGPLUGIN(png) USE_OSGPLUGIN(tga) USE_OSGPLUGIN(dds) USE_OSGPLUGIN(jpeg) USE_OSGPLUGIN(bmp) USE_OSGPLUGIN(osg) USE_SERIALIZER_WRAPPER_LIBRARY(osg) #endif namespace { osg::ref_ptr createWarningImage() { osg::ref_ptr warningImage = new osg::Image; int width = 8, height = 8; warningImage->allocateImage(width, height, 1, GL_RGB, GL_UNSIGNED_BYTE); assert(warningImage->isDataContiguous()); unsigned char* data = warningImage->data(); for (int i = 0; i < width * height; ++i) { data[3 * i] = (255); data[3 * i + 1] = (0); data[3 * i + 2] = (255); } return warningImage; } } namespace Resource { ImageManager::ImageManager(const VFS::Manager* vfs) : ResourceManager(vfs) , mWarningImage(createWarningImage()) , mOptions(new osgDB::Options("dds_flip dds_dxt1_detect_rgba ignoreTga2Fields")) , mOptionsNoFlip(new osgDB::Options("dds_dxt1_detect_rgba ignoreTga2Fields")) { } ImageManager::~ImageManager() {} bool checkSupported(osg::Image* image, const std::string& filename) { switch (image->getPixelFormat()) { case (GL_COMPRESSED_RGB_S3TC_DXT1_EXT): case (GL_COMPRESSED_RGBA_S3TC_DXT1_EXT): case (GL_COMPRESSED_RGBA_S3TC_DXT3_EXT): case (GL_COMPRESSED_RGBA_S3TC_DXT5_EXT): { osg::GLExtensions* exts = osg::GLExtensions::Get(0, false); if (exts && !exts->isTextureCompressionS3TCSupported // This one works too. Should it be included in isTextureCompressionS3TCSupported()? Submitted as a // patch to OSG. && !osg::isGLExtensionSupported(0, "GL_S3_s3tc")) { return false; } break; } // not bothering with checks for other compression formats right now, we are unlikely to ever use those // anyway default: return true; } return true; } osg::ref_ptr ImageManager::getImage(const std::string& filename, bool disableFlip) { const std::string normalized = mVFS->normalizeFilename(filename); osg::ref_ptr obj = mCache->getRefFromObjectCache(normalized); if (obj) return osg::ref_ptr(static_cast(obj.get())); else { Files::IStreamPtr stream; try { stream = mVFS->get(normalized); } catch (std::exception& e) { Log(Debug::Error) << "Failed to open image: " << e.what(); mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } const std::string ext(Misc::getFileExtension(normalized)); osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext); if (!reader) { Log(Debug::Error) << "Error loading " << filename << ": no readerwriter for '" << ext << "' found"; mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } bool killAlpha = false; if (reader->supportedExtensions().count("tga")) { // Morrowind ignores the alpha channel of 16bpp TGA files even when the header says not to unsigned char header[18]; stream->read((char*)header, 18); if (stream->gcount() != 18) { Log(Debug::Error) << "Error loading " << filename << ": couldn't read TGA header"; mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } int type = header[2]; int depth; if (type == 1 || type == 9) depth = header[7]; else depth = header[16]; int alphaBPP = header[17] & 0x0F; killAlpha = depth == 16 && alphaBPP == 1; stream->seekg(0); } osgDB::ReaderWriter::ReadResult result = reader->readImage(*stream, disableFlip ? mOptionsNoFlip : mOptions); if (!result.success()) { Log(Debug::Error) << "Error loading " << filename << ": " << result.message() << " code " << result.status(); mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } osg::ref_ptr image = result.getImage(); image->setFileName(normalized); if (!checkSupported(image, filename)) { static bool uncompress = (getenv("OPENMW_DECOMPRESS_TEXTURES") != nullptr); if (!uncompress) { Log(Debug::Error) << "Error loading " << filename << ": no S3TC texture compression support installed"; mCache->addEntryToObjectCache(normalized, mWarningImage); return mWarningImage; } else { // decompress texture in software if not supported by GPU // requires update to getColor() to be released with OSG 3.6 osg::ref_ptr newImage = new osg::Image; newImage->setFileName(image->getFileName()); newImage->allocateImage(image->s(), image->t(), image->r(), image->isImageTranslucent() ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE); for (int s = 0; s < image->s(); ++s) for (int t = 0; t < image->t(); ++t) for (int r = 0; r < image->r(); ++r) newImage->setColor(image->getColor(s, t, r), s, t, r); image = newImage; } } else if (killAlpha) { osg::ref_ptr newImage = new osg::Image; newImage->setFileName(image->getFileName()); newImage->allocateImage(image->s(), image->t(), image->r(), GL_RGB, GL_UNSIGNED_BYTE); // OSG just won't write the alpha as there's nowhere to put it. for (int s = 0; s < image->s(); ++s) for (int t = 0; t < image->t(); ++t) for (int r = 0; r < image->r(); ++r) newImage->setColor(image->getColor(s, t, r), s, t, r); image = newImage; } mCache->addEntryToObjectCache(normalized, image); return image; } } osg::Image* ImageManager::getWarningImage() { return mWarningImage; } void ImageManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const { stats->setAttribute(frameNumber, "Image", mCache->getCacheSize()); } }