1 // Copyright 2014 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 "render_int.h"
8
9 #include "core/include/fpdfapi/fpdf_pageobj.h"
10 #include "core/include/fpdfapi/fpdf_render.h"
11 #include "core/include/fxge/fx_ge.h"
12 #include "core/src/fpdfapi/fpdf_page/pageint.h"
13
14 struct CACHEINFO {
15 FX_DWORD time;
16 CPDF_Stream* pStream;
17 };
18
19 extern "C" {
compare(const void * data1,const void * data2)20 static int compare(const void* data1, const void* data2) {
21 return ((CACHEINFO*)data1)->time - ((CACHEINFO*)data2)->time;
22 }
23 } // extern "C"
24
~CPDF_PageRenderCache()25 CPDF_PageRenderCache::~CPDF_PageRenderCache() {
26 for (const auto& it : m_ImageCache)
27 delete it.second;
28 }
CacheOptimization(int32_t dwLimitCacheSize)29 void CPDF_PageRenderCache::CacheOptimization(int32_t dwLimitCacheSize) {
30 if (m_nCacheSize <= (FX_DWORD)dwLimitCacheSize)
31 return;
32
33 size_t nCount = m_ImageCache.size();
34 CACHEINFO* pCACHEINFO = FX_Alloc(CACHEINFO, nCount);
35 size_t i = 0;
36 for (const auto& it : m_ImageCache) {
37 pCACHEINFO[i].time = it.second->GetTimeCount();
38 pCACHEINFO[i++].pStream = it.second->GetStream();
39 }
40 FXSYS_qsort(pCACHEINFO, nCount, sizeof(CACHEINFO), compare);
41 FX_DWORD nTimeCount = m_nTimeCount;
42
43 // Check if time value is about to roll over and reset all entries.
44 // The comparision is legal because FX_DWORD is an unsigned type.
45 if (nTimeCount + 1 < nTimeCount) {
46 for (i = 0; i < nCount; i++)
47 m_ImageCache[pCACHEINFO[i].pStream]->m_dwTimeCount = i;
48 m_nTimeCount = nCount;
49 }
50
51 i = 0;
52 while (i + 15 < nCount)
53 ClearImageCacheEntry(pCACHEINFO[i++].pStream);
54
55 while (i < nCount && m_nCacheSize > (FX_DWORD)dwLimitCacheSize)
56 ClearImageCacheEntry(pCACHEINFO[i++].pStream);
57
58 FX_Free(pCACHEINFO);
59 }
ClearImageCacheEntry(CPDF_Stream * pStream)60 void CPDF_PageRenderCache::ClearImageCacheEntry(CPDF_Stream* pStream) {
61 auto it = m_ImageCache.find(pStream);
62 if (it == m_ImageCache.end())
63 return;
64
65 m_nCacheSize -= it->second->EstimateSize();
66 delete it->second;
67 m_ImageCache.erase(it);
68 }
EstimateSize()69 FX_DWORD CPDF_PageRenderCache::EstimateSize() {
70 FX_DWORD dwSize = 0;
71 for (const auto& it : m_ImageCache)
72 dwSize += it.second->EstimateSize();
73
74 m_nCacheSize = dwSize;
75 return dwSize;
76 }
GetCachedBitmap(CPDF_Stream * pStream,CFX_DIBSource * & pBitmap,CFX_DIBSource * & pMask,FX_DWORD & MatteColor,FX_BOOL bStdCS,FX_DWORD GroupFamily,FX_BOOL bLoadMask,CPDF_RenderStatus * pRenderStatus,int32_t downsampleWidth,int32_t downsampleHeight)77 void CPDF_PageRenderCache::GetCachedBitmap(CPDF_Stream* pStream,
78 CFX_DIBSource*& pBitmap,
79 CFX_DIBSource*& pMask,
80 FX_DWORD& MatteColor,
81 FX_BOOL bStdCS,
82 FX_DWORD GroupFamily,
83 FX_BOOL bLoadMask,
84 CPDF_RenderStatus* pRenderStatus,
85 int32_t downsampleWidth,
86 int32_t downsampleHeight) {
87 CPDF_ImageCacheEntry* pEntry;
88 const auto it = m_ImageCache.find(pStream);
89 FX_BOOL bFound = it != m_ImageCache.end();
90 if (bFound)
91 pEntry = it->second;
92 else
93 pEntry = new CPDF_ImageCacheEntry(m_pPage->m_pDocument, pStream);
94
95 m_nTimeCount++;
96 FX_BOOL bAlreadyCached = pEntry->GetCachedBitmap(
97 pBitmap, pMask, MatteColor, m_pPage->m_pPageResources, bStdCS,
98 GroupFamily, bLoadMask, pRenderStatus, downsampleWidth, downsampleHeight);
99
100 if (!bFound)
101 m_ImageCache[pStream] = pEntry;
102
103 if (!bAlreadyCached)
104 m_nCacheSize += pEntry->EstimateSize();
105 }
StartGetCachedBitmap(CPDF_Stream * pStream,FX_BOOL bStdCS,FX_DWORD GroupFamily,FX_BOOL bLoadMask,CPDF_RenderStatus * pRenderStatus,int32_t downsampleWidth,int32_t downsampleHeight)106 FX_BOOL CPDF_PageRenderCache::StartGetCachedBitmap(
107 CPDF_Stream* pStream,
108 FX_BOOL bStdCS,
109 FX_DWORD GroupFamily,
110 FX_BOOL bLoadMask,
111 CPDF_RenderStatus* pRenderStatus,
112 int32_t downsampleWidth,
113 int32_t downsampleHeight) {
114 const auto it = m_ImageCache.find(pStream);
115 m_bCurFindCache = it != m_ImageCache.end();
116 if (m_bCurFindCache) {
117 m_pCurImageCacheEntry = it->second;
118 } else {
119 m_pCurImageCacheEntry =
120 new CPDF_ImageCacheEntry(m_pPage->m_pDocument, pStream);
121 }
122 int ret = m_pCurImageCacheEntry->StartGetCachedBitmap(
123 pRenderStatus->m_pFormResource, m_pPage->m_pPageResources, bStdCS,
124 GroupFamily, bLoadMask, pRenderStatus, downsampleWidth, downsampleHeight);
125 if (ret == 2)
126 return TRUE;
127
128 m_nTimeCount++;
129 if (!m_bCurFindCache)
130 m_ImageCache[pStream] = m_pCurImageCacheEntry;
131
132 if (!ret)
133 m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
134
135 return FALSE;
136 }
Continue(IFX_Pause * pPause)137 FX_BOOL CPDF_PageRenderCache::Continue(IFX_Pause* pPause) {
138 int ret = m_pCurImageCacheEntry->Continue(pPause);
139 if (ret == 2)
140 return TRUE;
141 m_nTimeCount++;
142 if (!m_bCurFindCache)
143 m_ImageCache[m_pCurImageCacheEntry->GetStream()] = m_pCurImageCacheEntry;
144 if (!ret)
145 m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
146 return FALSE;
147 }
ResetBitmap(CPDF_Stream * pStream,const CFX_DIBitmap * pBitmap)148 void CPDF_PageRenderCache::ResetBitmap(CPDF_Stream* pStream,
149 const CFX_DIBitmap* pBitmap) {
150 CPDF_ImageCacheEntry* pEntry;
151 const auto it = m_ImageCache.find(pStream);
152 if (it == m_ImageCache.end()) {
153 if (!pBitmap)
154 return;
155 pEntry = new CPDF_ImageCacheEntry(m_pPage->m_pDocument, pStream);
156 m_ImageCache[pStream] = pEntry;
157 } else {
158 pEntry = it->second;
159 }
160 m_nCacheSize -= pEntry->EstimateSize();
161 pEntry->Reset(pBitmap);
162 m_nCacheSize += pEntry->EstimateSize();
163 }
CPDF_ImageCacheEntry(CPDF_Document * pDoc,CPDF_Stream * pStream)164 CPDF_ImageCacheEntry::CPDF_ImageCacheEntry(CPDF_Document* pDoc,
165 CPDF_Stream* pStream)
166 : m_dwTimeCount(0),
167 m_pCurBitmap(NULL),
168 m_pCurMask(NULL),
169 m_MatteColor(0),
170 m_pRenderStatus(NULL),
171 m_pDocument(pDoc),
172 m_pStream(pStream),
173 m_pCachedBitmap(NULL),
174 m_pCachedMask(NULL),
175 m_dwCacheSize(0) {}
~CPDF_ImageCacheEntry()176 CPDF_ImageCacheEntry::~CPDF_ImageCacheEntry() {
177 delete m_pCachedBitmap;
178 delete m_pCachedMask;
179 }
Reset(const CFX_DIBitmap * pBitmap)180 void CPDF_ImageCacheEntry::Reset(const CFX_DIBitmap* pBitmap) {
181 delete m_pCachedBitmap;
182 m_pCachedBitmap = NULL;
183 if (pBitmap) {
184 m_pCachedBitmap = pBitmap->Clone();
185 }
186 CalcSize();
187 }
ClearImageData()188 void CPDF_PageRenderCache::ClearImageData() {
189 for (const auto& it : m_ImageCache)
190 it.second->ClearImageData();
191 }
ClearImageData()192 void CPDF_ImageCacheEntry::ClearImageData() {
193 if (m_pCachedBitmap && !m_pCachedBitmap->GetBuffer()) {
194 ((CPDF_DIBSource*)m_pCachedBitmap)->ClearImageData();
195 }
196 }
FPDF_ImageCache_EstimateImageSize(const CFX_DIBSource * pDIB)197 static FX_DWORD FPDF_ImageCache_EstimateImageSize(const CFX_DIBSource* pDIB) {
198 return pDIB && pDIB->GetBuffer()
199 ? (FX_DWORD)pDIB->GetHeight() * pDIB->GetPitch() +
200 (FX_DWORD)pDIB->GetPaletteSize() * 4
201 : 0;
202 }
GetCachedBitmap(CFX_DIBSource * & pBitmap,CFX_DIBSource * & pMask,FX_DWORD & MatteColor,CPDF_Dictionary * pPageResources,FX_BOOL bStdCS,FX_DWORD GroupFamily,FX_BOOL bLoadMask,CPDF_RenderStatus * pRenderStatus,int32_t downsampleWidth,int32_t downsampleHeight)203 FX_BOOL CPDF_ImageCacheEntry::GetCachedBitmap(CFX_DIBSource*& pBitmap,
204 CFX_DIBSource*& pMask,
205 FX_DWORD& MatteColor,
206 CPDF_Dictionary* pPageResources,
207 FX_BOOL bStdCS,
208 FX_DWORD GroupFamily,
209 FX_BOOL bLoadMask,
210 CPDF_RenderStatus* pRenderStatus,
211 int32_t downsampleWidth,
212 int32_t downsampleHeight) {
213 if (m_pCachedBitmap) {
214 pBitmap = m_pCachedBitmap;
215 pMask = m_pCachedMask;
216 MatteColor = m_MatteColor;
217 return TRUE;
218 }
219 if (!pRenderStatus) {
220 return FALSE;
221 }
222 CPDF_RenderContext* pContext = pRenderStatus->GetContext();
223 CPDF_PageRenderCache* pPageRenderCache = pContext->GetPageCache();
224 m_dwTimeCount = pPageRenderCache->GetTimeCount();
225 CPDF_DIBSource* pSrc = new CPDF_DIBSource;
226 CPDF_DIBSource* pMaskSrc = NULL;
227 if (!pSrc->Load(m_pDocument, m_pStream, &pMaskSrc, &MatteColor,
228 pRenderStatus->m_pFormResource, pPageResources, bStdCS,
229 GroupFamily, bLoadMask)) {
230 delete pSrc;
231 pBitmap = NULL;
232 return FALSE;
233 }
234 m_MatteColor = MatteColor;
235 if (pSrc->GetPitch() * pSrc->GetHeight() < FPDF_HUGE_IMAGE_SIZE) {
236 m_pCachedBitmap = pSrc->Clone();
237 delete pSrc;
238 } else {
239 m_pCachedBitmap = pSrc;
240 }
241 if (pMaskSrc) {
242 m_pCachedMask = pMaskSrc->Clone();
243 delete pMaskSrc;
244 }
245
246 pBitmap = m_pCachedBitmap;
247 pMask = m_pCachedMask;
248 CalcSize();
249 return FALSE;
250 }
DetachBitmap()251 CFX_DIBSource* CPDF_ImageCacheEntry::DetachBitmap() {
252 CFX_DIBSource* pDIBSource = m_pCurBitmap;
253 m_pCurBitmap = NULL;
254 return pDIBSource;
255 }
DetachMask()256 CFX_DIBSource* CPDF_ImageCacheEntry::DetachMask() {
257 CFX_DIBSource* pDIBSource = m_pCurMask;
258 m_pCurMask = NULL;
259 return pDIBSource;
260 }
StartGetCachedBitmap(CPDF_Dictionary * pFormResources,CPDF_Dictionary * pPageResources,FX_BOOL bStdCS,FX_DWORD GroupFamily,FX_BOOL bLoadMask,CPDF_RenderStatus * pRenderStatus,int32_t downsampleWidth,int32_t downsampleHeight)261 int CPDF_ImageCacheEntry::StartGetCachedBitmap(CPDF_Dictionary* pFormResources,
262 CPDF_Dictionary* pPageResources,
263 FX_BOOL bStdCS,
264 FX_DWORD GroupFamily,
265 FX_BOOL bLoadMask,
266 CPDF_RenderStatus* pRenderStatus,
267 int32_t downsampleWidth,
268 int32_t downsampleHeight) {
269 if (m_pCachedBitmap) {
270 m_pCurBitmap = m_pCachedBitmap;
271 m_pCurMask = m_pCachedMask;
272 return 1;
273 }
274 if (!pRenderStatus) {
275 return 0;
276 }
277 m_pRenderStatus = pRenderStatus;
278 m_pCurBitmap = new CPDF_DIBSource;
279 int ret =
280 ((CPDF_DIBSource*)m_pCurBitmap)
281 ->StartLoadDIBSource(m_pDocument, m_pStream, TRUE, pFormResources,
282 pPageResources, bStdCS, GroupFamily, bLoadMask);
283 if (ret == 2) {
284 return ret;
285 }
286 if (!ret) {
287 delete m_pCurBitmap;
288 m_pCurBitmap = NULL;
289 return 0;
290 }
291 ContinueGetCachedBitmap();
292 return 0;
293 }
ContinueGetCachedBitmap()294 void CPDF_ImageCacheEntry::ContinueGetCachedBitmap() {
295 m_MatteColor = ((CPDF_DIBSource*)m_pCurBitmap)->m_MatteColor;
296 m_pCurMask = ((CPDF_DIBSource*)m_pCurBitmap)->DetachMask();
297 CPDF_RenderContext* pContext = m_pRenderStatus->GetContext();
298 CPDF_PageRenderCache* pPageRenderCache = pContext->GetPageCache();
299 m_dwTimeCount = pPageRenderCache->GetTimeCount();
300 if (m_pCurBitmap->GetPitch() * m_pCurBitmap->GetHeight() <
301 FPDF_HUGE_IMAGE_SIZE) {
302 m_pCachedBitmap = m_pCurBitmap->Clone();
303 delete m_pCurBitmap;
304 m_pCurBitmap = NULL;
305 } else {
306 m_pCachedBitmap = m_pCurBitmap;
307 }
308 if (m_pCurMask) {
309 m_pCachedMask = m_pCurMask->Clone();
310 delete m_pCurMask;
311 m_pCurMask = NULL;
312 }
313 m_pCurBitmap = m_pCachedBitmap;
314 m_pCurMask = m_pCachedMask;
315 CalcSize();
316 }
Continue(IFX_Pause * pPause)317 int CPDF_ImageCacheEntry::Continue(IFX_Pause* pPause) {
318 int ret = ((CPDF_DIBSource*)m_pCurBitmap)->ContinueLoadDIBSource(pPause);
319 if (ret == 2) {
320 return ret;
321 }
322 if (!ret) {
323 delete m_pCurBitmap;
324 m_pCurBitmap = NULL;
325 return 0;
326 }
327 ContinueGetCachedBitmap();
328 return 0;
329 }
CalcSize()330 void CPDF_ImageCacheEntry::CalcSize() {
331 m_dwCacheSize = FPDF_ImageCache_EstimateImageSize(m_pCachedBitmap) +
332 FPDF_ImageCache_EstimateImageSize(m_pCachedMask);
333 }
ClearRenderFont()334 void CPDF_Document::ClearRenderFont() {
335 if (m_pDocRender) {
336 CFX_FontCache* pCache = m_pDocRender->GetFontCache();
337 if (pCache) {
338 pCache->FreeCache(FALSE);
339 }
340 }
341 }
342