1 
2 
3 // Toonz core includes
4 #include "timagecache.h"
5 #include "trasterimage.h"
6 #include "ttoonzimage.h"
7 #include "tmeshimage.h"
8 #include "timage_io.h"
9 
10 // Qt includes (mutexing classes)
11 #include <QMutex>
12 #include <QMutexLocker>
13 #include <QReadWriteLock>
14 #include <QReadLocker>
15 #include <QWriteLocker>
16 
17 #include "toonz/imagemanager.h"
18 #include "toonz/txshsimplelevel.h"
19 
20 /* EXPLANATION (by Daniele):
21 
22   Images / Image Infos retrieval is quite a frequent task throughout Toonz - in
23   particular,
24   as Render operations tend to be multithreaded, it is important to ensure that
25   the
26   ImageManager treats these operations efficiently.
27 
28   Most of the image manager's job is that of caching hard-built image data so
29   that successive
30   queries avoid rebuilding the same images again.
31 
32   In a multithreaded environment, we must make sure that multiple threads block
33   each other out
34   as little as possible.
35 
36   Here are the main performance and threading notes:
37 
38     - Image infos are completely cached, while image cachability is
39   user-specified.
40       This is needed as some images must be loaded only temporarily.
41 
42     - One mutex will be used to protect the bindings table. It is the outermost
43   mutex.
44 
45     - One mutex (read/write lock) will be used to protect access to EACH
46   individual image.
47       Testing should be required. If file access is found to be too strictly
48   sequential,
49       perhaps a single mutex could suffice.
50 
51     - Having different mutexes to protect images and image infos is currently
52   not implemented,
53       but could be. Testing required.
54 */
55 
56 /*
57   TODO: TXshSimpleLevel::setFrame(...) usa aggiunte/rimozioni manuali di
58   immagini associate
59   a binding nell'ImageManager - aspettandosi che poiche' l'immagine e' presente
60   in cache,
61   verra' beccata...
62 */
63 
64 //************************************************************************************
65 //    Image Builder implementation
66 //************************************************************************************
67 
68 DEFINE_CLASS_CODE(ImageBuilder, 100)
69 
70 //-----------------------------------------------------------------------------
71 
ImageBuilder()72 ImageBuilder::ImageBuilder()
73     : TSmartObject(m_classCode)
74     , m_imageBuildingLock(QReadWriteLock::Recursive)
75     , m_cached(false)
76     , m_modified(false)
77     , m_imFlags(ImageManager::none) {}
78 
79 //-----------------------------------------------------------------------------
80 
~ImageBuilder()81 ImageBuilder::~ImageBuilder() {}
82 
83 //-----------------------------------------------------------------------------
84 
areInfosCompatible(int imFlags,void * extData)85 bool ImageBuilder::areInfosCompatible(int imFlags, void *extData) {
86   return m_info.m_valid;
87 }
88 
89 //-----------------------------------------------------------------------------
90 
isImageCompatible(int imFlags,void * extData)91 bool ImageBuilder::isImageCompatible(int imFlags, void *extData) {
92   return m_info.m_valid;
93 }
94 
95 //-----------------------------------------------------------------------------
96 
setImageInfo(TImageInfo & info,const TDimension & size)97 bool ImageBuilder::setImageInfo(TImageInfo &info, const TDimension &size) {
98   info         = TImageInfo();
99   info.m_lx    = size.lx;
100   info.m_ly    = size.ly;
101   info.m_x0    = 0;
102   info.m_y0    = 0;
103   info.m_x1    = size.lx - 1;
104   info.m_y1    = size.ly - 1;
105   info.m_valid = true;
106 
107   return true;
108 }
109 
110 //-----------------------------------------------------------------------------
111 
setImageInfo(TImageInfo & info,TImage * img)112 bool ImageBuilder::setImageInfo(TImageInfo &info, TImage *img) {
113   info = TImageInfo();
114   if (TRasterImageP ri = TRasterImageP(img)) {
115     TRasterP ras = ri->getRaster();
116     info.m_lx    = ras->getLx();
117     info.m_ly    = ras->getLy();
118     ri->getDpi(info.m_dpix, info.m_dpiy);
119     TRect savebox = ri->getSavebox();
120     info.m_x0     = savebox.x0;
121     info.m_y0     = savebox.y0;
122     info.m_x1     = savebox.x1;
123     info.m_y1     = savebox.y1;
124   } else if (TToonzImageP ti = TToonzImageP(img)) {
125     TRasterP ras = ti->getRaster();
126     info.m_lx    = ras->getLx();
127     info.m_ly    = ras->getLy();
128     ti->getDpi(info.m_dpix, info.m_dpiy);
129     TRect savebox = ti->getSavebox();
130     info.m_x0     = savebox.x0;
131     info.m_y0     = savebox.y0;
132     info.m_x1     = savebox.x1;
133     info.m_y1     = savebox.y1;
134   } else if (TMeshImageP mi = TMeshImageP(img)) {
135     mi->getDpi(info.m_dpix, info.m_dpiy);
136   }
137 
138   info.m_valid = true;
139   return true;
140 }
141 
142 //-----------------------------------------------------------------------------
143 
setImageInfo(TImageInfo & info,TImageReader * ir)144 bool ImageBuilder::setImageInfo(TImageInfo &info, TImageReader *ir) {
145   info = TImageInfo();
146 
147   const TImageInfo *tmp = ir->getImageInfo();
148   if (tmp) {
149     info = *tmp;
150     if (info.m_x1 < info.m_x0 || info.m_y1 < info.m_y0) {
151       info.m_x0 = info.m_y0 = 0;
152       info.m_x1             = info.m_lx - 1;
153       info.m_y1             = info.m_ly - 1;
154     }
155 
156     info.m_valid = true;
157     return true;
158   }
159 
160   return false;
161 }
162 
163 //************************************************************************************
164 //    Image Manager Privates implementation
165 //************************************************************************************
166 
167 struct ImageManager::Imp {
168   QReadWriteLock m_tableLock;  //!< Lock for the builders table
169   std::map<std::string, ImageBuilderP>
170       m_builders;  //!< identifier -> ImageBuilder table
171 
172 public:
ImpImageManager::Imp173   Imp() : m_tableLock(QReadWriteLock::Recursive) {}
174 
clearImageManager::Imp175   void clear() { m_builders.clear(); }
176 };
177 
178 //************************************************************************************
179 //    Image Manager implementation
180 //************************************************************************************
181 
ImageManager()182 ImageManager::ImageManager() : m_imp(new Imp) {}
183 
184 //-----------------------------------------------------------------------------
185 
~ImageManager()186 ImageManager::~ImageManager() {}
187 
188 //-----------------------------------------------------------------------------
189 
instance()190 ImageManager *ImageManager::instance() {
191   // Re-introdotto possibile baco: voglio controllare se esiste ancora
192   static ImageManager theInstance;
193   return &theInstance;
194 }
195 
196 //-----------------------------------------------------------------------------
197 
bind(const std::string & id,ImageBuilder * builderPtr)198 void ImageManager::bind(const std::string &id, ImageBuilder *builderPtr) {
199   if (!builderPtr) {
200     unbind(id);
201     return;
202   }
203 
204   QWriteLocker locker(&m_imp->m_tableLock);
205 
206   ImageBuilderP &builderP = m_imp->m_builders[id];
207   if (builderP && builderP->m_cached) TImageCache::instance()->remove(id);
208 
209   builderP = builderPtr;
210 }
211 
212 //-----------------------------------------------------------------------------
213 
unbind(const std::string & id)214 bool ImageManager::unbind(const std::string &id) {
215   QWriteLocker locker(&m_imp->m_tableLock);
216 
217   std::map<std::string, ImageBuilderP>::iterator it =
218       m_imp->m_builders.find(id);
219   if (it == m_imp->m_builders.end()) return false;
220 
221   ImageBuilderP &builderP = it->second;
222   if (builderP && builderP->m_cached) TImageCache::instance()->remove(id);
223 
224   m_imp->m_builders.erase(it);
225   return true;
226 }
227 
228 //-----------------------------------------------------------------------------
229 
isBound(const std::string & id) const230 bool ImageManager::isBound(const std::string &id) const {
231   QReadLocker locker(&m_imp->m_tableLock);
232   return m_imp->m_builders.find(id) != m_imp->m_builders.end();
233 }
234 
235 //-----------------------------------------------------------------------------
236 
rebind(const std::string & srcId,const std::string & dstId)237 bool ImageManager::rebind(const std::string &srcId, const std::string &dstId) {
238   QWriteLocker locker(&m_imp->m_tableLock);
239 
240   std::map<std::string, ImageBuilderP>::iterator st =
241       m_imp->m_builders.find(srcId);
242   if (st == m_imp->m_builders.end()) return false;
243 
244   ImageBuilderP builder = st->second;
245 
246   m_imp->m_builders.erase(st);
247   m_imp->m_builders[dstId] = builder;
248 
249   m_imp->m_builders[dstId]->m_cached   = true;
250   m_imp->m_builders[dstId]->m_modified = true;
251 
252   TImageCache::instance()->remap(dstId, srcId);
253 
254   return true;
255 }
256 
renumber(const std::string & srcId,const TFrameId & fid)257 bool ImageManager::renumber(const std::string &srcId, const TFrameId &fid) {
258   std::map<std::string, ImageBuilderP>::iterator st =
259       m_imp->m_builders.find(srcId);
260   if (st == m_imp->m_builders.end()) return false;
261 
262   m_imp->m_builders[srcId]->setFid(fid);
263 
264   return true;
265 }
266 
267 //-----------------------------------------------------------------------------
268 
clear()269 void ImageManager::clear() {
270   QWriteLocker locker(&m_imp->m_tableLock);
271 
272   TImageCache::instance()->clearSceneImages();
273   m_imp->clear();
274 }
275 
276 //-----------------------------------------------------------------------------
277 
getInfo(const std::string & id,int imFlags,void * extData)278 TImageInfo *ImageManager::getInfo(const std::string &id, int imFlags,
279                                   void *extData) {
280   // Lock for table read and try to find data in the cache
281   QReadLocker tableLocker(&m_imp->m_tableLock);
282 
283   std::map<std::string, ImageBuilderP>::iterator it =
284       m_imp->m_builders.find(id);
285   if (it == m_imp->m_builders.end()) return 0;
286 
287   ImageBuilderP &builder = it->second;
288 
289   assert(!((imFlags & ImageManager::toBeModified) && !builder->m_modified));
290 
291   // Check cached data
292   if (builder->areInfosCompatible(imFlags, extData)) return &builder->m_info;
293 
294   QWriteLocker imageBuildingLocker(&builder->m_imageBuildingLock);
295 
296   // Re-check as waiting may have changed the situation
297   if (builder->areInfosCompatible(imFlags, extData)) return &builder->m_info;
298 
299   TImageInfo info;
300   if (builder->getInfo(info, imFlags, extData)) {
301     builder->m_info = info;
302     return &builder->m_info;
303   }
304 
305   return 0;
306 }
307 
308 //-----------------------------------------------------------------------------
309 
getImage(const std::string & id,int imFlags,void * extData)310 TImageP ImageManager::getImage(const std::string &id, int imFlags,
311                                void *extData) {
312   assert(!((imFlags & ImageManager::toBeModified) &&
313            (imFlags & ImageManager::dontPutInCache)));
314   assert(!((imFlags & ImageManager::toBeModified) &&
315            (imFlags & ImageManager::toBeSaved)));
316 
317   // Lock for table read and try to find data in the cache
318   QReadLocker tableLocker(&m_imp->m_tableLock);
319 
320   std::map<std::string, ImageBuilderP>::iterator it =
321       m_imp->m_builders.find(id);
322   if (it == m_imp->m_builders.end()) return TImageP();
323 
324   ImageBuilderP &builder = it->second;
325   bool modified          = builder->m_modified;
326 
327   // Analyze imFlags
328   bool _putInCache =
329       TImageCache::instance()->isEnabled() && !(bool)(imFlags & dontPutInCache);
330   bool _toBeModified = (imFlags & toBeModified);
331   bool _toBeSaved    = (imFlags & toBeSaved);
332 
333   // Update the modified flag according to the specified flags
334   if (_toBeModified)
335     builder->m_modified = true;
336   else if (_toBeSaved)
337     builder->m_modified = false;
338 
339   // Now, fetch the image.
340   TImageP img;
341 
342   if (builder->m_cached) {
343     if (modified || builder->isImageCompatible(imFlags, extData)) {
344       img = TImageCache::instance()->get(id, _toBeModified);
345 
346       assert(img);
347       if (img) return img;
348     }
349   }
350 
351   // Lock for image building
352   QWriteLocker imageBuildingLocker(&builder->m_imageBuildingLock);
353 
354   // As multiple threads may block on filesLocker, re-check if the image is now
355   // available
356   if (builder->m_cached) {
357     if (modified || builder->isImageCompatible(imFlags, extData)) {
358       img = TImageCache::instance()->get(id, _toBeModified);
359 
360       assert(img);
361       if (img) return img;
362     }
363   }
364 
365   // The image was either not available or not conforming to the required
366   // specifications.
367   // We have to build it now, then.
368 
369   // Build the image
370   img = builder->build(imFlags, extData);
371 
372   if (img && _putInCache) {
373     builder->m_cached   = true;
374     builder->m_modified = _toBeModified;
375 
376     TImageCache::instance()->add(id, img, true);
377   }
378 
379   return img;
380 }
381 
382 //-----------------------------------------------------------------------------
383 // load icon (and image) data of all frames into cache
loadAllTlvIconsAndPutInCache(TXshSimpleLevel * level,std::vector<TFrameId> fids,std::vector<std::string> iconIds,bool cacheImagesAsWell)384 void ImageManager::loadAllTlvIconsAndPutInCache(
385     TXshSimpleLevel *level, std::vector<TFrameId> fids,
386     std::vector<std::string> iconIds, bool cacheImagesAsWell) {
387   if (fids.empty() || iconIds.empty()) return;
388   // number of fid and iconId should be the same
389   if ((int)fids.size() != (int)iconIds.size()) return;
390 
391   // obtain ImageLoader with the first fId
392   TImageInfo info;
393   std::map<std::string, ImageBuilderP>::iterator it =
394       m_imp->m_builders.find(level->getImageId(fids[0]));
395   if (it != m_imp->m_builders.end()) {
396     const ImageBuilderP &builder = it->second;
397     assert(builder);
398     assert(builder->getRefCount() > 0);
399 
400     // this function in reimpremented only in ImageLoader
401     builder->buildAllIconsAndPutInCache(level, fids, iconIds,
402                                         cacheImagesAsWell);
403     builder->getInfo(info, ImageManager::none, 0);
404   }
405   if (cacheImagesAsWell) {
406     // reset the savebox
407     info.m_x0 = info.m_y0 = 0;
408     info.m_x1             = info.m_lx - 1;
409     info.m_y1             = info.m_ly - 1;
410 
411     // put flags to all builders
412     for (int f = 0; f < fids.size(); f++) {
413       std::map<std::string, ImageBuilderP>::iterator it =
414           m_imp->m_builders.find(level->getImageId(fids[f]));
415       if (it != m_imp->m_builders.end()) {
416         const ImageBuilderP &builder = it->second;
417         builder->setImageCachedAndModified();
418         builder->m_info = info;
419       }
420     }
421   }
422 }
423 
424 //-----------------------------------------------------------------------------
425 
invalidate(const std::string & id)426 bool ImageManager::invalidate(const std::string &id) {
427   QWriteLocker locker(&m_imp->m_tableLock);
428 
429   std::map<std::string, ImageBuilderP>::iterator it =
430       m_imp->m_builders.find(id);
431   if (it == m_imp->m_builders.end()) return false;
432 
433   ImageBuilderP &builder = it->second;
434 
435   builder->invalidate();
436   builder->m_cached = builder->m_modified = false;
437 
438   TImageCache::instance()->remove(id);
439 
440   return true;
441 }
442 
443 //-----------------------------------------------------------------------------
444 
setImage(const std::string & id,const TImageP & img)445 bool ImageManager::setImage(const std::string &id, const TImageP &img) {
446   if (!img) return invalidate(id);
447 
448   QWriteLocker locker(&m_imp->m_tableLock);
449 
450   std::map<std::string, ImageBuilderP>::iterator it =
451       m_imp->m_builders.find(id);
452   if (it == m_imp->m_builders.end()) return false;
453 
454   ImageBuilderP &builder = it->second;
455 
456   builder->invalidate();  // WARNING: Not all infos are correctly restored
457   ImageBuilder::setImageInfo(
458       builder->m_info,
459       img.getPointer());  // from supplied image - must investigate further...
460 
461   TImageCache::instance()->add(id, img, true);
462   builder->m_cached = builder->m_modified = true;
463 
464   return true;
465 }
466 
467 //-----------------------------------------------------------------------------
468 
getBuilder(const std::string & id)469 ImageBuilder *ImageManager::getBuilder(const std::string &id) {
470   QWriteLocker locker(&m_imp->m_tableLock);
471 
472   std::map<std::string, ImageBuilderP>::iterator it =
473       m_imp->m_builders.find(id);
474   return (it == m_imp->m_builders.end()) ? (ImageBuilder *)0
475                                          : it->second.getPointer();
476 }
477 
478 //-----------------------------------------------------------------------------
479 
isCached(const std::string & id)480 bool ImageManager::isCached(const std::string &id) {
481   QWriteLocker locker(&m_imp->m_tableLock);
482 
483   std::map<std::string, ImageBuilderP>::iterator it =
484       m_imp->m_builders.find(id);
485   return (it == m_imp->m_builders.end()) ? false : it->second->m_cached;
486 }
487 
488 //-----------------------------------------------------------------------------
489 
isModified(const std::string & id)490 bool ImageManager::isModified(const std::string &id) {
491   QWriteLocker locker(&m_imp->m_tableLock);
492 
493   std::map<std::string, ImageBuilderP>::iterator it =
494       m_imp->m_builders.find(id);
495   return (it == m_imp->m_builders.end()) ? false : it->second->m_modified;
496 }
497