1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_sd.hxx" 30 31 #include "SlsBitmapCache.hxx" 32 #include "SlsCacheCompactor.hxx" 33 #include "SlsBitmapCompressor.hxx" 34 #include "SlsCacheConfiguration.hxx" 35 36 #include "sdpage.hxx" 37 #include "drawdoc.hxx" 38 39 // Uncomment the following define for some more OSL_TRACE messages. 40 #ifdef DEBUG 41 //#define VERBOSE 42 #endif 43 44 // Define the default value for the maximal cache size that is used for 45 // previews that are currently not visible. The visible previews are all 46 // held in memory at all times. This default is used only when the 47 // configuration does not have a value. 48 static const sal_Int32 MAXIMAL_CACHE_SIZE = 4L*1024L*1024L; 49 50 using namespace ::com::sun::star::uno; 51 52 namespace sd { namespace slidesorter { namespace cache { 53 54 class BitmapCache::CacheEntry 55 { 56 public: 57 CacheEntry(const Bitmap& rBitmap, sal_Int32 nLastAccessTime, bool bIsPrecious); 58 CacheEntry(sal_Int32 nLastAccessTime, bool bIsPrecious); 59 ~CacheEntry (void) {}; 60 inline void Recycle (const CacheEntry& rEntry); 61 inline sal_Int32 GetMemorySize (void) const; 62 void Compress (const ::boost::shared_ptr<BitmapCompressor>& rpCompressor); 63 inline void Decompress (void); 64 65 bool IsUpToDate (void) const { return mbIsUpToDate; } 66 void SetUpToDate (bool bIsUpToDate) { mbIsUpToDate = bIsUpToDate; } 67 sal_Int32 GetAccessTime (void) const { return mnLastAccessTime; } 68 void SetAccessTime (sal_Int32 nAccessTime) { mnLastAccessTime = nAccessTime; } 69 70 Bitmap GetPreview (void) const { return maPreview; } 71 inline void SetPreview (const Bitmap& rPreview); 72 bool HasPreview (void) const; 73 74 Bitmap GetMarkedPreview (void) const { return maMarkedPreview; } 75 inline void SetMarkedPreview (const Bitmap& rMarkePreview); 76 bool HasMarkedPreview (void) const; 77 78 bool HasReplacement (void) const { return (mpReplacement.get() != NULL); } 79 inline bool HasLosslessReplacement (void) const; 80 void Clear (void) { maPreview.SetEmpty(); maMarkedPreview.SetEmpty(); 81 mpReplacement.reset(); mpCompressor.reset(); } 82 void Invalidate (void) { mpReplacement.reset(); mpCompressor.reset(); mbIsUpToDate = false; } 83 bool IsPrecious (void) const { return mbIsPrecious; } 84 void SetPrecious (bool bIsPrecious) { mbIsPrecious = bIsPrecious; } 85 86 private: 87 Bitmap maPreview; 88 Bitmap maMarkedPreview; 89 ::boost::shared_ptr<BitmapReplacement> mpReplacement; 90 ::boost::shared_ptr<BitmapCompressor> mpCompressor; 91 Size maBitmapSize; 92 bool mbIsUpToDate; 93 sal_Int32 mnLastAccessTime; 94 // When this flag is set then the bitmap is not modified by a cache 95 // compactor. 96 bool mbIsPrecious; 97 }; 98 class CacheEntry; 99 100 class CacheHash { 101 public: 102 size_t operator()(const BitmapCache::CacheKey& p) const 103 { return (size_t)p; } 104 }; 105 106 class BitmapCache::CacheBitmapContainer 107 : public ::std::hash_map<CacheKey, CacheEntry, CacheHash> 108 { 109 public: 110 CacheBitmapContainer (void) {} 111 }; 112 113 namespace { 114 115 typedef ::std::vector< 116 ::std::pair< ::sd::slidesorter::cache::BitmapCache::CacheKey, 117 ::sd::slidesorter::cache::BitmapCache::CacheEntry> 118 > SortableBitmapContainer; 119 120 /** Compare elements of the bitmap cache according to their last access 121 time. 122 */ 123 class AccessTimeComparator 124 { 125 public: 126 bool operator () ( 127 const SortableBitmapContainer::value_type& e1, 128 const SortableBitmapContainer::value_type& e2) 129 { 130 return e1.second.GetAccessTime() < e2.second.GetAccessTime(); 131 } 132 }; 133 134 135 } // end of anonymous namespace 136 137 138 //===== BitmapCache ========================================================= 139 140 BitmapCache::BitmapCache (const sal_Int32 nMaximalNormalCacheSize) 141 : maMutex(), 142 mpBitmapContainer(new CacheBitmapContainer()), 143 mnNormalCacheSize(0), 144 mnPreciousCacheSize(0), 145 mnCurrentAccessTime(0), 146 mnMaximalNormalCacheSize(MAXIMAL_CACHE_SIZE), 147 mpCacheCompactor(), 148 mbIsFull(false) 149 { 150 if (nMaximalNormalCacheSize > 0) 151 mnMaximalNormalCacheSize = nMaximalNormalCacheSize; 152 else 153 { 154 Any aCacheSize (CacheConfiguration::Instance()->GetValue( 155 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CacheSize")))); 156 if (aCacheSize.has<sal_Int32>()) 157 aCacheSize >>= mnMaximalNormalCacheSize; 158 } 159 160 mpCacheCompactor = CacheCompactor::Create(*this,mnMaximalNormalCacheSize); 161 } 162 163 164 165 166 BitmapCache::~BitmapCache (void) 167 { 168 Clear(); 169 } 170 171 172 173 174 void BitmapCache::Clear (void) 175 { 176 ::osl::MutexGuard aGuard (maMutex); 177 178 mpBitmapContainer->clear(); 179 mnNormalCacheSize = 0; 180 mnPreciousCacheSize = 0; 181 mnCurrentAccessTime = 0; 182 } 183 184 185 186 187 bool BitmapCache::IsFull (void) const 188 { 189 return mbIsFull; 190 } 191 192 193 194 195 sal_Int32 BitmapCache::GetSize (void) 196 { 197 return mnNormalCacheSize; 198 } 199 200 201 202 203 bool BitmapCache::HasBitmap (const CacheKey& rKey) 204 { 205 ::osl::MutexGuard aGuard (maMutex); 206 207 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); 208 return (iEntry != mpBitmapContainer->end() 209 && (iEntry->second.HasPreview() || iEntry->second.HasReplacement())); 210 } 211 212 213 214 215 bool BitmapCache::BitmapIsUpToDate (const CacheKey& rKey) 216 { 217 ::osl::MutexGuard aGuard (maMutex); 218 219 bool bIsUpToDate = false; 220 CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey)); 221 if (aIterator != mpBitmapContainer->end()) 222 bIsUpToDate = aIterator->second.IsUpToDate(); 223 224 return bIsUpToDate; 225 } 226 227 228 229 230 Bitmap BitmapCache::GetBitmap (const CacheKey& rKey) 231 { 232 ::osl::MutexGuard aGuard (maMutex); 233 234 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); 235 if (iEntry == mpBitmapContainer->end()) 236 { 237 // Create an empty bitmap for the given key that acts as placeholder 238 // until we are given the real one. Mark it as not being up to date. 239 SetBitmap(rKey, Bitmap(), false); 240 iEntry = mpBitmapContainer->find(rKey); 241 iEntry->second.SetUpToDate(false); 242 } 243 else 244 { 245 iEntry->second.SetAccessTime(mnCurrentAccessTime++); 246 247 // Maybe we have to decompress the preview. 248 if ( ! iEntry->second.HasPreview() && iEntry->second.HasReplacement()) 249 { 250 UpdateCacheSize(iEntry->second, REMOVE); 251 iEntry->second.Decompress(); 252 UpdateCacheSize(iEntry->second, ADD); 253 } 254 } 255 return iEntry->second.GetPreview(); 256 } 257 258 259 260 261 Bitmap BitmapCache::GetMarkedBitmap (const CacheKey& rKey) 262 { 263 ::osl::MutexGuard aGuard (maMutex); 264 265 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); 266 if (iEntry != mpBitmapContainer->end()) 267 { 268 iEntry->second.SetAccessTime(mnCurrentAccessTime++); 269 return iEntry->second.GetMarkedPreview(); 270 } 271 else 272 return Bitmap(); 273 } 274 275 276 277 278 void BitmapCache::ReleaseBitmap (const CacheKey& rKey) 279 { 280 ::osl::MutexGuard aGuard (maMutex); 281 282 CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey)); 283 if (aIterator != mpBitmapContainer->end()) 284 { 285 UpdateCacheSize(aIterator->second, REMOVE); 286 mpBitmapContainer->erase(aIterator); 287 } 288 } 289 290 291 292 293 bool BitmapCache::InvalidateBitmap (const CacheKey& rKey) 294 { 295 ::osl::MutexGuard aGuard (maMutex); 296 297 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); 298 if (iEntry != mpBitmapContainer->end()) 299 { 300 iEntry->second.SetUpToDate(false); 301 302 // When there is a preview then we release the replacement. The 303 // preview itself is kept until a new one is created. 304 if (iEntry->second.HasPreview()) 305 { 306 UpdateCacheSize(iEntry->second, REMOVE); 307 iEntry->second.Invalidate(); 308 UpdateCacheSize(iEntry->second, ADD); 309 } 310 return true; 311 } 312 else 313 return false; 314 } 315 316 317 318 319 void BitmapCache::InvalidateCache (void) 320 { 321 ::osl::MutexGuard aGuard (maMutex); 322 323 CacheBitmapContainer::iterator iEntry; 324 for (iEntry=mpBitmapContainer->begin(); iEntry!=mpBitmapContainer->end(); ++iEntry) 325 { 326 iEntry->second.Invalidate(); 327 } 328 ReCalculateTotalCacheSize(); 329 } 330 331 332 333 334 void BitmapCache::SetBitmap ( 335 const CacheKey& rKey, 336 const Bitmap& rPreview, 337 bool bIsPrecious) 338 { 339 ::osl::MutexGuard aGuard (maMutex); 340 341 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); 342 if (iEntry != mpBitmapContainer->end()) 343 { 344 UpdateCacheSize(iEntry->second, REMOVE); 345 iEntry->second.SetPreview(rPreview); 346 iEntry->second.SetUpToDate(true); 347 iEntry->second.SetAccessTime(mnCurrentAccessTime++); 348 } 349 else 350 { 351 iEntry = mpBitmapContainer->insert(CacheBitmapContainer::value_type ( 352 rKey, 353 CacheEntry(rPreview, mnCurrentAccessTime++, bIsPrecious)) 354 ).first; 355 } 356 357 if (iEntry != mpBitmapContainer->end()) 358 UpdateCacheSize(iEntry->second, ADD); 359 } 360 361 362 363 364 void BitmapCache::SetMarkedBitmap ( 365 const CacheKey& rKey, 366 const Bitmap& rPreview) 367 { 368 ::osl::MutexGuard aGuard (maMutex); 369 370 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); 371 if (iEntry != mpBitmapContainer->end()) 372 { 373 UpdateCacheSize(iEntry->second, REMOVE); 374 iEntry->second.SetMarkedPreview(rPreview); 375 iEntry->second.SetAccessTime(mnCurrentAccessTime++); 376 UpdateCacheSize(iEntry->second, ADD); 377 } 378 } 379 380 381 382 383 void BitmapCache::SetPrecious (const CacheKey& rKey, bool bIsPrecious) 384 { 385 ::osl::MutexGuard aGuard (maMutex); 386 387 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); 388 if (iEntry != mpBitmapContainer->end()) 389 { 390 if (iEntry->second.IsPrecious() != bIsPrecious) 391 { 392 UpdateCacheSize(iEntry->second, REMOVE); 393 iEntry->second.SetPrecious(bIsPrecious); 394 UpdateCacheSize(iEntry->second, ADD); 395 } 396 } 397 else if (bIsPrecious) 398 { 399 iEntry = mpBitmapContainer->insert(CacheBitmapContainer::value_type ( 400 rKey, 401 CacheEntry(Bitmap(), mnCurrentAccessTime++, bIsPrecious)) 402 ).first; 403 UpdateCacheSize(iEntry->second, ADD); 404 } 405 } 406 407 408 409 410 void BitmapCache::ReCalculateTotalCacheSize (void) 411 { 412 ::osl::MutexGuard aGuard (maMutex); 413 414 mnNormalCacheSize = 0; 415 mnPreciousCacheSize = 0; 416 CacheBitmapContainer::iterator iEntry; 417 for (iEntry=mpBitmapContainer->begin(); iEntry!=mpBitmapContainer->end(); ++iEntry) 418 { 419 if (iEntry->second.IsPrecious()) 420 mnPreciousCacheSize += iEntry->second.GetMemorySize(); 421 else 422 mnNormalCacheSize += iEntry->second.GetMemorySize(); 423 } 424 mbIsFull = (mnNormalCacheSize >= mnMaximalNormalCacheSize); 425 426 #ifdef VERBOSE 427 OSL_TRACE("cache size is %d/%d", mnNormalCacheSize, mnPreciousCacheSize); 428 #endif 429 } 430 431 432 433 434 void BitmapCache::Recycle (const BitmapCache& rCache) 435 { 436 ::osl::MutexGuard aGuard (maMutex); 437 438 CacheBitmapContainer::const_iterator iOtherEntry; 439 for (iOtherEntry=rCache.mpBitmapContainer->begin(); 440 iOtherEntry!=rCache.mpBitmapContainer->end(); 441 ++iOtherEntry) 442 { 443 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(iOtherEntry->first)); 444 if (iEntry == mpBitmapContainer->end()) 445 { 446 iEntry = mpBitmapContainer->insert(CacheBitmapContainer::value_type ( 447 iOtherEntry->first, 448 CacheEntry(mnCurrentAccessTime++, true)) 449 ).first; 450 UpdateCacheSize(iEntry->second, ADD); 451 } 452 if (iEntry != mpBitmapContainer->end()) 453 { 454 UpdateCacheSize(iEntry->second, REMOVE); 455 iEntry->second.Recycle(iOtherEntry->second); 456 UpdateCacheSize(iEntry->second, ADD); 457 } 458 } 459 } 460 461 462 463 464 ::std::auto_ptr<BitmapCache::CacheIndex> BitmapCache::GetCacheIndex ( 465 bool bIncludePrecious, 466 bool bIncludeNoPreview) const 467 { 468 ::osl::MutexGuard aGuard (maMutex); 469 470 // Create a copy of the bitmap container. 471 SortableBitmapContainer aSortedContainer; 472 aSortedContainer.reserve(mpBitmapContainer->size()); 473 474 // Copy the relevant entries. 475 CacheBitmapContainer::iterator iEntry; 476 for (iEntry=mpBitmapContainer->begin(); iEntry!=mpBitmapContainer->end(); ++iEntry) 477 { 478 if ( ! bIncludePrecious && iEntry->second.IsPrecious()) 479 continue; 480 481 if ( ! bIncludeNoPreview && ! iEntry->second.HasPreview()) 482 continue; 483 484 aSortedContainer.push_back(SortableBitmapContainer::value_type( 485 iEntry->first,iEntry->second)); 486 } 487 488 // Sort the remaining entries. 489 ::std::sort(aSortedContainer.begin(), aSortedContainer.end(), AccessTimeComparator()); 490 491 // Return a list with the keys of the sorted entries. 492 ::std::auto_ptr<CacheIndex> pIndex(new CacheIndex()); 493 SortableBitmapContainer::iterator iIndexEntry; 494 pIndex->reserve(aSortedContainer.size()); 495 for (iIndexEntry=aSortedContainer.begin(); iIndexEntry!=aSortedContainer.end(); ++iIndexEntry) 496 pIndex->push_back(iIndexEntry->first); 497 return pIndex; 498 } 499 500 501 502 503 void BitmapCache::Compress ( 504 const CacheKey& rKey, 505 const ::boost::shared_ptr<BitmapCompressor>& rpCompressor) 506 { 507 ::osl::MutexGuard aGuard (maMutex); 508 509 CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); 510 if (iEntry != mpBitmapContainer->end() && iEntry->second.HasPreview()) 511 { 512 UpdateCacheSize(iEntry->second, REMOVE); 513 iEntry->second.Compress(rpCompressor); 514 UpdateCacheSize(iEntry->second, ADD); 515 } 516 } 517 518 519 520 521 void BitmapCache::UpdateCacheSize (const CacheEntry& rEntry, CacheOperation eOperation) 522 { 523 sal_Int32 nEntrySize (rEntry.GetMemorySize()); 524 sal_Int32& rCacheSize (rEntry.IsPrecious() ? mnPreciousCacheSize : mnNormalCacheSize); 525 switch (eOperation) 526 { 527 case ADD: 528 rCacheSize += nEntrySize; 529 if ( ! rEntry.IsPrecious() && mnNormalCacheSize>mnMaximalNormalCacheSize) 530 { 531 mbIsFull = true; 532 #ifdef VERBOSE 533 OSL_TRACE("cache size is %d > %d", mnNormalCacheSize,mnMaximalNormalCacheSize); 534 #endif 535 mpCacheCompactor->RequestCompaction(); 536 } 537 break; 538 539 case REMOVE: 540 rCacheSize -= nEntrySize; 541 if (mnNormalCacheSize < mnMaximalNormalCacheSize) 542 mbIsFull = false; 543 break; 544 545 default: 546 OSL_ASSERT(false); 547 break; 548 } 549 } 550 551 552 553 554 //===== CacheEntry ============================================================ 555 556 BitmapCache::CacheEntry::CacheEntry( 557 sal_Int32 nLastAccessTime, 558 bool bIsPrecious) 559 : maPreview(), 560 maMarkedPreview(), 561 mbIsUpToDate(true), 562 mnLastAccessTime(nLastAccessTime), 563 mbIsPrecious(bIsPrecious) 564 { 565 } 566 567 568 569 570 BitmapCache::CacheEntry::CacheEntry( 571 const Bitmap& rPreview, 572 sal_Int32 nLastAccessTime, 573 bool bIsPrecious) 574 : maPreview(rPreview), 575 maMarkedPreview(), 576 mbIsUpToDate(true), 577 mnLastAccessTime(nLastAccessTime), 578 mbIsPrecious(bIsPrecious) 579 { 580 } 581 582 583 584 585 inline void BitmapCache::CacheEntry::Recycle (const CacheEntry& rEntry) 586 { 587 if ((rEntry.HasPreview() || rEntry.HasLosslessReplacement()) 588 && ! (HasPreview() || HasLosslessReplacement())) 589 { 590 maPreview = rEntry.maPreview; 591 maMarkedPreview = rEntry.maMarkedPreview; 592 mpReplacement = rEntry.mpReplacement; 593 mpCompressor = rEntry.mpCompressor; 594 mnLastAccessTime = rEntry.mnLastAccessTime; 595 mbIsUpToDate = rEntry.mbIsUpToDate; 596 } 597 } 598 599 600 601 602 inline sal_Int32 BitmapCache::CacheEntry::GetMemorySize (void) const 603 { 604 sal_Int32 nSize (0); 605 nSize += maPreview.GetSizeBytes(); 606 nSize += maMarkedPreview.GetSizeBytes(); 607 if (mpReplacement.get() != NULL) 608 nSize += mpReplacement->GetMemorySize(); 609 return nSize; 610 } 611 612 613 614 615 void BitmapCache::CacheEntry::Compress (const ::boost::shared_ptr<BitmapCompressor>& rpCompressor) 616 { 617 if ( ! maPreview.IsEmpty()) 618 { 619 if (mpReplacement.get() == NULL) 620 { 621 mpReplacement = rpCompressor->Compress(maPreview); 622 623 #ifdef VERBOSE 624 sal_uInt32 nOldSize (maPreview.GetSizeBytes()); 625 sal_uInt32 nNewSize (mpReplacement.get()!=NULL ? mpReplacement->GetMemorySize() : 0); 626 if (nOldSize == 0) 627 nOldSize = 1; 628 sal_Int32 nRatio (100L * nNewSize / nOldSize); 629 OSL_TRACE("compressing bitmap for %x from %d to %d bytes (%d%%)", 630 this, 631 nOldSize, 632 nNewSize, 633 nRatio); 634 #endif 635 636 mpCompressor = rpCompressor; 637 } 638 639 maPreview.SetEmpty(); 640 maMarkedPreview.SetEmpty(); 641 } 642 } 643 644 645 646 647 inline void BitmapCache::CacheEntry::Decompress (void) 648 { 649 if (mpReplacement.get()!=NULL && mpCompressor.get()!=NULL && maPreview.IsEmpty()) 650 { 651 maPreview = mpCompressor->Decompress(*mpReplacement); 652 maMarkedPreview.SetEmpty(); 653 if ( ! mpCompressor->IsLossless()) 654 mbIsUpToDate = false; 655 } 656 } 657 658 659 660 inline void BitmapCache::CacheEntry::SetPreview (const Bitmap& rPreview) 661 { 662 maPreview = rPreview; 663 maMarkedPreview.SetEmpty(); 664 mpReplacement.reset(); 665 mpCompressor.reset(); 666 } 667 668 669 670 671 bool BitmapCache::CacheEntry::HasPreview (void) const 672 { 673 return ! maPreview.IsEmpty(); 674 } 675 676 677 678 679 inline void BitmapCache::CacheEntry::SetMarkedPreview (const Bitmap& rMarkedPreview) 680 { 681 maMarkedPreview = rMarkedPreview; 682 } 683 684 685 686 687 bool BitmapCache::CacheEntry::HasMarkedPreview (void) const 688 { 689 return ! maMarkedPreview.IsEmpty(); 690 } 691 692 693 694 695 inline bool BitmapCache::CacheEntry::HasLosslessReplacement (void) const 696 { 697 return mpReplacement.get()!=NULL 698 && mpCompressor.get()!=NULL 699 && mpCompressor->IsLossless(); 700 } 701 702 703 } } } // end of namespace ::sd::slidesorter::cache 704