1 // Copyright 2016 PDFium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com 6 7 #include "core/fpdfapi/render/cpdf_pagerendercache.h" 8 9 #include <algorithm> 10 #include <vector> 11 12 #include "core/fpdfapi/page/cpdf_image.h" 13 #include "core/fpdfapi/page/cpdf_page.h" 14 #include "core/fpdfapi/render/cpdf_imagecacheentry.h" 15 #include "core/fpdfapi/render/cpdf_renderstatus.h" 16 17 namespace { 18 19 struct CacheInfo { 20 CacheInfo(uint32_t t, CPDF_Stream* stream) : time(t), pStream(stream) {} 21 22 uint32_t time; 23 CPDF_Stream* pStream; 24 25 bool operator<(const CacheInfo& other) const { return time < other.time; } 26 }; 27 28 } // namespace 29 30 CPDF_PageRenderCache::CPDF_PageRenderCache(CPDF_Page* pPage) 31 : m_pPage(pPage), 32 m_pCurImageCacheEntry(nullptr), 33 m_nTimeCount(0), 34 m_nCacheSize(0), 35 m_bCurFindCache(false) {} 36 37 CPDF_PageRenderCache::~CPDF_PageRenderCache() { 38 for (const auto& it : m_ImageCache) 39 delete it.second; 40 } 41 42 void CPDF_PageRenderCache::CacheOptimization(int32_t dwLimitCacheSize) { 43 if (m_nCacheSize <= (uint32_t)dwLimitCacheSize) 44 return; 45 46 size_t nCount = m_ImageCache.size(); 47 std::vector<CacheInfo> cache_info; 48 cache_info.reserve(nCount); 49 for (const auto& it : m_ImageCache) { 50 cache_info.emplace_back(it.second->GetTimeCount(), 51 it.second->GetImage()->GetStream()); 52 } 53 std::sort(cache_info.begin(), cache_info.end()); 54 55 // Check if time value is about to roll over and reset all entries. 56 // The comparision is legal because uint32_t is an unsigned type. 57 uint32_t nTimeCount = m_nTimeCount; 58 if (nTimeCount + 1 < nTimeCount) { 59 for (size_t i = 0; i < nCount; i++) 60 m_ImageCache[cache_info[i].pStream]->m_dwTimeCount = i; 61 m_nTimeCount = nCount; 62 } 63 64 size_t i = 0; 65 while (i + 15 < nCount) 66 ClearImageCacheEntry(cache_info[i++].pStream); 67 68 while (i < nCount && m_nCacheSize > (uint32_t)dwLimitCacheSize) 69 ClearImageCacheEntry(cache_info[i++].pStream); 70 } 71 72 void CPDF_PageRenderCache::ClearImageCacheEntry(CPDF_Stream* pStream) { 73 auto it = m_ImageCache.find(pStream); 74 if (it == m_ImageCache.end()) 75 return; 76 77 m_nCacheSize -= it->second->EstimateSize(); 78 delete it->second; 79 m_ImageCache.erase(it); 80 } 81 82 bool CPDF_PageRenderCache::StartGetCachedBitmap( 83 const RetainPtr<CPDF_Image>& pImage, 84 bool bStdCS, 85 uint32_t GroupFamily, 86 bool bLoadMask, 87 CPDF_RenderStatus* pRenderStatus) { 88 CPDF_Stream* pStream = pImage->GetStream(); 89 const auto it = m_ImageCache.find(pStream); 90 m_bCurFindCache = it != m_ImageCache.end(); 91 if (m_bCurFindCache) { 92 m_pCurImageCacheEntry = it->second; 93 } else { 94 m_pCurImageCacheEntry = 95 new CPDF_ImageCacheEntry(m_pPage->m_pDocument.Get(), pImage); 96 } 97 int ret = m_pCurImageCacheEntry->StartGetCachedBitmap( 98 pRenderStatus->GetFormResource(), m_pPage->m_pPageResources.Get(), bStdCS, 99 GroupFamily, bLoadMask, pRenderStatus); 100 if (ret == 2) 101 return true; 102 103 m_nTimeCount++; 104 if (!m_bCurFindCache) 105 m_ImageCache[pStream] = m_pCurImageCacheEntry; 106 107 if (!ret) 108 m_nCacheSize += m_pCurImageCacheEntry->EstimateSize(); 109 110 return false; 111 } 112 113 bool CPDF_PageRenderCache::Continue(IFX_PauseIndicator* pPause, 114 CPDF_RenderStatus* pRenderStatus) { 115 int ret = m_pCurImageCacheEntry->Continue(pPause, pRenderStatus); 116 if (ret == 2) 117 return true; 118 119 m_nTimeCount++; 120 if (!m_bCurFindCache) { 121 m_ImageCache[m_pCurImageCacheEntry->GetImage()->GetStream()] = 122 m_pCurImageCacheEntry; 123 } 124 if (!ret) 125 m_nCacheSize += m_pCurImageCacheEntry->EstimateSize(); 126 return false; 127 } 128 129 void CPDF_PageRenderCache::ResetBitmap(const RetainPtr<CPDF_Image>& pImage, 130 const RetainPtr<CFX_DIBitmap>& pBitmap) { 131 CPDF_ImageCacheEntry* pEntry; 132 CPDF_Stream* pStream = pImage->GetStream(); 133 const auto it = m_ImageCache.find(pStream); 134 if (it == m_ImageCache.end()) { 135 if (!pBitmap) 136 return; 137 138 pEntry = new CPDF_ImageCacheEntry(m_pPage->m_pDocument.Get(), pImage); 139 m_ImageCache[pStream] = pEntry; 140 } else { 141 pEntry = it->second; 142 } 143 m_nCacheSize -= pEntry->EstimateSize(); 144 pEntry->Reset(pBitmap); 145 m_nCacheSize += pEntry->EstimateSize(); 146 } 147