2016-02-05 22:03:53 +00:00
|
|
|
#include "imagemanager.hpp"
|
2015-03-28 01:20:20 +00:00
|
|
|
|
2017-05-05 19:21:11 +00:00
|
|
|
#include <cassert>
|
2015-03-28 01:20:20 +00:00
|
|
|
#include <osgDB/Registry>
|
|
|
|
|
2018-08-14 15:42:41 +00:00
|
|
|
#include <components/debug/debuglog.hpp>
|
2021-09-11 13:49:47 +00:00
|
|
|
#include <components/misc/pathhelpers.hpp>
|
2015-03-28 01:20:20 +00:00
|
|
|
#include <components/vfs/manager.hpp>
|
2023-05-31 21:11:03 +00:00
|
|
|
#include <components/vfs/pathutil.hpp>
|
2015-03-28 01:20:20 +00:00
|
|
|
|
2016-02-05 22:31:59 +00:00
|
|
|
#include "objectcache.hpp"
|
|
|
|
|
2015-07-17 20:13:43 +00:00
|
|
|
#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)
|
2016-10-08 17:20:44 +00:00
|
|
|
USE_OSGPLUGIN(bmp)
|
|
|
|
USE_OSGPLUGIN(osg)
|
|
|
|
USE_SERIALIZER_WRAPPER_LIBRARY(osg)
|
2015-07-17 20:13:43 +00:00
|
|
|
#endif
|
|
|
|
|
2015-03-28 01:57:39 +00:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
2016-02-05 22:05:37 +00:00
|
|
|
osg::ref_ptr<osg::Image> createWarningImage()
|
2015-03-28 01:57:39 +00:00
|
|
|
{
|
|
|
|
osg::ref_ptr<osg::Image> warningImage = new osg::Image;
|
|
|
|
|
2015-05-02 16:06:17 +00:00
|
|
|
int width = 8, height = 8;
|
|
|
|
warningImage->allocateImage(width, height, 1, GL_RGB, GL_UNSIGNED_BYTE);
|
2022-09-22 18:26:05 +00:00
|
|
|
assert(warningImage->isDataContiguous());
|
2015-05-02 16:06:17 +00:00
|
|
|
unsigned char* data = warningImage->data();
|
2022-09-22 18:26:05 +00:00
|
|
|
for (int i = 0; i < width * height; ++i)
|
2015-03-28 01:57:39 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
data[3 * i] = (255);
|
|
|
|
data[3 * i + 1] = (0);
|
|
|
|
data[3 * i + 2] = (255);
|
2015-03-28 01:57:39 +00:00
|
|
|
}
|
2016-02-05 22:05:37 +00:00
|
|
|
return warningImage;
|
2015-03-28 01:57:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-03-28 01:20:20 +00:00
|
|
|
namespace Resource
|
|
|
|
{
|
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
ImageManager::ImageManager(const VFS::Manager* vfs)
|
2016-02-06 15:57:54 +00:00
|
|
|
: ResourceManager(vfs)
|
2016-02-05 22:05:37 +00:00
|
|
|
, mWarningImage(createWarningImage())
|
2020-04-29 11:14:53 +00:00
|
|
|
, mOptions(new osgDB::Options("dds_flip dds_dxt1_detect_rgba ignoreTga2Fields"))
|
2022-05-15 06:06:32 +00:00
|
|
|
, mOptionsNoFlip(new osgDB::Options("dds_dxt1_detect_rgba ignoreTga2Fields"))
|
2015-03-28 01:20:20 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
ImageManager::~ImageManager() {}
|
2015-05-08 15:52:35 +00:00
|
|
|
|
2023-05-21 14:39:32 +00:00
|
|
|
bool checkSupported(osg::Image* image)
|
2015-07-28 21:35:10 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
switch (image->getPixelFormat())
|
2015-07-28 21:35:10 +00:00
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
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):
|
2015-07-28 21:35:10 +00:00
|
|
|
{
|
2015-07-30 09:45:10 +00:00
|
|
|
osg::GLExtensions* exts = osg::GLExtensions::Get(0, false);
|
2022-09-22 18:26:05 +00:00
|
|
|
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"))
|
2015-07-28 21:35:10 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2022-09-22 18:26:05 +00:00
|
|
|
// not bothering with checks for other compression formats right now, we are unlikely to ever use those
|
|
|
|
// anyway
|
2015-07-28 21:35:10 +00:00
|
|
|
default:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-05-21 14:39:32 +00:00
|
|
|
osg::ref_ptr<osg::Image> ImageManager::getImage(std::string_view filename, bool disableFlip)
|
2015-11-16 22:26:43 +00:00
|
|
|
{
|
2023-05-31 21:11:03 +00:00
|
|
|
const std::string normalized = VFS::Path::normalizeFilename(filename);
|
2016-02-05 22:31:59 +00:00
|
|
|
|
|
|
|
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(normalized);
|
|
|
|
if (obj)
|
|
|
|
return osg::ref_ptr<osg::Image>(static_cast<osg::Image*>(obj.get()));
|
2015-11-16 22:26:43 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
Files::IStreamPtr stream;
|
|
|
|
try
|
|
|
|
{
|
2021-06-23 20:19:08 +00:00
|
|
|
stream = mVFS->get(normalized);
|
2015-11-16 22:26:43 +00:00
|
|
|
}
|
|
|
|
catch (std::exception& e)
|
|
|
|
{
|
2018-08-14 15:42:41 +00:00
|
|
|
Log(Debug::Error) << "Failed to open image: " << e.what();
|
2016-02-05 22:31:59 +00:00
|
|
|
mCache->addEntryToObjectCache(normalized, mWarningImage);
|
2016-02-05 21:58:02 +00:00
|
|
|
return mWarningImage;
|
2015-11-16 22:26:43 +00:00
|
|
|
}
|
|
|
|
|
2021-09-11 13:49:47 +00:00
|
|
|
const std::string ext(Misc::getFileExtension(normalized));
|
2015-11-16 22:26:43 +00:00
|
|
|
osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext);
|
|
|
|
if (!reader)
|
|
|
|
{
|
2018-08-14 15:42:41 +00:00
|
|
|
Log(Debug::Error) << "Error loading " << filename << ": no readerwriter for '" << ext << "' found";
|
2016-02-05 22:31:59 +00:00
|
|
|
mCache->addEntryToObjectCache(normalized, mWarningImage);
|
2016-02-05 21:58:02 +00:00
|
|
|
return mWarningImage;
|
2015-11-16 22:26:43 +00:00
|
|
|
}
|
|
|
|
|
2020-09-11 23:20:44 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
osgDB::ReaderWriter::ReadResult result
|
|
|
|
= reader->readImage(*stream, disableFlip ? mOptionsNoFlip : mOptions);
|
2015-11-16 22:26:43 +00:00
|
|
|
if (!result.success())
|
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
Log(Debug::Error) << "Error loading " << filename << ": " << result.message() << " code "
|
|
|
|
<< result.status();
|
2016-02-05 22:31:59 +00:00
|
|
|
mCache->addEntryToObjectCache(normalized, mWarningImage);
|
2016-02-05 21:58:02 +00:00
|
|
|
return mWarningImage;
|
2015-11-16 22:26:43 +00:00
|
|
|
}
|
|
|
|
|
2017-10-14 18:28:46 +00:00
|
|
|
osg::ref_ptr<osg::Image> image = result.getImage();
|
|
|
|
|
2016-02-20 16:57:19 +00:00
|
|
|
image->setFileName(normalized);
|
2023-05-21 14:39:32 +00:00
|
|
|
if (!checkSupported(image))
|
2015-11-16 22:26:43 +00:00
|
|
|
{
|
2020-11-13 07:39:47 +00:00
|
|
|
static bool uncompress = (getenv("OPENMW_DECOMPRESS_TEXTURES") != nullptr);
|
2017-10-14 18:28:46 +00:00
|
|
|
if (!uncompress)
|
|
|
|
{
|
2022-09-22 18:26:05 +00:00
|
|
|
Log(Debug::Error) << "Error loading " << filename
|
|
|
|
<< ": no S3TC texture compression support installed";
|
2017-10-14 18:28:46 +00:00
|
|
|
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());
|
2022-09-22 18:26:05 +00:00
|
|
|
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);
|
2017-10-14 18:28:46 +00:00
|
|
|
image = newImage;
|
|
|
|
}
|
2015-11-16 22:26:43 +00:00
|
|
|
}
|
2020-09-11 23:20:44 +00:00
|
|
|
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;
|
|
|
|
}
|
2015-11-16 22:26:43 +00:00
|
|
|
|
2016-02-05 22:31:59 +00:00
|
|
|
mCache->addEntryToObjectCache(normalized, image);
|
2015-11-16 22:26:43 +00:00
|
|
|
return image;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
osg::Image* ImageManager::getWarningImage()
|
2015-06-19 18:55:04 +00:00
|
|
|
{
|
2016-02-05 22:05:37 +00:00
|
|
|
return mWarningImage;
|
2015-06-19 18:55:04 +00:00
|
|
|
}
|
|
|
|
|
2022-09-22 18:26:05 +00:00
|
|
|
void ImageManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const
|
2017-02-22 01:18:18 +00:00
|
|
|
{
|
|
|
|
stats->setAttribute(frameNumber, "Image", mCache->getCacheSize());
|
|
|
|
}
|
|
|
|
|
2015-03-28 01:20:20 +00:00
|
|
|
}
|