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 "../../../include/fpdfapi/fpdf_render.h"
8 #include "../../../include/fpdfapi/fpdf_pageobj.h"
9 #include "../../../include/fxge/fx_ge.h"
10 #include "../fpdf_page/pageint.h"
11 #include "render_int.h"
12 struct CACHEINFO {
13 FX_DWORD time;
14 CPDF_Stream* pStream;
15 };
16 extern "C" {
compare(const void * data1,const void * data2)17 static int compare(const void* data1, const void* data2)
18 {
19 return ((CACHEINFO*)data1)->time - ((CACHEINFO*)data2)->time;
20 }
21 };
ClearRenderCache()22 void CPDF_Page::ClearRenderCache()
23 {
24 if (m_pPageRender) {
25 m_pPageRender->ClearAll();
26 }
27 }
ClearAll()28 void CPDF_PageRenderCache::ClearAll()
29 {
30 FX_POSITION pos = m_ImageCaches.GetStartPosition();
31 while (pos) {
32 FX_LPVOID key, value;
33 m_ImageCaches.GetNextAssoc(pos, key, value);
34 delete (CPDF_ImageCache*)value;
35 }
36 m_ImageCaches.RemoveAll();
37 m_nCacheSize = 0;
38 m_nTimeCount = 0;
39 }
CacheOptimization(FX_INT32 dwLimitCacheSize)40 void CPDF_PageRenderCache::CacheOptimization(FX_INT32 dwLimitCacheSize)
41 {
42 if (m_nCacheSize <= (FX_DWORD)dwLimitCacheSize) {
43 return;
44 }
45 int nCount = m_ImageCaches.GetCount();
46 CACHEINFO* pCACHEINFO = (CACHEINFO*)FX_Alloc(FX_BYTE, (sizeof (CACHEINFO)) * nCount);
47 FX_POSITION pos = m_ImageCaches.GetStartPosition();
48 int i = 0;
49 while (pos) {
50 FX_LPVOID key, value;
51 m_ImageCaches.GetNextAssoc(pos, key, value);
52 pCACHEINFO[i].time = ((CPDF_ImageCache*)value)->GetTimeCount();
53 pCACHEINFO[i++].pStream = ((CPDF_ImageCache*)value)->GetStream();
54 }
55 FXSYS_qsort(pCACHEINFO, nCount, sizeof (CACHEINFO), compare);
56 FX_DWORD nTimeCount = m_nTimeCount;
57 if (nTimeCount + 1 < nTimeCount) {
58 for (i = 0; i < nCount; i ++) {
59 ((CPDF_ImageCache*)(m_ImageCaches[pCACHEINFO[i].pStream]))->m_dwTimeCount = i;
60 }
61 m_nTimeCount = nCount;
62 }
63 i = 0;
64 while(nCount > 15) {
65 ClearImageCache(pCACHEINFO[i++].pStream);
66 nCount--;
67 }
68 while (m_nCacheSize > (FX_DWORD)dwLimitCacheSize) {
69 ClearImageCache(pCACHEINFO[i++].pStream);
70 }
71 FX_Free(pCACHEINFO);
72 }
ClearImageCache(CPDF_Stream * pStream)73 void CPDF_PageRenderCache::ClearImageCache(CPDF_Stream* pStream)
74 {
75 FX_LPVOID value = m_ImageCaches.GetValueAt(pStream);
76 if (value == NULL) {
77 m_ImageCaches.RemoveKey(pStream);
78 return;
79 }
80 m_nCacheSize -= ((CPDF_ImageCache*)value)->EstimateSize();
81 delete (CPDF_ImageCache*)value;
82 m_ImageCaches.RemoveKey(pStream);
83 }
EstimateSize()84 FX_DWORD CPDF_PageRenderCache::EstimateSize()
85 {
86 FX_DWORD dwSize = 0;
87 FX_POSITION pos = m_ImageCaches.GetStartPosition();
88 while (pos) {
89 FX_LPVOID key, value;
90 m_ImageCaches.GetNextAssoc(pos, key, value);
91 dwSize += ((CPDF_ImageCache*)value)->EstimateSize();
92 }
93 m_nCacheSize = dwSize;
94 return dwSize;
95 }
GetCachedSize(CPDF_Stream * pStream) const96 FX_DWORD CPDF_PageRenderCache::GetCachedSize(CPDF_Stream* pStream) const
97 {
98 if (pStream == NULL) {
99 return m_nCacheSize;
100 }
101 CPDF_ImageCache* pImageCache;
102 if (!m_ImageCaches.Lookup(pStream, (FX_LPVOID&)pImageCache)) {
103 return 0;
104 }
105 return pImageCache->EstimateSize();
106 }
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,FX_INT32 downsampleWidth,FX_INT32 downsampleHeight)107 void CPDF_PageRenderCache::GetCachedBitmap(CPDF_Stream* pStream, CFX_DIBSource*& pBitmap, CFX_DIBSource*& pMask, FX_DWORD& MatteColor,
108 FX_BOOL bStdCS, FX_DWORD GroupFamily, FX_BOOL bLoadMask, CPDF_RenderStatus* pRenderStatus,
109 FX_INT32 downsampleWidth, FX_INT32 downsampleHeight)
110 {
111 CPDF_ImageCache* pImageCache;
112 FX_BOOL bFind = m_ImageCaches.Lookup(pStream, (FX_LPVOID&)pImageCache);
113 if (!bFind) {
114 pImageCache = FX_NEW CPDF_ImageCache(m_pPage->m_pDocument, pStream);
115 }
116 m_nTimeCount ++;
117 FX_BOOL bCached = pImageCache->GetCachedBitmap(pBitmap, pMask, MatteColor, m_pPage->m_pPageResources, bStdCS, GroupFamily, bLoadMask, pRenderStatus, downsampleWidth, downsampleHeight);
118 if (!bFind) {
119 m_ImageCaches.SetAt(pStream, pImageCache);
120 }
121 if (!bCached) {
122 m_nCacheSize += pImageCache->EstimateSize();
123 }
124 }
StartGetCachedBitmap(CPDF_Stream * pStream,FX_BOOL bStdCS,FX_DWORD GroupFamily,FX_BOOL bLoadMask,CPDF_RenderStatus * pRenderStatus,FX_INT32 downsampleWidth,FX_INT32 downsampleHeight)125 FX_BOOL CPDF_PageRenderCache::StartGetCachedBitmap(CPDF_Stream* pStream, FX_BOOL bStdCS, FX_DWORD GroupFamily, FX_BOOL bLoadMask, CPDF_RenderStatus* pRenderStatus, FX_INT32 downsampleWidth, FX_INT32 downsampleHeight)
126 {
127 m_bCurFindCache = m_ImageCaches.Lookup(pStream, (FX_LPVOID&)m_pCurImageCache);
128 if (!m_bCurFindCache) {
129 m_pCurImageCache = FX_NEW CPDF_ImageCache(m_pPage->m_pDocument, pStream);
130 }
131 int ret = m_pCurImageCache->StartGetCachedBitmap(pRenderStatus->m_pFormResource, m_pPage->m_pPageResources, bStdCS, GroupFamily, bLoadMask, pRenderStatus, downsampleWidth, downsampleHeight);
132 if (ret == 2) {
133 return TRUE;
134 }
135 m_nTimeCount ++;
136 if (!m_bCurFindCache) {
137 m_ImageCaches.SetAt(pStream, m_pCurImageCache);
138 }
139 if (!ret) {
140 m_nCacheSize += m_pCurImageCache->EstimateSize();
141 }
142 return FALSE;
143 }
Continue(IFX_Pause * pPause)144 FX_BOOL CPDF_PageRenderCache::Continue(IFX_Pause* pPause)
145 {
146 int ret = m_pCurImageCache->Continue(pPause);
147 if (ret == 2) {
148 return TRUE;
149 }
150 m_nTimeCount ++;
151 if (!m_bCurFindCache) {
152 m_ImageCaches.SetAt(m_pCurImageCache->GetStream(), m_pCurImageCache);
153 }
154 if (!ret) {
155 m_nCacheSize += m_pCurImageCache->EstimateSize();
156 }
157 return FALSE;
158 }
ResetBitmap(CPDF_Stream * pStream,const CFX_DIBitmap * pBitmap)159 void CPDF_PageRenderCache::ResetBitmap(CPDF_Stream* pStream, const CFX_DIBitmap* pBitmap)
160 {
161 CPDF_ImageCache* pImageCache;
162 if (!m_ImageCaches.Lookup(pStream, (FX_LPVOID&)pImageCache)) {
163 if (pBitmap == NULL) {
164 return;
165 }
166 pImageCache = FX_NEW CPDF_ImageCache(m_pPage->m_pDocument, pStream);
167 m_ImageCaches.SetAt(pStream, pImageCache);
168 }
169 int oldsize = pImageCache->EstimateSize();
170 pImageCache->Reset(pBitmap);
171 m_nCacheSize = pImageCache->EstimateSize() - oldsize;
172 }
CPDF_ImageCache(CPDF_Document * pDoc,CPDF_Stream * pStream)173 CPDF_ImageCache::CPDF_ImageCache(CPDF_Document* pDoc, CPDF_Stream* pStream)
174 : m_pDocument(pDoc)
175 , m_pStream(pStream)
176 , m_pCachedBitmap(NULL)
177 , m_pCachedMask(NULL)
178 , m_dwCacheSize(0)
179 , m_dwTimeCount(0)
180 , m_pCurBitmap(NULL)
181 , m_pCurMask(NULL)
182 , m_MatteColor(0)
183 , m_pRenderStatus(NULL)
184 {
185 }
~CPDF_ImageCache()186 CPDF_ImageCache::~CPDF_ImageCache()
187 {
188 if (m_pCachedBitmap) {
189 delete m_pCachedBitmap;
190 m_pCachedBitmap = NULL;
191 }
192 if (m_pCachedMask) {
193 delete m_pCachedMask;
194 m_pCachedMask = NULL;
195 }
196 }
Reset(const CFX_DIBitmap * pBitmap)197 void CPDF_ImageCache::Reset(const CFX_DIBitmap* pBitmap)
198 {
199 if (m_pCachedBitmap) {
200 delete m_pCachedBitmap;
201 }
202 m_pCachedBitmap = NULL;
203 if (pBitmap) {
204 m_pCachedBitmap = pBitmap->Clone();
205 }
206 CalcSize();
207 }
ClearImageData()208 void CPDF_PageRenderCache::ClearImageData()
209 {
210 FX_POSITION pos = m_ImageCaches.GetStartPosition();
211 while (pos) {
212 FX_LPVOID key, value;
213 m_ImageCaches.GetNextAssoc(pos, key, value);
214 ((CPDF_ImageCache*)value)->ClearImageData();
215 }
216 }
ClearImageData()217 void CPDF_ImageCache::ClearImageData()
218 {
219 if (m_pCachedBitmap && m_pCachedBitmap->GetBuffer() == NULL) {
220 ((CPDF_DIBSource*)m_pCachedBitmap)->ClearImageData();
221 }
222 }
FPDF_ImageCache_EstimateImageSize(const CFX_DIBSource * pDIB)223 static FX_DWORD FPDF_ImageCache_EstimateImageSize(const CFX_DIBSource* pDIB)
224 {
225 return pDIB && pDIB->GetBuffer() ? (FX_DWORD)pDIB->GetHeight() * pDIB->GetPitch() + (FX_DWORD)pDIB->GetPaletteSize() * 4 : 0;
226 }
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,FX_INT32 downsampleWidth,FX_INT32 downsampleHeight)227 FX_BOOL CPDF_ImageCache::GetCachedBitmap(CFX_DIBSource*& pBitmap, CFX_DIBSource*& pMask, FX_DWORD& MatteColor, CPDF_Dictionary* pPageResources,
228 FX_BOOL bStdCS, FX_DWORD GroupFamily, FX_BOOL bLoadMask, CPDF_RenderStatus* pRenderStatus,
229 FX_INT32 downsampleWidth, FX_INT32 downsampleHeight)
230 {
231 if (m_pCachedBitmap) {
232 pBitmap = m_pCachedBitmap;
233 pMask = m_pCachedMask;
234 MatteColor = m_MatteColor;
235 return TRUE;
236 }
237 if (!pRenderStatus) {
238 return FALSE;
239 }
240 CPDF_RenderContext*pContext = pRenderStatus->GetContext();
241 CPDF_PageRenderCache* pPageRenderCache = pContext->m_pPageCache;
242 m_dwTimeCount = pPageRenderCache->GetTimeCount();
243 CPDF_DIBSource* pSrc = FX_NEW CPDF_DIBSource;
244 CPDF_DIBSource* pMaskSrc = NULL;
245 if (!pSrc->Load(m_pDocument, m_pStream, &pMaskSrc, &MatteColor, pRenderStatus->m_pFormResource, pPageResources, bStdCS, GroupFamily, bLoadMask)) {
246 delete pSrc;
247 pBitmap = NULL;
248 return FALSE;
249 }
250 m_MatteColor = MatteColor;
251 #if !defined(_FPDFAPI_MINI_)
252 if (pSrc->GetPitch() * pSrc->GetHeight() < FPDF_HUGE_IMAGE_SIZE) {
253 m_pCachedBitmap = pSrc->Clone();
254 delete pSrc;
255 } else {
256 m_pCachedBitmap = pSrc;
257 }
258 if (pMaskSrc) {
259 m_pCachedMask = pMaskSrc->Clone();
260 delete pMaskSrc;
261 }
262 #else
263 if (pSrc->GetFormat() == FXDIB_8bppRgb && pSrc->GetPalette() &&
264 pSrc->GetHeight() * pSrc->GetWidth() * 3 < 1024) {
265 #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
266 m_pCachedBitmap = pSrc->CloneConvert(FXDIB_Rgb32);
267 #else
268 m_pCachedBitmap = pSrc->CloneConvert(FXDIB_Rgb);
269 #endif
270 delete pSrc;
271 } else if (pSrc->GetPitch() * pSrc->GetHeight() < 102400) {
272 m_pCachedBitmap = pSrc->Clone();
273 delete pSrc;
274 } else {
275 m_pCachedBitmap = pSrc;
276 }
277 m_pCachedMask = pMaskSrc;
278 #endif
279 pBitmap = m_pCachedBitmap;
280 pMask = m_pCachedMask;
281 CalcSize();
282 return FALSE;
283 }
DetachBitmap()284 CFX_DIBSource* CPDF_ImageCache::DetachBitmap()
285 {
286 CFX_DIBSource* pDIBSource = m_pCurBitmap;
287 m_pCurBitmap = NULL;
288 return pDIBSource;
289 }
DetachMask()290 CFX_DIBSource* CPDF_ImageCache::DetachMask()
291 {
292 CFX_DIBSource* pDIBSource = m_pCurMask;
293 m_pCurMask = NULL;
294 return pDIBSource;
295 }
StartGetCachedBitmap(CPDF_Dictionary * pFormResources,CPDF_Dictionary * pPageResources,FX_BOOL bStdCS,FX_DWORD GroupFamily,FX_BOOL bLoadMask,CPDF_RenderStatus * pRenderStatus,FX_INT32 downsampleWidth,FX_INT32 downsampleHeight)296 int CPDF_ImageCache::StartGetCachedBitmap(CPDF_Dictionary* pFormResources, CPDF_Dictionary* pPageResources, FX_BOOL bStdCS,
297 FX_DWORD GroupFamily, FX_BOOL bLoadMask, CPDF_RenderStatus* pRenderStatus,
298 FX_INT32 downsampleWidth, FX_INT32 downsampleHeight)
299 {
300 if (m_pCachedBitmap) {
301 m_pCurBitmap = m_pCachedBitmap;
302 m_pCurMask = m_pCachedMask;
303 return 1;
304 }
305 if (!pRenderStatus) {
306 return 0;
307 }
308 m_pRenderStatus = pRenderStatus;
309 m_pCurBitmap = FX_NEW CPDF_DIBSource;
310 int ret = ((CPDF_DIBSource*)m_pCurBitmap)->StartLoadDIBSource(m_pDocument, m_pStream, TRUE, pFormResources, pPageResources, bStdCS, GroupFamily, bLoadMask);
311 if (ret == 2) {
312 return ret;
313 }
314 if (!ret) {
315 delete m_pCurBitmap;
316 m_pCurBitmap = NULL;
317 return 0;
318 }
319 ContinueGetCachedBitmap();
320 return 0;
321 }
ContinueGetCachedBitmap()322 int CPDF_ImageCache::ContinueGetCachedBitmap()
323 {
324 m_MatteColor = ((CPDF_DIBSource*)m_pCurBitmap)->m_MatteColor;
325 m_pCurMask = ((CPDF_DIBSource*)m_pCurBitmap)->DetachMask();
326 CPDF_RenderContext*pContext = m_pRenderStatus->GetContext();
327 CPDF_PageRenderCache* pPageRenderCache = pContext->m_pPageCache;
328 m_dwTimeCount = pPageRenderCache->GetTimeCount();
329 #if !defined(_FPDFAPI_MINI_)
330 if (m_pCurBitmap->GetPitch() * m_pCurBitmap->GetHeight() < FPDF_HUGE_IMAGE_SIZE) {
331 m_pCachedBitmap = m_pCurBitmap->Clone();
332 delete m_pCurBitmap;
333 m_pCurBitmap = NULL;
334 } else {
335 m_pCachedBitmap = m_pCurBitmap;
336 }
337 if (m_pCurMask) {
338 m_pCachedMask = m_pCurMask->Clone();
339 delete m_pCurMask;
340 m_pCurMask = NULL;
341 }
342 #else
343 if (m_pCurBitmap->GetFormat() == FXDIB_8bppRgb && m_pCurBitmap->GetPalette() &&
344 m_pCurBitmap->GetHeight() * m_pCurBitmap->GetWidth() * 3 < 1024) {
345 m_pCachedBitmap = m_pCurBitmap->CloneConvert(FXDIB_Rgb32);
346 m_pCachedBitmap = m_pCurBitmap->CloneConvert(FXDIB_Rgb);
347 delete m_pCurBitmap;
348 m_pCurBitmap = NULL;
349 } else if (m_pCurBitmap->GetPitch() * m_pCurBitmap->GetHeight() < 102400) {
350 m_pCachedBitmap = m_pCurBitmap->Clone();
351 delete m_pCurBitmap;
352 m_pCurBitmap = NULL;
353 } else {
354 m_pCachedBitmap = m_pCurBitmap;
355 }
356 m_pCachedMask = m_pCurMask;
357 #endif
358 m_pCurBitmap = m_pCachedBitmap;
359 m_pCurMask = m_pCachedMask;
360 CalcSize();
361 return 0;
362 }
Continue(IFX_Pause * pPause)363 int CPDF_ImageCache::Continue(IFX_Pause* pPause)
364 {
365 int ret = ((CPDF_DIBSource*)m_pCurBitmap)->ContinueLoadDIBSource(pPause);
366 if (ret == 2) {
367 return ret;
368 }
369 if (!ret) {
370 delete m_pCurBitmap;
371 m_pCurBitmap = NULL;
372 return 0;
373 }
374 ContinueGetCachedBitmap();
375 return 0;
376 }
CalcSize()377 void CPDF_ImageCache::CalcSize()
378 {
379 m_dwCacheSize = FPDF_ImageCache_EstimateImageSize(m_pCachedBitmap) + FPDF_ImageCache_EstimateImageSize(m_pCachedMask);
380 }
ClearRenderFont()381 void CPDF_Document::ClearRenderFont()
382 {
383 if (m_pDocRender) {
384 CFX_FontCache* pCache = m_pDocRender->GetFontCache();
385 if (pCache) {
386 pCache->FreeCache(FALSE);
387 }
388 }
389 }
390