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