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