#ifndef _DEBUG
#undef _STLP_DEBUG
#else
#define _STLP_DEBUG 1
#endif
#ifdef TNZCORE_LIGHT
#ifdef _DEBUGTOONZ
#undef _DEBUGTOONZ
#endif
#else
#ifdef _DEBUG
#define _DEBUGTOONZ _DEBUG
#endif
#endif
#include "timagecache.h"
#include "trasterimage.h"
#ifndef TNZCORE_LIGHT
#include "tvectorimage.h"
#include "trastercm.h"
#include "tropcm.h"
#endif
#include "tcodec.h"
#include "tfilepath_io.h"
#include "tconvert.h"
#include "tsystem.h"
#include "traster.h"
//#include "tstopwatch.h"
#include "tconvert.h"
#include "tbigmemorymanager.h"
#include "tstream.h"
#include "tenv.h"
#include <deque>
#include <numeric>
#include <sstream>
#ifdef WIN32
#include <crtdbg.h>
#endif
// Qt includes
#include <QThreadStorage>
//------------------------------------------------------------------------------
#undef DVAPI
#undef DVVAR
#ifdef TSYSTEM_EXPORTS
#define DVAPI DV_EXPORT_API
#define DVVAR DV_EXPORT_VAR
#else
#define DVAPI DV_IMPORT_API
#define DVVAR DV_IMPORT_VAR
#endif
class ImageBuilder;
class ImageInfo;
//std::ofstream os("C:\\cache.txt");
TUINT32 HistoryCount = 0;
//------------------------------------------------------------------------------
class TheCodec : public TRasterCodecLz4
{
public:
static TheCodec *instance()
{
if (!_instance)
_instance = new TheCodec();
return _instance;
}
void reset()
{
if (_instance)
_instance->TRasterCodecLz4::reset();
}
private:
static TheCodec *_instance;
TheCodec() : TRasterCodecLz4("Lz4_Codec", false) {}
};
TheCodec *TheCodec::_instance = 0;
//------------------------------------------------------------------------------
class CacheItem : public TSmartObject
{
DECLARE_CLASS_CODE
public:
CacheItem()
: m_cantCompress(false), m_builder(0), m_imageInfo(0), m_modified(false) {}
CacheItem(ImageBuilder *builder, ImageInfo *imageInfo)
: m_cantCompress(false), m_builder(builder), m_imageInfo(imageInfo), m_historyCount(0), m_modified(false)
{
}
virtual ~CacheItem() {}
virtual TUINT32 getSize() const = 0;
// getImage restituisce un'immagine non compressa
virtual TImageP getImage() const = 0;
bool m_cantCompress;
ImageBuilder *m_builder;
ImageInfo *m_imageInfo;
string m_id;
TUINT32 m_historyCount;
bool m_modified;
};
#ifdef WIN32
template class DVAPI TSmartPointerT<CacheItem>;
#endif
typedef TSmartPointerT<CacheItem> CacheItemP;
DEFINE_CLASS_CODE(CacheItem, 101)
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
class ImageInfo
{
public:
TDimension m_size;
ImageInfo(const TDimension &size) : m_size(size) {}
virtual ~ImageInfo() {}
virtual ImageInfo *clone() = 0;
};
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
class ImageBuilder
{
public:
virtual ~ImageBuilder() {}
virtual ImageBuilder *clone() = 0;
virtual TImageP build(ImageInfo *info, const TRasterP &ras) = 0;
};
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
class RasterImageInfo : public ImageInfo
{
public:
RasterImageInfo(const TRasterImageP &ri);
void setInfo(const TRasterImageP &ri);
ImageInfo *clone();
double m_dpix, m_dpiy;
string m_name;
TRect m_savebox;
bool m_isOpaque;
TPoint m_offset;
int m_subs;
};
RasterImageInfo::RasterImageInfo(const TRasterImageP &ri)
: ImageInfo(ri->getRaster()->getSize())
{
ri->getDpi(m_dpix, m_dpiy);
m_name = ri->getName();
m_savebox = ri->getSavebox();
m_isOpaque = ri->isOpaque();
m_offset = ri->getOffset();
m_subs = ri->getSubsampling();
}
void RasterImageInfo::setInfo(const TRasterImageP &ri)
{
ri->setDpi(m_dpix, m_dpiy);
ri->setName(m_name);
ri->setSavebox(m_savebox);
ri->setOpaqueFlag(m_isOpaque);
ri->setOffset(m_offset);
ri->setSubsampling(m_subs);
}
ImageInfo *RasterImageInfo::clone()
{
return new RasterImageInfo(*this);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
#ifndef TNZCORE_LIGHT
#include "tpalette.h"
#include "ttoonzimage.h"
class ToonzImageInfo : public ImageInfo
{
public:
ToonzImageInfo(const TToonzImageP &ti);
~ToonzImageInfo()
{
if (m_palette)
m_palette->release();
}
ImageInfo *clone()
{
ToonzImageInfo *ret = new ToonzImageInfo(*this);
if (ret->m_palette)
ret->m_palette->addRef();
return ret;
}
void setInfo(const TToonzImageP &ti);
double m_dpix, m_dpiy;
string m_name;
TRect m_savebox;
TPoint m_offset;
int m_subs;
TPalette *m_palette;
};
ToonzImageInfo::ToonzImageInfo(const TToonzImageP &ti) : ImageInfo(ti->getSize())
{
m_palette = ti->getPalette();
if (m_palette)
m_palette->addRef();
ti->getDpi(m_dpix, m_dpiy);
m_savebox = ti->getSavebox();
m_offset = ti->getOffset();
m_subs = ti->getSubsampling();
}
void ToonzImageInfo::setInfo(const TToonzImageP &ti)
{
ti->setPalette(m_palette);
ti->setDpi(m_dpix, m_dpiy);
ti->setOffset(m_offset);
ti->setSubsampling(m_subs);
}
#endif
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
class RasterImageBuilder : public ImageBuilder
{
public:
ImageBuilder *clone()
{
return new RasterImageBuilder(*this);
}
TImageP build(ImageInfo *info, const TRasterP &ras);
};
TImageP RasterImageBuilder::build(ImageInfo *info, const TRasterP &ras)
{
RasterImageInfo *riInfo = dynamic_cast<RasterImageInfo *>(info);
assert(riInfo);
int rcount = ras->getRefCount();
TRasterImageP ri = new TRasterImage();
#ifdef _DEBUGTOONZ
ras->m_cashed = true;
#endif
ri->setRaster(ras);
riInfo->setInfo(ri);
assert(ras->getRefCount() > rcount);
return ri;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
#ifndef TNZCORE_LIGHT
class ToonzImageBuilder : public ImageBuilder
{
public:
ImageBuilder *clone()
{
return new ToonzImageBuilder(*this);
}
TImageP build(ImageInfo *info, const TRasterP &ras);
};
TImageP ToonzImageBuilder::build(ImageInfo *info, const TRasterP &ras)
{
ToonzImageInfo *tiInfo = dynamic_cast<ToonzImageInfo *>(info);
assert(tiInfo);
TRasterCM32P rasCM32 = ras;
assert(rasCM32);
TRasterCM32P imgRasCM32;
assert(TRect(tiInfo->m_size).contains(tiInfo->m_savebox));
if (ras->getSize() != tiInfo->m_size) {
TRasterCM32P fullRas(tiInfo->m_size);
TRect rectToExtract(tiInfo->m_savebox);
TPixelCM32 bgColor;
fullRas->fillOutside(tiInfo->m_savebox, bgColor);
fullRas->extractT(rectToExtract)->copy(ras);
assert(rectToExtract == tiInfo->m_savebox);
imgRasCM32 = fullRas;
} else
imgRasCM32 = rasCM32;
#ifdef _DEBUG
imgRasCM32->m_cashed = true;
#endif
TToonzImageP ti = new TToonzImage(imgRasCM32, tiInfo->m_savebox);
tiInfo->setInfo(ti);
return ti;
}
#endif
//------------------------------------------------------------------------------
class UncompressedOnMemoryCacheItem : public CacheItem
{
public:
UncompressedOnMemoryCacheItem(const TImageP &image) : m_image(image)
{
TRasterImageP ri = m_image;
if (ri)
m_imageInfo = new RasterImageInfo(ri);
#ifndef TNZCORE_LIGHT
else {
TToonzImageP ti = m_image;
if (ti)
m_imageInfo = new ToonzImageInfo(ti);
else
m_imageInfo = 0;
}
#else
else
m_imageInfo = 0;
#endif
}
~UncompressedOnMemoryCacheItem()
{
if (m_imageInfo)
delete m_imageInfo;
}
TUINT32 getSize() const;
TImageP getImage() const { return m_image; }
TImageP m_image;
};
#ifdef WIN32
template class DVAPI TSmartPointerT<UncompressedOnMemoryCacheItem>;
template class DVAPI TDerivedSmartPointerT<UncompressedOnMemoryCacheItem, CacheItem>;
#endif
typedef TDerivedSmartPointerT<UncompressedOnMemoryCacheItem, CacheItem> UncompressedOnMemoryCacheItemP;
//------------------------------------------------------------------------------
TUINT32 UncompressedOnMemoryCacheItem::getSize() const
{
TRasterImageP ri = m_image;
if (ri) {
TRasterP ras = ri->getRaster();
if (ras)
return ras->getLy() * ras->getRowSize();
else
return 0;
} else {
#ifndef TNZCORE_LIGHT
TToonzImageP ti = m_image;
if (ti) {
TDimension size = ti->getSize();
return size.lx * size.ly * sizeof(TPixelCM32);
}
#endif
}
return 0;
}
//------------------------------------------------------------------------------
class CompressedOnMemoryCacheItem : public CacheItem
{
public:
CompressedOnMemoryCacheItem(const TImageP &img);
CompressedOnMemoryCacheItem(const TRasterP &compressedRas,
ImageBuilder *builder,
ImageInfo *info);
~CompressedOnMemoryCacheItem();
TUINT32 getSize() const;
TImageP getImage() const;
TRasterP m_compressedRas;
};
#ifdef WIN32
template class DVAPI TSmartPointerT<CompressedOnMemoryCacheItem>;
template class DVAPI TDerivedSmartPointerT<CompressedOnMemoryCacheItem, CacheItem>;
#endif
typedef TDerivedSmartPointerT<CompressedOnMemoryCacheItem, CacheItem> CompressedOnMemoryCacheItemP;
//------------------------------------------------------------------------------
CompressedOnMemoryCacheItem::CompressedOnMemoryCacheItem(const TImageP &img)
: m_compressedRas()
{
TRasterImageP ri = img;
if (ri) {
m_imageInfo = new RasterImageInfo(ri);
m_builder = new RasterImageBuilder();
TINT32 buffSize = 0;
m_compressedRas = TheCodec::instance()->compress(ri->getRaster(), 1, buffSize);
}
#ifndef TNZCORE_LIGHT
else {
TToonzImageP ti = img;
if (ti) {
m_imageInfo = new ToonzImageInfo(ti);
m_builder = new ToonzImageBuilder();
TRasterCM32P rasCM32 = ti->getRaster();
TINT32 buffSize = 0;
m_compressedRas = TheCodec::instance()->compress(rasCM32, 1, buffSize);
} else
assert(false);
}
#else
else
assert(false);
#endif
}
//------------------------------------------------------------------------------
CompressedOnMemoryCacheItem::CompressedOnMemoryCacheItem(const TRasterP &ras,
ImageBuilder *builder,
ImageInfo *info)
: CacheItem(builder, info), m_compressedRas(ras)
{
}
//------------------------------------------------------------------------------
CompressedOnMemoryCacheItem::~CompressedOnMemoryCacheItem()
{
delete m_imageInfo;
}
//------------------------------------------------------------------------------
TUINT32 CompressedOnMemoryCacheItem::getSize() const
{
if (m_compressedRas)
return m_compressedRas->getLx();
else
return 0;
}
//------------------------------------------------------------------------------
TImageP CompressedOnMemoryCacheItem::getImage() const
{
assert(m_compressedRas);
// PER IL MOMENTO DISCRIMINO: DA ELIMINARE
TRasterP ras;
TheCodec::instance()->decompress(m_compressedRas, ras);
#ifdef _DEBUGTOONZ
ras->m_cashed = true;
#endif
#ifndef TNZCORE_LIGHT
ToonzImageBuilder *tibuilder = dynamic_cast<ToonzImageBuilder *>(m_builder);
if (tibuilder)
return tibuilder->build(m_imageInfo, ras);
else
#endif
return m_builder->build(m_imageInfo, ras);
}
//------------------------------------------------------------------------------
class CompressedOnDiskCacheItem : public CacheItem
{
public:
CompressedOnDiskCacheItem(const TFilePath &fp,
const TRasterP &compressedRas,
ImageBuilder *builder,
ImageInfo *info);
~CompressedOnDiskCacheItem();
TUINT32 getSize() const { return 0; }
TImageP getImage() const;
TFilePath m_fp;
};
#ifdef WIN32
template class DVAPI TSmartPointerT<CompressedOnDiskCacheItem>;
template class DVAPI TDerivedSmartPointerT<CompressedOnDiskCacheItem, CacheItem>;
#endif
typedef TDerivedSmartPointerT<CompressedOnDiskCacheItem, CacheItem> CompressedOnDiskCacheItemP;
//------------------------------------------------------------------------------
CompressedOnDiskCacheItem::CompressedOnDiskCacheItem(const TFilePath &fp,
const TRasterP &compressedRas,
ImageBuilder *builder,
ImageInfo *info)
: CacheItem(builder, info), m_fp(fp)
{
compressedRas->lock();
Tofstream oss(m_fp);
assert(compressedRas->getLy() == 1 && compressedRas->getPixelSize() == 1);
TUINT32 size = compressedRas->getLx();
oss.write((char *)&size, sizeof(TUINT32));
oss.write((char *)compressedRas->getRawData(), size);
assert(!oss.fail());
compressedRas->unlock();
}
//------------------------------------------------------------------------------
CompressedOnDiskCacheItem::~CompressedOnDiskCacheItem()
{
delete m_imageInfo;
TSystem::deleteFile(m_fp);
}
//------------------------------------------------------------------------------
TImageP CompressedOnDiskCacheItem::getImage() const
{
Tifstream is(m_fp);
TUINT32 dataSize;
is.read((char *)&dataSize, sizeof(TUINT32));
TRasterGR8P ras(dataSize, 1);
ras->lock();
UCHAR *data = ras->getRawData();
is.read((char *)data, dataSize);
assert(!is.fail());
ras->unlock();
CompressedOnMemoryCacheItem item(ras, m_builder->clone(), m_imageInfo->clone());
return item.getImage();
}
//------------------------------------------------------------------------------
class UncompressedOnDiskCacheItem : public CacheItem
{
int m_pixelsize;
public:
UncompressedOnDiskCacheItem(const TFilePath &fp,
const TImageP &img);
~UncompressedOnDiskCacheItem();
TUINT32 getSize() const { return 0; }
TImageP getImage() const;
//TRaster32P getRaster32() const;
TFilePath m_fp;
};
#ifdef WIN32
template class DVAPI TSmartPointerT<UncompressedOnDiskCacheItem>;
template class DVAPI TDerivedSmartPointerT<UncompressedOnDiskCacheItem, CacheItem>;
#endif
typedef TDerivedSmartPointerT<UncompressedOnDiskCacheItem, CacheItem> UncompressedOnDiskCacheItemP;
//------------------------------------------------------------------------------
UncompressedOnDiskCacheItem::UncompressedOnDiskCacheItem(const TFilePath &fp,
const TImageP &image)
: CacheItem(0, 0), m_fp(fp)
{
TRasterImageP ri = image;
TRasterP ras;
if (ri) {
m_imageInfo = new RasterImageInfo(ri);
ras = ri->getRaster();
}
#ifndef TNZCORE_LIGHT
else {
TToonzImageP ti = image;
if (ti) {
m_imageInfo = new ToonzImageInfo(ti);
ras = ti->getRaster();
} else
assert(false);
}
#else
else
assert(false);
#endif
m_builder = 0;
int dataSize = ras->getLx() * ras->getLy() * ras->getPixelSize();
int lx = ras->getLx();
int ly = ras->getLy();
int wrap = ras->getWrap();
m_pixelsize = ras->getPixelSize();
Tofstream oss(m_fp);
//oss.write((char*)&dataSize, sizeof(TUINT32));
//assert(!oss.fail());
ras->lock();
if (lx == wrap) {
oss.write((char *)ras->getRawData(), dataSize);
assert(!oss.fail());
} else {
char *buf = (char *)ras->getRawData();
for (int i = 0; i < ly; i++, buf += wrap) {
oss.write(buf, lx * m_pixelsize);
assert(!oss.fail());
}
}
ras->unlock();
}
//------------------------------------------------------------------------------
UncompressedOnDiskCacheItem::~UncompressedOnDiskCacheItem()
{
delete m_imageInfo;
TSystem::deleteFile(m_fp);
}
//------------------------------------------------------------------------------
TImageP UncompressedOnDiskCacheItem::getImage() const
{
Tifstream is(m_fp);
TUINT32 dataSize = m_imageInfo->m_size.lx * m_imageInfo->m_size.ly * m_pixelsize;
//is.read((char*)&dataSize, sizeof(TUINT32));
//assert(unsigned(m_lx*m_ly*m_pixelsize)==dataSize);
TRasterP ras;
RasterImageInfo *rii = dynamic_cast<RasterImageInfo *>(m_imageInfo);
if (rii) {
if (m_pixelsize == 4)
ras = (TRasterP)(TRaster32P(rii->m_size));
else if (m_pixelsize == 8)
ras = (TRasterP)(TRaster64P(rii->m_size));
else if (m_pixelsize == 1)
ras = (TRasterP)(TRasterGR8P(rii->m_size));
else if (m_pixelsize == 2)
ras = (TRasterP)(TRasterGR16P(rii->m_size));
else
assert(false);
ras->lock();
char *data = (char *)ras->getRawData();
is.read(data, dataSize);
ras->unlock();
#ifdef _DEBUGTOONZ
ras->m_cashed = true;
#endif
return RasterImageBuilder().build(m_imageInfo, ras);
}
#ifndef TNZCORE_LIGHT
else {
ToonzImageInfo *tii = dynamic_cast<ToonzImageInfo *>(m_imageInfo);
if (tii) {
ras = (TRasterP)(TRasterCM32P(tii->m_size));
ras->lock();
char *data = (char *)ras->getRawData();
is.read(data, dataSize);
ras->unlock();
#ifdef _DEBUG
ras->m_cashed = true;
#endif
return ToonzImageBuilder().build(m_imageInfo, ras);
} else {
assert(false);
return 0;
}
}
#else
else {
assert(false);
return 0;
}
#endif
}
//------------------------------------------------------------------------------
string TImageCache::getUniqueId(void)
{
static TAtomicVar count;
std::stringstream ss;
ss << ++count;
return "IMAGECACHEUNIQUEID" + ss.str();
}
class TImageCache::Imp
{
public:
Imp() : m_rootDir()
{
//ATTENZIONE: e' molto piu' veloce se si usa memoria fisica
//invece che virtuale: la virtuale e' tanta, non c'e' quindi bisogno
//di comprimere le immagini, che grandi come sono vengono swappate su disco
if (TBigMemoryManager::instance()->isActive())
return;
m_reservedMemory = (TINT64)(TSystem::getMemorySize(true) * 0.10);
if (m_reservedMemory < 64 * 1024)
m_reservedMemory = 64 * 1024;
}
~Imp()
{
if (m_rootDir != TFilePath())
TSystem::rmDirTree(m_rootDir);
}
bool inline notEnoughMemory()
{
if (TBigMemoryManager::instance()->isActive())
return TBigMemoryManager::instance()->getAvailableMemoryinKb() < 50 * 1024;
else
return TSystem::memoryShortage();
}
void doCompress();
void doCompress(string id);
UCHAR *compressAndMalloc(TUINT32 requestedSize); // compress in the cache till it can nallocate the requested memory
void outputMap(UINT chunkRequested, string filename);
void remove(const string &id);
void remap(const string &dstId, const string &srcId);
TImageP get(const string &id, bool toBeModified);
void add(const string &id, const TImageP &img, bool overwrite);
TFilePath m_rootDir;
#ifndef TNZCORE_LIGHT
QThreadStorage<bool *> m_isEnabled;
#else
bool m_isEnabled;
#endif
map<string, CacheItemP> m_uncompressedItems;
map<TUINT32, string> m_itemHistory;
map<string, CacheItemP> m_compressedItems;
map<void *, string> m_itemsByImagePointer; //items ordered by ImageP.getPointer()
map<string, string> m_duplicatedItems; //for duplicated items (when id1!=id2 but image1==image2) in the map: key is dup id, value is main id
//memoria fisica totale della macchina che non puo' essere utilizzata;
TINT64 m_reservedMemory;
TThread::Mutex m_mutex;
static int m_fileid;
};
int TImageCache::Imp::m_fileid;
//------------------------------------------------------------------------------
namespace
{
inline void *getPointer(const TImageP &img)
{
TRasterImageP rimg = img;
if (rimg)
return rimg->getRaster().getPointer();
#ifndef TNZCORE_LIGHT
TToonzImageP timg = img;
if (timg)
return timg->getRaster().getPointer();
#endif
return img.getPointer();
}
// Returns true or false whether the image or its eventual raster are
// referenced by someone other than Toonz cache.
inline TINT32 hasExternalReferences(const TImageP &img)
{
int refCount;
{
TRasterImageP rimg = img;
if (rimg)
refCount = rimg->getRaster()->getRefCount();
}
#ifndef TNZCORE_LIGHT
{
TToonzImageP timg = img;
if (timg)
refCount = timg->getRaster()->getRefCount() - 1; //!!! the TToonzImage::getRaster method increments raster refCount!(the TRasterImage::getRaster don't)
}
#endif
return tmax(refCount, img->getRefCount()) > 1;
}
}
//------------------------------------------------------------------------------
void TImageCache::Imp::doCompress()
{
// se la memoria usata per mantenere le immagini decompresse e' superiore
// a un dato valore, comprimo alcune immagini non compresse non checked-out
// in modo da liberare memoria
// per il momento scorre tutte le immagini alla ricerca di immagini
// non compresse non checked-out
TThread::MutexLocker sl(&m_mutex);
std::map<TUINT32, string>::iterator itu = m_itemHistory.begin();
for (; itu != m_itemHistory.end() && notEnoughMemory();) {
std::map<string, CacheItemP>::iterator it = m_uncompressedItems.find(itu->second);
assert(it != m_uncompressedItems.end());
CacheItemP item = it->second;
UncompressedOnMemoryCacheItemP uitem = item;
if (item->m_cantCompress ||
(uitem && (!uitem->m_image || hasExternalReferences(uitem->m_image)))) {
++itu;
continue;
}
string id = it->first;
#ifdef WIN32
assert(itu->first == it->second->m_historyCount);
itu = m_itemHistory.erase(itu);
m_itemsByImagePointer.erase(getPointer(item->getImage()));
m_uncompressedItems.erase(it);
#else
std::map<TUINT32, string>::iterator itu2 = itu;
itu++;
m_itemHistory.erase(itu2);
m_itemsByImagePointer.erase(item->getImage().getPointer());
m_uncompressedItems.erase(it);
#endif
if (m_compressedItems.find(id) == m_compressedItems.end()) {
assert(uitem);
item->m_cantCompress = true;
CacheItemP newItem = new CompressedOnMemoryCacheItem(item->getImage()); //WARNING the codec buffer allocation can CHANGE the cache.
item->m_cantCompress = false;
if (newItem->getSize() == 0) ///non c'era memoria sufficiente per il buffer compresso....
{
assert(m_rootDir != TFilePath());
TFilePath fp = m_rootDir + TFilePath(toString(TImageCache::Imp::m_fileid++));
newItem = new UncompressedOnDiskCacheItem(fp, item->getImage());
}
m_compressedItems[id] = newItem;
item = CacheItemP();
uitem = UncompressedOnMemoryCacheItemP();
//doCompress();//restart, since interators can have been changed (see comment above)
//return;
itu = m_itemHistory.begin();
}
}
// se il quantitativo di memoria utilizzata e' superiore a un dato valore, sposto
// su disco alcune immagini compresse in modo da liberare memoria
if (itu != m_itemHistory.end()) //memory is enough!
return;
std::map<string, CacheItemP>::iterator itc = m_compressedItems.begin();
for (; itc != m_compressedItems.end() && notEnoughMemory(); ++itc) {
CacheItemP item = itc->second;
if (item->m_cantCompress)
continue;
CompressedOnMemoryCacheItemP citem = itc->second;
if (citem) {
assert(m_rootDir != TFilePath());
TFilePath fp = m_rootDir + TFilePath(toString(TImageCache::Imp::m_fileid++));
CacheItemP newItem = new CompressedOnDiskCacheItem(fp, citem->m_compressedRas,
citem->m_builder->clone(), citem->m_imageInfo->clone());
itc->second = 0;
m_compressedItems[itc->first] = newItem;
}
}
}
//------------------------------------------------------------------------------
void TImageCache::Imp::doCompress(string id)
{
TThread::MutexLocker sl(&m_mutex);
// search id in m_uncompressedItems
std::map<string, CacheItemP>::iterator it = m_uncompressedItems.find(id);
if (it == m_uncompressedItems.end())
return; // id not found: return
// is item suitable for compression ?
CacheItemP item = it->second;
UncompressedOnMemoryCacheItemP uitem = item;
if (item->m_cantCompress ||
(uitem && (!uitem->m_image || hasExternalReferences(uitem->m_image))))
return;
// search id in m_itemHistory
std::map<TUINT32, string>::iterator itu = m_itemHistory.begin();
while (itu != m_itemHistory.end() && itu->second != id)
++itu;
if (itu == m_itemHistory.end())
return; // id not found: return
// delete itu from m_itemHistory
#ifdef WIN32
assert(itu->first == it->second->m_historyCount);
itu = m_itemHistory.erase(itu);
m_itemsByImagePointer.erase(getPointer(item->getImage()));
#else
std::map<TUINT32, string>::iterator itu2 = itu;
itu++;
m_itemHistory.erase(itu2);
m_itemsByImagePointer.erase(item->getImage().getPointer());
#endif
// delete item from m_uncompressedItems
m_uncompressedItems.erase(it);
// check if item has been already compressed. this should never happen
if (m_compressedItems.find(id) != m_compressedItems.end())
return;
assert(uitem);
item->m_cantCompress = true; // ??
CacheItemP newItem = new CompressedOnMemoryCacheItem(item->getImage()); //WARNING the codec buffer allocation can CHANGE the cache.
item->m_cantCompress = false; // ??
if (newItem->getSize() == 0) ///non c'era memoria sufficiente per il buffer compresso....
{
assert(m_rootDir != TFilePath());
TFilePath fp = m_rootDir + TFilePath(toString(TImageCache::Imp::m_fileid++));
newItem = new UncompressedOnDiskCacheItem(fp, item->getImage());
}
m_compressedItems[id] = newItem;
item = CacheItemP();
uitem = UncompressedOnMemoryCacheItemP();
}
/*
// se il quantitativo di memoria utilizzata e' superiore a un dato valore, sposto
// su disco alcune immagini compresse in modo da liberare memoria
if (itu != m_itemHistory.end()) //memory is enough!
return;
std::map<string, CacheItemP>::iterator itc = m_compressedItems.begin();
for ( ; itc != m_compressedItems.end() && notEnoughMemory(); ++itc)
{
CacheItemP item = itc->second;
if (item->m_cantCompress)
continue;
CompressedOnMemoryCacheItemP citem = itc->second;
if (citem)
{
assert(m_rootDir!=TFilePath());
TFilePath fp = m_rootDir + TFilePath(toString(TImageCache::Imp::m_fileid++));
CacheItemP newItem = new CompressedOnDiskCacheItem(fp, citem->m_compressedRas,
citem->m_builder->clone(), citem->m_imageInfo->clone());
itc->second = 0;
m_compressedItems[itc->first] = newItem;
}
}
*/
//------------------------------------------------------------------------------
UCHAR *TImageCache::Imp::compressAndMalloc(TUINT32 size)
{
UCHAR *buf = 0;
TThread::MutexLocker sl(&m_mutex);
TheCodec::instance()->reset();
//if (size!=0)
// size = size>>10;
//assert(size==0 || TBigMemoryManager::instance()->isActive());
std::map<TUINT32, string>::iterator itu = m_itemHistory.begin();
while ((buf = TBigMemoryManager::instance()->getBuffer(size)) == 0 &&
itu != m_itemHistory.end()) //>TBigMemoryManager::instance()->getAvailableMemoryinKb()))
{
std::map<string, CacheItemP>::iterator it = m_uncompressedItems.find(itu->second);
assert(it != m_uncompressedItems.end());
CacheItemP item = it->second;
UncompressedOnMemoryCacheItemP uitem = item;
if (item->m_cantCompress || (uitem && (!uitem->m_image || hasExternalReferences(uitem->m_image)))) {
++itu;
continue;
}
if (m_compressedItems.find(it->first) == m_compressedItems.end()) {
assert(uitem);
CacheItemP newItem;
//newItem = new CompressedOnMemoryCacheItem(item->getImage());
//if (newItem->getSize()==0)
// {
assert(m_rootDir != TFilePath());
TFilePath fp = m_rootDir + TFilePath(toString(TImageCache::Imp::m_fileid++));
newItem = new UncompressedOnDiskCacheItem(fp, item->getImage());
// }
m_compressedItems[it->first] = newItem;
}
#ifdef WIN32
assert(itu->first == it->second->m_historyCount);
itu = m_itemHistory.erase(itu);
m_itemsByImagePointer.erase(getPointer(item->getImage()));
m_uncompressedItems.erase(it);
#else
std::map<TUINT32, string>::iterator itu2 = itu;
itu++;
m_itemHistory.erase(itu2);
m_itemsByImagePointer.erase(item->getImage().getPointer());
m_uncompressedItems.erase(it);
#endif
}
if (buf != 0)
return buf;
std::map<string, CacheItemP>::iterator itc = m_compressedItems.begin();
for (; itc != m_compressedItems.end() &&
(buf = TBigMemoryManager::instance()->getBuffer(size)) == 0;
++itc) {
CacheItemP item = itc->second;
if (item->m_cantCompress)
continue;
CompressedOnMemoryCacheItemP citem = itc->second;
if (citem) {
assert(m_rootDir != TFilePath());
TFilePath fp = m_rootDir + TFilePath(toString(TImageCache::Imp::m_fileid++));
CacheItemP newItem = new CompressedOnDiskCacheItem(
fp, citem->m_compressedRas,
citem->m_builder->clone(), citem->m_imageInfo->clone());
itc->second = 0;
m_compressedItems[itc->first] = newItem;
}
}
return buf;
}
//------------------------------------------------------------------------------
namespace
{
int check = 0;
const int magic = 123456;
}
static TImageCache *CacheInstance = 0;
TImageCache *TImageCache::instance()
{
if (CacheInstance == 0)
CacheInstance = new TImageCache();
return CacheInstance;
/*
if (!ImageCache::m_instance)
{
ImageCache::m_instance = new ImageCache;
ImageCache::m_destroyer.m_imageCache = ImageCache::m_instance;
}
return ImageCache::m_instance;
*/
}
//------------------------------------------------------------------------------
TImageCache::TImageCache()
: m_imp(new Imp())
{
assert(check == 0);
check = magic;
}
//------------------------------------------------------------------------------
TImageCache::~TImageCache()
{
assert(check == magic);
check = -1;
delete m_imp;
CacheInstance = 0;
}
//------------------------------------------------------------------------------
void TImageCache::setEnabled(bool isEnabled)
{
#ifndef TNZCORE_LIGHT
QThreadStorage<bool *> &storage = m_imp->m_isEnabled;
if (storage.hasLocalData() && *(storage.localData()) == isEnabled)
return;
if (!storage.hasLocalData())
storage.setLocalData(new bool(isEnabled));
else
*(storage.localData()) = isEnabled;
#else
m_imp->m_isEnabled = isEnabled;
#endif
}
//------------------------------------------------------------------------------
bool TImageCache::isEnabled()
{
#ifndef TNZCORE_LIGHT
QThreadStorage<bool *> &storage = m_imp->m_isEnabled;
if (!storage.hasLocalData())
return true;
return *(storage.localData());
#else
return m_isEnabled;
#endif
}
//------------------------------------------------------------------------------
void TImageCache::setRootDir(const TFilePath &cacheDir)
{
if (m_imp->m_rootDir != TFilePath())
return;
m_imp->m_rootDir = cacheDir + TFilePath(toString(TSystem::getProcessId()));
#ifndef TNZCORE_LIGHT
TFileStatus fs1(m_imp->m_rootDir);
if (!fs1.doesExist())
TSystem::mkDir(m_imp->m_rootDir);
#endif
}
//------------------------------------------------------------------------------
/*
TFilePath TImageCache::getRootDir() const
{
return m_imp->m_rootDir;
}
*/
//------------------------------------------------------------------------------
UCHAR *TImageCache::compressAndMalloc(TUINT32 requestedSize)
{
return m_imp->compressAndMalloc(requestedSize);
}
//------------------------------------------------------------------------------
void TImageCache::add(const string &id, const TImageP &img, bool overwrite)
{
if (!isEnabled())
return;
m_imp->add(id, img, overwrite);
}
//------------------------------------------------------------------------------
void TImageCache::Imp::add(const string &id, const TImageP &img, bool overwrite)
{
TThread::MutexLocker sl(&m_mutex);
#ifdef LEVO
std::map<string, CacheItemP>::iterator it1 = m_uncompressedItems.begin();
for (; it1 != m_uncompressedItems.end(); ++it1) {
UncompressedOnMemoryCacheItemP item = (UncompressedOnMemoryCacheItemP)it1->second;
//m_memUsage -= item->getSize();
assert(item);
TImageP refImg = item->getImage();
if (refImg.getPointer() == img.getPointer() && it1->first != id)
assert(!"opps gia' esiste in cache!");
}
#endif
std::map<string, CacheItemP>::iterator itUncompr = m_uncompressedItems.find(id);
std::map<string, CacheItemP>::iterator itCompr = m_compressedItems.find(id);
#ifdef _DEBUGTOONZ
TRasterImageP rimg = (TRasterImageP)img;
TToonzImageP timg = (TToonzImageP)img;
#endif
if (itUncompr != m_uncompressedItems.end() ||
itCompr != m_compressedItems.end()) //already present in cache with same id...
{
if (overwrite) {
#ifdef _DEBUGTOONZ
if (rimg)
rimg->getRaster()->m_cashed = true;
else if (timg)
timg->getRaster()->m_cashed = true;
#endif
std::map<string, CacheItemP>::iterator it;
if (itUncompr != m_uncompressedItems.end()) {
assert(m_itemHistory.find(itUncompr->second->m_historyCount) != m_itemHistory.end());
m_itemHistory.erase(itUncompr->second->m_historyCount);
m_itemsByImagePointer.erase(getPointer(itUncompr->second->getImage()));
m_uncompressedItems.erase(itUncompr);
}
if (itCompr != m_compressedItems.end())
m_compressedItems.erase(id);
} else
return;
} else {
std::map<string, string>::iterator dt = m_duplicatedItems.find(id);
if ((dt != m_duplicatedItems.end()) && !overwrite)
return;
std::map<void *, string>::iterator it;
if ((it = m_itemsByImagePointer.find(getPointer(img))) != m_itemsByImagePointer.end()) //already present in cache with another id...
{
m_duplicatedItems[id] = it->second;
return;
}
if (dt != m_duplicatedItems.end())
m_duplicatedItems.erase(dt);
}
CacheItemP item;
#ifdef _DEBUGTOONZ
if (rimg)
rimg->getRaster()->m_cashed = true;
else if (timg)
timg->getRaster()->m_cashed = true;
#endif
item = new UncompressedOnMemoryCacheItem(img);
#ifdef TNZCORE_LIGHT
item->m_cantCompress = false;
#else
item->m_cantCompress = (TVectorImageP(img) ? true : false);
#endif
item->m_id = id;
m_uncompressedItems[id] = item;
m_itemsByImagePointer[getPointer(img)] = id;
item->m_historyCount = HistoryCount;
m_itemHistory[HistoryCount] = id;
HistoryCount++;
doCompress();
#ifdef _DEBUGTOONZ
//int itemCount = m_imp->m_uncompressedItems.size()+m_imp->m_compressedItems.size();
//m_imp->outputDebug();
#endif
}
void TImageCache::remove(const string &id)
{
m_imp->remove(id);
}
//------------------------------------------------------------------------------
void TImageCache::Imp::remove(const string &id)
{
if (CacheInstance == 0)
return; //the remove can be called when exiting from toonz...after the imagecache was already freed!
assert(check == magic);
TThread::MutexLocker sl(&m_mutex);
std::map<string, string>::iterator it1;
if ((it1 = m_duplicatedItems.find(id)) != m_duplicatedItems.end()) //it's a duplicated id...
{
m_duplicatedItems.erase(it1);
return;
}
for (it1 = m_duplicatedItems.begin(); it1 != m_duplicatedItems.end(); ++it1)
if (it1->second == id)
break;
if (it1 != m_duplicatedItems.end()) //it has duplicated, so cannot erase it; I erase the duplicate, and assign its id has the main id
{
string sonId = it1->first;
m_duplicatedItems.erase(it1);
remap(sonId, id);
return;
}
std::map<string, CacheItemP>::iterator it = m_uncompressedItems.find(id);
std::map<string, CacheItemP>::iterator itc = m_compressedItems.find(id);
if (it != m_uncompressedItems.end()) {
const CacheItemP &item = it->second;
assert((UncompressedOnMemoryCacheItemP)item);
assert(m_itemHistory.find(it->second->m_historyCount) != m_itemHistory.end());
m_itemHistory.erase(it->second->m_historyCount);
m_itemsByImagePointer.erase(getPointer(it->second->getImage()));
#ifdef _DEBUGTOONZ
if ((TRasterImageP)it->second->getImage())
((TRasterImageP)it->second->getImage())->getRaster()->m_cashed = false;
else if ((TToonzImageP)it->second->getImage())
((TToonzImageP)it->second->getImage())->getRaster()->m_cashed = false;
#endif
m_uncompressedItems.erase(it);
}
if (itc != m_compressedItems.end())
m_compressedItems.erase(itc);
}
//------------------------------------------------------------------------------
void TImageCache::remap(const string &dstId, const string &srcId)
{
m_imp->remap(dstId, srcId);
}
void TImageCache::Imp::remap(const string &dstId, const string &srcId)
{
TThread::MutexLocker sl(&m_mutex);
std::map<string, CacheItemP>::iterator it = m_uncompressedItems.find(srcId);
if (it != m_uncompressedItems.end()) {
CacheItemP citem = it->second;
assert(m_itemHistory.find(citem->m_historyCount) != m_itemHistory.end());
m_itemHistory.erase(citem->m_historyCount);
m_itemsByImagePointer.erase(getPointer(citem->getImage()));
m_uncompressedItems.erase(it);
m_uncompressedItems[dstId] = citem;
m_itemHistory[citem->m_historyCount] = dstId;
m_itemsByImagePointer[getPointer(citem->getImage())] = dstId;
}
it = m_compressedItems.find(srcId);
if (it != m_compressedItems.end()) {
CacheItemP citem = it->second;
m_compressedItems.erase(it);
m_compressedItems[dstId] = citem;
}
std::map<string, string>::iterator it2 = m_duplicatedItems.find(srcId);
if (it2 != m_duplicatedItems.end()) {
string id = it2->second;
m_duplicatedItems.erase(it2);
m_duplicatedItems[dstId] = id;
}
for (it2 = m_duplicatedItems.begin(); it2 != m_duplicatedItems.end(); ++it2)
if (it2->second == srcId)
it2->second = dstId;
}
//------------------------------------------------------------------------------
void TImageCache::remapIcons(const string &dstId, const string &srcId)
{
std::map<string, CacheItemP>::iterator it;
std::map<string, string> table;
string prefix = srcId + ":";
int j = (int)prefix.length();
for (it = m_imp->m_uncompressedItems.begin(); it != m_imp->m_uncompressedItems.end(); ++it) {
string id = it->first;
if (id.find(prefix) == 0)
table[id] = dstId + ":" + id.substr(j);
}
for (std::map<string, string>::iterator it2 = table.begin();
it2 != table.end(); ++it2) {
remap(it2->second, it2->first);
}
}
//------------------------------------------------------------------------------
void TImageCache::clear(bool deleteFolder)
{
TThread::MutexLocker sl(&m_imp->m_mutex);
m_imp->m_uncompressedItems.clear();
m_imp->m_itemHistory.clear();
m_imp->m_compressedItems.clear();
m_imp->m_duplicatedItems.clear();
m_imp->m_itemsByImagePointer.clear();
if (deleteFolder && m_imp->m_rootDir != TFilePath())
TSystem::rmDirTree(m_imp->m_rootDir);
}
//------------------------------------------------------------------------------
void TImageCache::clearSceneImages()
{
TThread::MutexLocker sl(&m_imp->m_mutex);
//note the ';' - which follows ':' in the ascii table
m_imp->m_uncompressedItems.erase(m_imp->m_uncompressedItems.begin(), m_imp->m_uncompressedItems.lower_bound("$:"));
m_imp->m_uncompressedItems.erase(m_imp->m_uncompressedItems.lower_bound("$;"), m_imp->m_uncompressedItems.end());
m_imp->m_compressedItems.erase(m_imp->m_compressedItems.begin(), m_imp->m_compressedItems.lower_bound("$:"));
m_imp->m_compressedItems.erase(m_imp->m_compressedItems.lower_bound("$;"), m_imp->m_compressedItems.end());
m_imp->m_duplicatedItems.erase(m_imp->m_duplicatedItems.begin(), m_imp->m_duplicatedItems.lower_bound("$:"));
m_imp->m_duplicatedItems.erase(m_imp->m_duplicatedItems.lower_bound("$;"), m_imp->m_duplicatedItems.end());
//Clear maps whose id is on the second of map pairs.
std::map<TUINT32, std::string>::iterator it;
for (it = m_imp->m_itemHistory.begin(); it != m_imp->m_itemHistory.end();) {
if (it->second.size() >= 2 && it->second[0] == '$' && it->second[1] == ':')
++it;
else {
std::map<TUINT32, std::string>::iterator app = it;
app++;
m_imp->m_itemHistory.erase(it);
it = app;
}
}
std::map<void *, std::string>::iterator jt;
for (jt = m_imp->m_itemsByImagePointer.begin(); jt != m_imp->m_itemsByImagePointer.end();) {
if (jt->second.size() >= 2 && jt->second[0] == '$' && jt->second[1] == ':')
++jt;
else {
std::map<void *, std::string>::iterator app = jt;
app++;
m_imp->m_itemsByImagePointer.erase(jt);
jt = app;
}
}
}
//------------------------------------------------------------------------------
bool TImageCache::isCached(const string &id) const
{
TThread::MutexLocker sl(&m_imp->m_mutex);
return (m_imp->m_uncompressedItems.find(id) != m_imp->m_uncompressedItems.end() ||
m_imp->m_compressedItems.find(id) != m_imp->m_compressedItems.end() ||
m_imp->m_duplicatedItems.find(id) != m_imp->m_duplicatedItems.end());
}
//------------------------------------------------------------------------------
#ifdef LEVO
bool TImageCache::getSize(const string &id, TDimension &size) const
{
QMutexLocker sl(&m_imp->m_mutex);
std::map<string, CacheItemP>::iterator it = m_imp->m_uncompressedItems.find(id);
if (it != m_imp->m_uncompressedItems.end()) {
UncompressedOnMemoryCacheItemP uncompressed = it->second;
assert(uncompressed);
TToonzImageP ti = uncompressed->getImage();
if (ti) {
size = ti->getSize();
return true;
}
TRasterImageP ri = uncompressed->getImage();
if (ri && ri->getRaster()) {
size = ri->getRaster()->getSize();
return true;
}
return false;
}
std::map<string, CacheItemP>::iterator itc = m_imp->m_compressedItems.find(id);
if (itc == m_imp->m_compressedItems.end())
return false;
CacheItemP cacheItem = itc->second;
if (cacheItem->m_imageInfo) {
RasterImageInfo *rimageInfo = dynamic_cast<RasterImageInfo *>(cacheItem->m_imageInfo);
if (rimageInfo) {
size = rimageInfo->m_size;
return true;
}
ToonzImageInfo *timageInfo = dynamic_cast<ToonzImageInfo *>(cacheItem->m_imageInfo);
if (timageInfo) {
size = timageInfo->m_size;
return true;
}
}
return false;
}
//------------------------------------------------------------------------------
bool TImageCache::getSavebox(const string &id, TRect &savebox) const
{
QMutexLocker sl(&m_imp->m_mutex);
std::map<string, CacheItemP>::iterator it = m_imp->m_uncompressedItems.find(id);
if (it != m_imp->m_uncompressedItems.end()) {
UncompressedOnMemoryCacheItemP uncompressed = it->second;
assert(uncompressed);
TToonzImageP ti = uncompressed->getImage();
if (ti) {
savebox = ti->getSavebox();
return true;
}
TRasterImageP ri = uncompressed->getImage();
if (ri) {
savebox = ri->getSavebox();
return true;
}
return false;
}
std::map<string, CacheItemP>::iterator itc = m_imp->m_compressedItems.find(id);
if (itc == m_imp->m_compressedItems.end())
return false;
CacheItemP cacheItem = itc->second;
assert(cacheItem->m_imageInfo);
RasterImageInfo *rimageInfo = dynamic_cast<RasterImageInfo *>(cacheItem->m_imageInfo);
if (rimageInfo) {
savebox = rimageInfo->m_savebox;
return true;
}
#ifndef TNZCORE_LIGHT
ToonzImageInfo *timageInfo = dynamic_cast<ToonzImageInfo *>(cacheItem->m_imageInfo);
if (timageInfo) {
savebox = timageInfo->m_savebox;
return true;
}
#endif
return false;
}
//------------------------------------------------------------------------------
bool TImageCache::getDpi(const string &id, double &dpiX, double &dpiY) const
{
QMutexLocker sl(&m_imp->m_mutex);
std::map<string, CacheItemP>::iterator it = m_imp->m_uncompressedItems.find(id);
if (it != m_imp->m_uncompressedItems.end()) {
UncompressedOnMemoryCacheItemP uncompressed = it->second;
assert(uncompressed);
TToonzImageP ti = uncompressed->getImage();
if (ti) {
ti->getDpi(dpiX, dpiY);
return true;
}
TRasterImageP ri = uncompressed->getImage();
if (ri) {
ri->getDpi(dpiX, dpiY);
return true;
}
return false;
}
std::map<string, CacheItemP>::iterator itc = m_imp->m_compressedItems.find(id);
if (itc == m_imp->m_compressedItems.end())
return false;
CacheItemP cacheItem = itc->second;
assert(cacheItem->m_imageInfo);
RasterImageInfo *rimageInfo = dynamic_cast<RasterImageInfo *>(cacheItem->m_imageInfo);
if (rimageInfo) {
dpiX = rimageInfo->m_dpix;
dpiY = rimageInfo->m_dpiy;
return true;
}
#ifndef TNZCORE_LIGHT
ToonzImageInfo *timageInfo = dynamic_cast<ToonzImageInfo *>(cacheItem->m_imageInfo);
if (timageInfo) {
dpiX = timageInfo->m_dpix;
dpiY = timageInfo->m_dpiy;
return true;
}
#endif
return false;
}
//------------------------------------------------------------------------------
#endif
bool TImageCache::getSubsampling(const string &id, int &subs) const
{
TThread::MutexLocker sl(&m_imp->m_mutex);
std::map<string, string>::iterator it1;
if ((it1 = m_imp->m_duplicatedItems.find(id)) != m_imp->m_duplicatedItems.end()) {
assert(m_imp->m_duplicatedItems.find(it1->second) == m_imp->m_duplicatedItems.end());
return getSubsampling(it1->second, subs);
}
std::map<string, CacheItemP>::iterator it = m_imp->m_uncompressedItems.find(id);
if (it != m_imp->m_uncompressedItems.end()) {
UncompressedOnMemoryCacheItemP uncompressed = it->second;
assert(uncompressed);
#ifndef TNZCORE_LIGHT
if (TToonzImageP ti = uncompressed->getImage()) {
subs = ti->getSubsampling();
return true;
}
else
#endif
if (TRasterImageP ri = uncompressed->getImage()) {
subs = ri->getSubsampling();
return true;
} else
return false;
}
std::map<string, CacheItemP>::iterator itc = m_imp->m_compressedItems.find(id);
if (itc == m_imp->m_compressedItems.end())
return false;
CacheItemP cacheItem = itc->second;
assert(cacheItem->m_imageInfo);
if (RasterImageInfo *rimageInfo = dynamic_cast<RasterImageInfo *>(cacheItem->m_imageInfo)) {
subs = rimageInfo->m_subs;
return true;
}
#ifndef TNZCORE_LIGHT
else if (ToonzImageInfo *timageInfo = dynamic_cast<ToonzImageInfo *>(cacheItem->m_imageInfo)) {
subs = timageInfo->m_subs;
return true;
}
#endif
else
return false;
}
//------------------------------------------------------------------------------
bool TImageCache::hasBeenModified(const string &id, bool reset) const
{
TThread::MutexLocker sl(&m_imp->m_mutex);
std::map<string, string>::iterator it;
if ((it = m_imp->m_duplicatedItems.find(id)) != m_imp->m_duplicatedItems.end()) {
assert(m_imp->m_duplicatedItems.find(it->second) == m_imp->m_duplicatedItems.end());
return hasBeenModified(it->second, reset);
}
TImageP img;
std::map<string, CacheItemP>::iterator itu = m_imp->m_uncompressedItems.find(id);
if (itu != m_imp->m_uncompressedItems.end()) {
if (reset && itu->second->m_modified) {
itu->second->m_modified = false;
return true;
} else
return itu->second->m_modified;
}
return true; //not present in cache==modified (for particle purposes...)
}
//------------------------------------------------------------------------------
TImageP TImageCache::get(const string &id, bool toBeModified) const
{
return m_imp->get(id, toBeModified);
}
//------------------------------------------------------------------------------
TImageP TImageCache::Imp::get(const string &id, bool toBeModified)
{
TThread::MutexLocker sl(&m_mutex);
std::map<string, string>::const_iterator it;
if ((it = m_duplicatedItems.find(id)) != m_duplicatedItems.end()) {
assert(m_duplicatedItems.find(it->second) == m_duplicatedItems.end());
return get(it->second, toBeModified);
}
TImageP img;
std::map<string, CacheItemP>::iterator itu = m_uncompressedItems.find(id);
if (itu != m_uncompressedItems.end()) {
img = itu->second->getImage();
if (itu->second->m_historyCount != HistoryCount - 1) //significa che l'ultimo get non era sulla stessa immagine, quindi serve aggiornare l'history!
{
assert(m_itemHistory.find(itu->second->m_historyCount) != m_itemHistory.end());
m_itemHistory.erase(itu->second->m_historyCount);
m_itemHistory[HistoryCount] = id;
itu->second->m_historyCount = HistoryCount;
HistoryCount++;
}
if (toBeModified) {
itu->second->m_modified = true;
std::map<string, CacheItemP>::iterator itc = m_compressedItems.find(id);
if (itc != m_compressedItems.end())
m_compressedItems.erase(itc);
}
return img;
}
std::map<string, CacheItemP>::iterator itc = m_compressedItems.find(id);
if (itc == m_compressedItems.end())
return 0;
CacheItemP cacheItem = itc->second;
img = cacheItem->getImage();
CacheItemP uncompressed;
uncompressed = new UncompressedOnMemoryCacheItem(img);
m_uncompressedItems[itc->first] = uncompressed;
m_itemsByImagePointer[getPointer(img)] = itc->first;
m_itemHistory[HistoryCount] = itc->first;
uncompressed->m_historyCount = HistoryCount;
HistoryCount++;
if (CompressedOnMemoryCacheItemP(cacheItem))
//l'immagine compressa non la tengo insieme alla
//uncompressa se e' troppo grande
{
if (10 * cacheItem->getSize() > uncompressed->getSize()) {
m_compressedItems.erase(itc);
itc = m_compressedItems.end();
}
} else
assert((CompressedOnDiskCacheItemP)cacheItem || (UncompressedOnDiskCacheItemP)cacheItem); //deve essere compressa!
if (toBeModified && itc != m_compressedItems.end()) {
uncompressed->m_modified = true;
m_compressedItems.erase(itc);
}
uncompressed->m_cantCompress = toBeModified;
// se la memoria utilizzata e' superiore al massimo consentito, comprime
doCompress();
uncompressed->m_cantCompress = false;
//#define DO_MEMCHECK
#ifdef DO_MEMCHECK
assert(_CrtCheckMemory());
#endif
return img;
}
//------------------------------------------------------------------------------
namespace
{
class AccumulateMemUsage
{
public:
int operator()(int oldValue, std::pair<string, CacheItemP> item)
{
return oldValue + item.second->getSize();
}
};
}
UINT TImageCache::getMemUsage() const
{
TThread::MutexLocker sl(&m_imp->m_mutex);
int ret = std::accumulate(
m_imp->m_uncompressedItems.begin(),
m_imp->m_uncompressedItems.end(),
0,
AccumulateMemUsage());
return ret + std::accumulate(
m_imp->m_compressedItems.begin(),
m_imp->m_compressedItems.end(),
0,
AccumulateMemUsage());
}
//------------------------------------------------------------------------------
UINT TImageCache::getDiskUsage() const
{
return 0;
}
//------------------------------------------------------------------------------
UINT TImageCache::getMemUsage(const string &id) const
{
std::map<string, CacheItemP>::iterator it = m_imp->m_uncompressedItems.find(id);
if (it != m_imp->m_uncompressedItems.end())
return it->second->getSize();
it = m_imp->m_compressedItems.find(id);
if (it != m_imp->m_compressedItems.end())
return it->second->getSize();
return 0;
}
//------------------------------------------------------------------------------
//! Returns the uncompressed image size (in KB) of the image associated with
//! passd id, or 0 if none was found.
UINT TImageCache::getUncompressedMemUsage(const string &id) const
{
std::map<string, CacheItemP>::iterator it = m_imp->m_uncompressedItems.find(id);
if (it != m_imp->m_uncompressedItems.end())
return it->second->getSize();
it = m_imp->m_compressedItems.find(id);
if (it != m_imp->m_compressedItems.end())
return it->second->getSize();
return 0;
}
//------------------------------------------------------------------------------
/*
int TImageCache::getItemCount() const
{
return m_imp->m_uncompressedItems.size()+m_imp->m_compressedItems.size();
}
*/
//------------------------------------------------------------------------------
UINT TImageCache::getDiskUsage(const string &id) const
{
return 0;
}
//------------------------------------------------------------------------------
void TImageCache::dump(ostream &os) const
{
os << "mem: " << getMemUsage() << std::endl;
std::map<string, CacheItemP>::iterator it = m_imp->m_uncompressedItems.begin();
for (; it != m_imp->m_uncompressedItems.end(); ++it) {
os << it->first << std::endl;
}
}
//------------------------------------------------------------------------------
void TImageCache::outputMap(UINT chunkRequested, string filename)
{
m_imp->outputMap(chunkRequested, filename);
}
//------------------------------------------------------------------------------
void TImageCache::Imp::outputMap(UINT chunkRequested, string filename)
{
TThread::MutexLocker sl(&m_mutex);
//#ifdef _DEBUG
//static int Count = 0;
std::string st = filename /*+toString(Count++)*/ + ".txt";
TFilePath fp(st);
Tofstream os(fp);
int umcount1 = 0;
int umcount2 = 0;
int umcount3 = 0;
int cmcount = 0;
int cdcount = 0;
int umcount = 0;
int udcount = 0;
TUINT64 umsize1 = 0;
TUINT64 umsize2 = 0;
TUINT64 umsize3 = 0;
TUINT64 cmsize = 0;
TUINT64 cdsize = 0;
TUINT64 umsize = 0;
TUINT64 udsize = 0;
std::map<string, CacheItemP>::iterator itu = m_uncompressedItems.begin();
for (; itu != m_uncompressedItems.end(); ++itu) {
UncompressedOnMemoryCacheItemP uitem = itu->second;
if (uitem->m_image && hasExternalReferences(uitem->m_image)) {
umcount1++;
umsize1 += (TUINT64)(itu->second->getSize() / 1024.0);
} else if (uitem->m_cantCompress) {
umcount2++;
umsize2 += (TUINT64)(itu->second->getSize() / 1024.0);
} else {
umcount3++;
umsize3 += (TUINT64)(itu->second->getSize() / 1024.0);
}
}
std::map<string, CacheItemP>::iterator itc = m_compressedItems.begin();
for (; itc != m_compressedItems.end(); ++itc) {
CacheItemP boh = itc->second;
CompressedOnMemoryCacheItemP cmitem = itc->second;
CompressedOnDiskCacheItemP cditem = itc->second;
UncompressedOnDiskCacheItemP uditem = itc->second;
if (cmitem) {
cmcount++;
cmsize += cmitem->getSize();
} else if (cditem) {
cdcount++;
cdsize += cditem->getSize();
} else {
assert(uditem);
udcount++;
udsize += uditem->getSize();
}
}
TUINT64 currPhisMemoryAvail = (TUINT64)(TSystem::getFreeMemorySize(true) / 1024.0);
//TUINT64 currVirtualMemoryAvail = TSystem::getFreeMemorySize(false)/1024.0;
os << "************************************************************\n";
os << "***requested memory: " + toString((int)chunkRequested / 1048576.0) + " MB\n";
//os<<"*** memory in rasters: " + toString((int)TRaster::getTotalMemoryInKB()/1024.0) + " MB\n";
//os<<"***virtualmem " + toString((int)currVirtualMemoryAvail) + " MB\n";
os << "***phismem " + toString((int)currPhisMemoryAvail) + " MB; percent of tot:" + toString((int)((currPhisMemoryAvail * 100) / m_reservedMemory)) + "\n";
//os<<"***bigmem available" + toString((int)TBigMemoryManager::instance()->getAvailableMemoryinKb());
os << "***uncompressed NOT compressable(refcount>1) " + toString(umcount1) + " " + toString(umsize1 / 1024.0) + " MB\n";
os << "***uncompressed NOT compressable(cantCompress) " + toString(umcount2) + " " + toString(umsize2 / 1024.0) + " MB\n";
os << "***uncompressed compressable " + toString(umcount3) + " " + toString(umsize3 / 1024.0) + " MB\n";
os << "***compressed on mem " + toString(cmcount) + " " + toString((int)cmsize / 1048576.0) + " MB\n";
os << "***compressed on disk " + toString(cdcount) + " " + toString((int)cdsize / 1048576.0) + " MB\n";
os << "***uncompressed on disk " + toString(udcount) + " " + toString((int)udsize / 1048576.0) + " MB\n";
//TBigMemoryManager::instance()->printMap();
//#endif
}
//------------------------------------------------------------------------------
void TImageCache::compress(const string &id)
{
m_imp->doCompress(id);
}
//------------------------------------------------------------------------------
#ifndef TNZCORE_LIGHT
void TImageCache::add(const QString &id, const TImageP &img, bool overwrite)
{
if (!isEnabled())
return;
m_imp->add(id.toStdString(), img, overwrite);
}
//------------------------------------------------------------------------------
void TImageCache::remove(const QString &id)
{
m_imp->remove(id.toStdString());
}
//------------------------------------------------------------------------------
TImageP TImageCache::get(const QString &id, bool toBeModified) const
{
return get(id.toStdString(), toBeModified);
}
#endif
//*************************************************************************************
// TCachedImage implementation
//*************************************************************************************
DEFINE_CLASS_CODE(TCachedImage, 103)
TCachedImage::TCachedImage()
: TSmartObject(m_classCode), m_ref(TImageCache::instance()->getUniqueId())
{
}
//------------------------------------------------------------------------------
TCachedImage::TCachedImage(const TImageP &img)
: TSmartObject(m_classCode), m_ref(TImageCache::instance()->getUniqueId())
{
setImage(img);
}
//------------------------------------------------------------------------------
TCachedImage::~TCachedImage()
{
TImageCache::instance()->remove(m_ref);
}
//------------------------------------------------------------------------------
void TCachedImage::setImage(const TImageP &img, bool overwrite)
{
TImageCache::instance()->add(m_ref, img, overwrite);
}
//------------------------------------------------------------------------------
TImageP TCachedImage::image(bool toBeModified)
{
return TImageCache::instance()->get(m_ref, toBeModified);
}