mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 05:26:39 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			207 lines
		
	
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			207 lines
		
	
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "imagemanager.hpp"
 | 
						|
 | 
						|
#include <cassert>
 | 
						|
#include <osgDB/Registry>
 | 
						|
 | 
						|
#include <components/debug/debuglog.hpp>
 | 
						|
#include <components/misc/pathhelpers.hpp>
 | 
						|
#include <components/sceneutil/glextensions.hpp>
 | 
						|
#include <components/vfs/manager.hpp>
 | 
						|
#include <components/vfs/pathutil.hpp>
 | 
						|
 | 
						|
#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<osg::Image> createWarningImage()
 | 
						|
    {
 | 
						|
        osg::ref_ptr<osg::Image> 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, double expiryDelay)
 | 
						|
        : ResourceManager(vfs, expiryDelay)
 | 
						|
        , 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)
 | 
						|
    {
 | 
						|
        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):
 | 
						|
            {
 | 
						|
                if (!SceneUtil::glExtensionsReady())
 | 
						|
                    return true; // hashtag yolo (CS might not have context when loading assets)
 | 
						|
                osg::GLExtensions& exts = SceneUtil::getGLExtensions();
 | 
						|
                if (!exts.isTextureCompressionS3TCSupported
 | 
						|
                    // This one works too. Should it be included in isTextureCompressionS3TCSupported()? Submitted as a
 | 
						|
                    // patch to OSG.
 | 
						|
                    && !osg::isGLExtensionSupported(exts.contextID, "GL_S3_s3tc"))
 | 
						|
                {
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            // not bothering with checks for other compression formats right now
 | 
						|
            default:
 | 
						|
                return true;
 | 
						|
        }
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    osg::ref_ptr<osg::Image> ImageManager::getImage(std::string_view filename, bool disableFlip)
 | 
						|
    {
 | 
						|
        const std::string normalized = VFS::Path::normalizeFilename(filename);
 | 
						|
 | 
						|
        osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);
 | 
						|
        if (obj)
 | 
						|
            return osg::ref_ptr<osg::Image>(static_cast<osg::Image*>(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<osg::Image> image = result.getImage();
 | 
						|
 | 
						|
            image->setFileName(normalized);
 | 
						|
            if (!checkSupported(image))
 | 
						|
            {
 | 
						|
                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<osg::Image> 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<osg::Image> 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
 | 
						|
    {
 | 
						|
        Resource::reportStats("Image", frameNumber, mCache->getStats(), *stats);
 | 
						|
    }
 | 
						|
 | 
						|
}
 |