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 "core/fpdfapi/page/cpdf_page.h"
10 #include "core/fpdfapi/render/cpdf_imagecacheentry.h"
11 #include "core/fpdfapi/render/cpdf_renderstatus.h"
12
13 namespace {
14
15 struct CACHEINFO {
16 uint32_t time;
17 CPDF_Stream* pStream;
18 };
19
20 extern "C" {
compare(const void * data1,const void * data2)21 static int compare(const void* data1, const void* data2) {
22 return ((CACHEINFO*)data1)->time - ((CACHEINFO*)data2)->time;
23 }
24 } // extern "C"
25
26 } // namespace
27
CPDF_PageRenderCache(CPDF_Page * pPage)28 CPDF_PageRenderCache::CPDF_PageRenderCache(CPDF_Page* pPage)
29 : m_pPage(pPage),
30 m_pCurImageCacheEntry(nullptr),
31 m_nTimeCount(0),
32 m_nCacheSize(0),
33 m_bCurFindCache(false) {}
34
~CPDF_PageRenderCache()35 CPDF_PageRenderCache::~CPDF_PageRenderCache() {
36 for (const auto& it : m_ImageCache)
37 delete it.second;
38 }
39
CacheOptimization(int32_t dwLimitCacheSize)40 void CPDF_PageRenderCache::CacheOptimization(int32_t dwLimitCacheSize) {
41 if (m_nCacheSize <= (uint32_t)dwLimitCacheSize)
42 return;
43
44 size_t nCount = m_ImageCache.size();
45 CACHEINFO* pCACHEINFO = FX_Alloc(CACHEINFO, nCount);
46 size_t i = 0;
47 for (const auto& it : m_ImageCache) {
48 pCACHEINFO[i].time = it.second->GetTimeCount();
49 pCACHEINFO[i++].pStream = it.second->GetStream();
50 }
51 FXSYS_qsort(pCACHEINFO, nCount, sizeof(CACHEINFO), compare);
52 uint32_t nTimeCount = m_nTimeCount;
53
54 // Check if time value is about to roll over and reset all entries.
55 // The comparision is legal because uint32_t is an unsigned type.
56 if (nTimeCount + 1 < nTimeCount) {
57 for (i = 0; i < nCount; i++)
58 m_ImageCache[pCACHEINFO[i].pStream]->m_dwTimeCount = i;
59 m_nTimeCount = nCount;
60 }
61
62 i = 0;
63 while (i + 15 < nCount)
64 ClearImageCacheEntry(pCACHEINFO[i++].pStream);
65
66 while (i < nCount && m_nCacheSize > (uint32_t)dwLimitCacheSize)
67 ClearImageCacheEntry(pCACHEINFO[i++].pStream);
68
69 FX_Free(pCACHEINFO);
70 }
71
ClearImageCacheEntry(CPDF_Stream * pStream)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
StartGetCachedBitmap(CPDF_Stream * pStream,bool bStdCS,uint32_t GroupFamily,bool bLoadMask,CPDF_RenderStatus * pRenderStatus,int32_t downsampleWidth,int32_t downsampleHeight)82 bool CPDF_PageRenderCache::StartGetCachedBitmap(
83 CPDF_Stream* pStream,
84 bool bStdCS,
85 uint32_t GroupFamily,
86 bool bLoadMask,
87 CPDF_RenderStatus* pRenderStatus,
88 int32_t downsampleWidth,
89 int32_t downsampleHeight) {
90 const auto it = m_ImageCache.find(pStream);
91 m_bCurFindCache = it != m_ImageCache.end();
92 if (m_bCurFindCache) {
93 m_pCurImageCacheEntry = it->second;
94 } else {
95 m_pCurImageCacheEntry =
96 new CPDF_ImageCacheEntry(m_pPage->m_pDocument, pStream);
97 }
98 int ret = m_pCurImageCacheEntry->StartGetCachedBitmap(
99 pRenderStatus->m_pFormResource, m_pPage->m_pPageResources, bStdCS,
100 GroupFamily, bLoadMask, pRenderStatus, downsampleWidth, downsampleHeight);
101 if (ret == 2)
102 return true;
103
104 m_nTimeCount++;
105 if (!m_bCurFindCache)
106 m_ImageCache[pStream] = m_pCurImageCacheEntry;
107
108 if (!ret)
109 m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
110
111 return false;
112 }
113
Continue(IFX_Pause * pPause)114 bool CPDF_PageRenderCache::Continue(IFX_Pause* pPause) {
115 int ret = m_pCurImageCacheEntry->Continue(pPause);
116 if (ret == 2)
117 return true;
118
119 m_nTimeCount++;
120 if (!m_bCurFindCache)
121 m_ImageCache[m_pCurImageCacheEntry->GetStream()] = m_pCurImageCacheEntry;
122 if (!ret)
123 m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
124 return false;
125 }
126
ResetBitmap(CPDF_Stream * pStream,const CFX_DIBitmap * pBitmap)127 void CPDF_PageRenderCache::ResetBitmap(CPDF_Stream* pStream,
128 const CFX_DIBitmap* pBitmap) {
129 CPDF_ImageCacheEntry* pEntry;
130 const auto it = m_ImageCache.find(pStream);
131 if (it == m_ImageCache.end()) {
132 if (!pBitmap)
133 return;
134
135 pEntry = new CPDF_ImageCacheEntry(m_pPage->m_pDocument, pStream);
136 m_ImageCache[pStream] = pEntry;
137 } else {
138 pEntry = it->second;
139 }
140 m_nCacheSize -= pEntry->EstimateSize();
141 pEntry->Reset(pBitmap);
142 m_nCacheSize += pEntry->EstimateSize();
143 }
144