tahoma2d/toonz/sources/toonzlib/imagemanager.cpp

497 lines
15 KiB
C++
Raw Normal View History

2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
// Toonz core includes
2016-03-19 06:57:51 +13:00
#include "timagecache.h"
#include "trasterimage.h"
#include "ttoonzimage.h"
#include "tmeshimage.h"
#include "timage_io.h"
2016-06-15 18:43:10 +12:00
// Qt includes (mutexing classes)
2016-03-19 06:57:51 +13:00
#include <QMutex>
#include <QMutexLocker>
#include <QReadWriteLock>
#include <QReadLocker>
#include <QWriteLocker>
#include "toonz/imagemanager.h"
#include "toonz/txshsimplelevel.h"
/* EXPLANATION (by Daniele):
2016-06-15 18:43:10 +12:00
Images / Image Infos retrieval is quite a frequent task throughout Toonz - in
particular,
as Render operations tend to be multithreaded, it is important to ensure that
the
2016-03-19 06:57:51 +13:00
ImageManager treats these operations efficiently.
2016-06-15 18:43:10 +12:00
Most of the image manager's job is that of caching hard-built image data so
that successive
2016-03-19 06:57:51 +13:00
queries avoid rebuilding the same images again.
2016-06-15 18:43:10 +12:00
In a multithreaded environment, we must make sure that multiple threads block
each other out
2016-03-19 06:57:51 +13:00
as little as possible.
Here are the main performance and threading notes:
2016-06-15 18:43:10 +12:00
- Image infos are completely cached, while image cachability is
user-specified.
2016-03-19 06:57:51 +13:00
This is needed as some images must be loaded only temporarily.
2016-06-15 18:43:10 +12:00
- One mutex will be used to protect the bindings table. It is the outermost
mutex.
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
- One mutex (read/write lock) will be used to protect access to EACH
individual image.
Testing should be required. If file access is found to be too strictly
sequential,
2016-03-19 06:57:51 +13:00
perhaps a single mutex could suffice.
2016-06-15 18:43:10 +12:00
- Having different mutexes to protect images and image infos is currently
not implemented,
2016-03-19 06:57:51 +13:00
but could be. Testing required.
*/
/*
2016-06-15 18:43:10 +12:00
TODO: TXshSimpleLevel::setFrame(...) usa aggiunte/rimozioni manuali di
immagini associate
a binding nell'ImageManager - aspettandosi che poiche' l'immagine e' presente
in cache,
2016-03-19 06:57:51 +13:00
verra' beccata...
*/
//************************************************************************************
// Image Builder implementation
//************************************************************************************
DEFINE_CLASS_CODE(ImageBuilder, 100)
//-----------------------------------------------------------------------------
ImageBuilder::ImageBuilder()
2016-06-15 18:43:10 +12:00
: TSmartObject(m_classCode)
, m_imageBuildingLock(QReadWriteLock::Recursive)
, m_cached(false)
, m_modified(false)
, m_imFlags(ImageManager::none) {}
2016-03-19 06:57:51 +13:00
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
ImageBuilder::~ImageBuilder() {}
2016-03-19 06:57:51 +13:00
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
bool ImageBuilder::areInfosCompatible(int imFlags, void *extData) {
return m_info.m_valid;
2016-03-19 06:57:51 +13:00
}
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
bool ImageBuilder::isImageCompatible(int imFlags, void *extData) {
return m_info.m_valid;
2016-03-19 06:57:51 +13:00
}
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
bool ImageBuilder::setImageInfo(TImageInfo &info, const TDimension &size) {
info = TImageInfo();
info.m_lx = size.lx;
info.m_ly = size.ly;
info.m_x0 = 0;
info.m_y0 = 0;
info.m_x1 = size.lx - 1;
info.m_y1 = size.ly - 1;
info.m_valid = true;
return true;
2016-03-19 06:57:51 +13:00
}
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
bool ImageBuilder::setImageInfo(TImageInfo &info, TImage *img) {
info = TImageInfo();
if (TRasterImageP ri = TRasterImageP(img)) {
TRasterP ras = ri->getRaster();
info.m_lx = ras->getLx();
info.m_ly = ras->getLy();
ri->getDpi(info.m_dpix, info.m_dpiy);
TRect savebox = ri->getSavebox();
info.m_x0 = savebox.x0;
info.m_y0 = savebox.y0;
info.m_x1 = savebox.x1;
info.m_y1 = savebox.y1;
} else if (TToonzImageP ti = TToonzImageP(img)) {
TRasterP ras = ti->getRaster();
info.m_lx = ras->getLx();
info.m_ly = ras->getLy();
ti->getDpi(info.m_dpix, info.m_dpiy);
TRect savebox = ti->getSavebox();
info.m_x0 = savebox.x0;
info.m_y0 = savebox.y0;
info.m_x1 = savebox.x1;
info.m_y1 = savebox.y1;
} else if (TMeshImageP mi = TMeshImageP(img)) {
mi->getDpi(info.m_dpix, info.m_dpiy);
}
info.m_valid = true;
return true;
2016-03-19 06:57:51 +13:00
}
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
bool ImageBuilder::setImageInfo(TImageInfo &info, TImageReader *ir) {
info = TImageInfo();
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
const TImageInfo *tmp = ir->getImageInfo();
if (tmp) {
info = *tmp;
if (info.m_x1 < info.m_x0 || info.m_y1 < info.m_y0) {
info.m_x0 = info.m_y0 = 0;
info.m_x1 = info.m_lx - 1;
info.m_y1 = info.m_ly - 1;
}
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
info.m_valid = true;
return true;
}
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
return false;
2016-03-19 06:57:51 +13:00
}
//************************************************************************************
// Image Manager Privates implementation
//************************************************************************************
struct ImageManager::Imp {
2016-06-15 18:43:10 +12:00
QReadWriteLock m_tableLock; //!< Lock for the builders table
std::map<std::string, ImageBuilderP>
m_builders; //!< identifier -> ImageBuilder table
2016-03-19 06:57:51 +13:00
public:
2016-06-15 18:43:10 +12:00
Imp() : m_tableLock(QReadWriteLock::Recursive) {}
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
void clear() { m_builders.clear(); }
2016-03-19 06:57:51 +13:00
};
//************************************************************************************
// Image Manager implementation
//************************************************************************************
2016-06-15 18:43:10 +12:00
ImageManager::ImageManager() : m_imp(new Imp) {}
2016-03-19 06:57:51 +13:00
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
ImageManager::~ImageManager() {}
2016-03-19 06:57:51 +13:00
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
ImageManager *ImageManager::instance() {
// Re-introdotto possibile baco: voglio controllare se esiste ancora
static ImageManager theInstance;
return &theInstance;
2016-03-19 06:57:51 +13:00
}
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
void ImageManager::bind(const std::string &id, ImageBuilder *builderPtr) {
if (!builderPtr) {
unbind(id);
return;
}
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
QWriteLocker locker(&m_imp->m_tableLock);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
ImageBuilderP &builderP = m_imp->m_builders[id];
if (builderP && builderP->m_cached) TImageCache::instance()->remove(id);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
builderP = builderPtr;
2016-03-19 06:57:51 +13:00
}
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
bool ImageManager::unbind(const std::string &id) {
QWriteLocker locker(&m_imp->m_tableLock);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
std::map<std::string, ImageBuilderP>::iterator it =
m_imp->m_builders.find(id);
if (it == m_imp->m_builders.end()) return false;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
ImageBuilderP &builderP = it->second;
if (builderP && builderP->m_cached) TImageCache::instance()->remove(id);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
m_imp->m_builders.erase(it);
return true;
2016-03-19 06:57:51 +13:00
}
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
bool ImageManager::isBound(const std::string &id) const {
QReadLocker locker(&m_imp->m_tableLock);
return m_imp->m_builders.find(id) != m_imp->m_builders.end();
2016-03-19 06:57:51 +13:00
}
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
bool ImageManager::rebind(const std::string &srcId, const std::string &dstId) {
QWriteLocker locker(&m_imp->m_tableLock);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
std::map<std::string, ImageBuilderP>::iterator st =
m_imp->m_builders.find(srcId);
if (st == m_imp->m_builders.end()) return false;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
ImageBuilderP builder = st->second;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
m_imp->m_builders.erase(st);
m_imp->m_builders[dstId] = builder;
2016-03-19 06:57:51 +13:00
2017-10-02 23:30:28 +13:00
m_imp->m_builders[dstId]->m_cached = true;
m_imp->m_builders[dstId]->m_modified = true;
2016-06-15 18:43:10 +12:00
TImageCache::instance()->remap(dstId, srcId);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
return true;
2016-03-19 06:57:51 +13:00
}
2017-10-02 23:30:28 +13:00
bool ImageManager::renumber(const std::string &srcId, const TFrameId &fid) {
std::map<std::string, ImageBuilderP>::iterator st =
m_imp->m_builders.find(srcId);
if (st == m_imp->m_builders.end()) return false;
m_imp->m_builders[srcId]->setFid(fid);
return true;
}
2016-03-19 06:57:51 +13:00
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
void ImageManager::clear() {
QWriteLocker locker(&m_imp->m_tableLock);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
TImageCache::instance()->clearSceneImages();
m_imp->clear();
2016-03-19 06:57:51 +13:00
}
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
TImageInfo *ImageManager::getInfo(const std::string &id, int imFlags,
void *extData) {
// Lock for table read and try to find data in the cache
QReadLocker tableLocker(&m_imp->m_tableLock);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
std::map<std::string, ImageBuilderP>::iterator it =
m_imp->m_builders.find(id);
if (it == m_imp->m_builders.end()) return 0;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
ImageBuilderP &builder = it->second;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
assert(!((imFlags & ImageManager::toBeModified) && !builder->m_modified));
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
// Check cached data
if (builder->areInfosCompatible(imFlags, extData)) return &builder->m_info;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
QWriteLocker imageBuildingLocker(&builder->m_imageBuildingLock);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
// Re-check as waiting may have changed the situation
if (builder->areInfosCompatible(imFlags, extData)) return &builder->m_info;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
TImageInfo info;
if (builder->getInfo(info, imFlags, extData)) {
builder->m_info = info;
return &builder->m_info;
}
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
return 0;
2016-03-19 06:57:51 +13:00
}
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
TImageP ImageManager::getImage(const std::string &id, int imFlags,
void *extData) {
assert(!((imFlags & ImageManager::toBeModified) &&
(imFlags & ImageManager::dontPutInCache)));
assert(!((imFlags & ImageManager::toBeModified) &&
(imFlags & ImageManager::toBeSaved)));
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
// Lock for table read and try to find data in the cache
QReadLocker tableLocker(&m_imp->m_tableLock);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
std::map<std::string, ImageBuilderP>::iterator it =
m_imp->m_builders.find(id);
if (it == m_imp->m_builders.end()) return TImageP();
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
ImageBuilderP &builder = it->second;
bool modified = builder->m_modified;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
// Analyze imFlags
bool _putInCache =
TImageCache::instance()->isEnabled() && !(bool)(imFlags & dontPutInCache);
bool _toBeModified = (imFlags & toBeModified);
bool _toBeSaved = (imFlags & toBeSaved);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
// Update the modified flag according to the specified flags
if (_toBeModified)
builder->m_modified = true;
else if (_toBeSaved)
builder->m_modified = false;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
// Now, fetch the image.
TImageP img;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
if (builder->m_cached) {
if (modified || builder->isImageCompatible(imFlags, extData)) {
img = TImageCache::instance()->get(id, _toBeModified);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
assert(img);
if (img) return img;
}
}
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
// Lock for image building
QWriteLocker imageBuildingLocker(&builder->m_imageBuildingLock);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
// As multiple threads may block on filesLocker, re-check if the image is now
// available
if (builder->m_cached) {
if (modified || builder->isImageCompatible(imFlags, extData)) {
img = TImageCache::instance()->get(id, _toBeModified);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
assert(img);
if (img) return img;
}
}
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
// The image was either not available or not conforming to the required
// specifications.
// We have to build it now, then.
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
// Build the image
img = builder->build(imFlags, extData);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
if (img && _putInCache) {
builder->m_cached = true;
builder->m_modified = _toBeModified;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
TImageCache::instance()->add(id, img, true);
}
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
return img;
2016-03-19 06:57:51 +13:00
}
//-----------------------------------------------------------------------------
// load icon (and image) data of all frames into cache
2016-06-15 18:43:10 +12:00
void ImageManager::loadAllTlvIconsAndPutInCache(
TXshSimpleLevel *level, std::vector<TFrameId> fids,
std::vector<std::string> iconIds, bool cacheImagesAsWell) {
if (fids.empty() || iconIds.empty()) return;
// number of fid and iconId should be the same
if ((int)fids.size() != (int)iconIds.size()) return;
// obtain ImageLoader with the first fId
TImageInfo info;
std::map<std::string, ImageBuilderP>::iterator it =
m_imp->m_builders.find(level->getImageId(fids[0]));
if (it != m_imp->m_builders.end()) {
const ImageBuilderP &builder = it->second;
assert(builder);
assert(builder->getRefCount() > 0);
// this function in reimpremented only in ImageLoader
builder->buildAllIconsAndPutInCache(level, fids, iconIds,
cacheImagesAsWell);
builder->getInfo(info, ImageManager::none, 0);
}
if (cacheImagesAsWell) {
// reset the savebox
info.m_x0 = info.m_y0 = 0;
info.m_x1 = info.m_lx - 1;
info.m_y1 = info.m_ly - 1;
// put flags to all builders
for (int f = 0; f < fids.size(); f++) {
std::map<std::string, ImageBuilderP>::iterator it =
m_imp->m_builders.find(level->getImageId(fids[f]));
if (it != m_imp->m_builders.end()) {
const ImageBuilderP &builder = it->second;
builder->setImageCachedAndModified();
builder->m_info = info;
}
}
}
2016-03-19 06:57:51 +13:00
}
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
bool ImageManager::invalidate(const std::string &id) {
QWriteLocker locker(&m_imp->m_tableLock);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
std::map<std::string, ImageBuilderP>::iterator it =
m_imp->m_builders.find(id);
if (it == m_imp->m_builders.end()) return false;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
ImageBuilderP &builder = it->second;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
builder->invalidate();
builder->m_cached = builder->m_modified = false;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
TImageCache::instance()->remove(id);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
return true;
2016-03-19 06:57:51 +13:00
}
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
bool ImageManager::setImage(const std::string &id, const TImageP &img) {
if (!img) return invalidate(id);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
QWriteLocker locker(&m_imp->m_tableLock);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
std::map<std::string, ImageBuilderP>::iterator it =
m_imp->m_builders.find(id);
if (it == m_imp->m_builders.end()) return false;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
ImageBuilderP &builder = it->second;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
builder->invalidate(); // WARNING: Not all infos are correctly restored
ImageBuilder::setImageInfo(
builder->m_info,
img.getPointer()); // from supplied image - must investigate further...
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
TImageCache::instance()->add(id, img, true);
builder->m_cached = builder->m_modified = true;
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
return true;
2016-03-19 06:57:51 +13:00
}
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
ImageBuilder *ImageManager::getBuilder(const std::string &id) {
QWriteLocker locker(&m_imp->m_tableLock);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
std::map<std::string, ImageBuilderP>::iterator it =
m_imp->m_builders.find(id);
return (it == m_imp->m_builders.end()) ? (ImageBuilder *)0
: it->second.getPointer();
2016-03-19 06:57:51 +13:00
}
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
bool ImageManager::isCached(const std::string &id) {
QWriteLocker locker(&m_imp->m_tableLock);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
std::map<std::string, ImageBuilderP>::iterator it =
m_imp->m_builders.find(id);
return (it == m_imp->m_builders.end()) ? false : it->second->m_cached;
2016-03-19 06:57:51 +13:00
}
//-----------------------------------------------------------------------------
2016-06-15 18:43:10 +12:00
bool ImageManager::isModified(const std::string &id) {
QWriteLocker locker(&m_imp->m_tableLock);
2016-03-19 06:57:51 +13:00
2016-06-15 18:43:10 +12:00
std::map<std::string, ImageBuilderP>::iterator it =
m_imp->m_builders.find(id);
return (it == m_imp->m_builders.end()) ? false : it->second->m_modified;
2016-03-19 06:57:51 +13:00
}