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_Alloc2D(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 = 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 = 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 = 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_dwTimeCount(0)
175 , m_pCurBitmap(NULL)
176 , m_pCurMask(NULL)
177 , m_MatteColor(0)
178 , m_pRenderStatus(NULL)
179 , m_pDocument(pDoc)
180 , m_pStream(pStream)
181 , m_pCachedBitmap(NULL)
182 , m_pCachedMask(NULL)
183 , m_dwCacheSize(0)
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 = 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 (pSrc->GetPitch() * pSrc->GetHeight() < FPDF_HUGE_IMAGE_SIZE) {
252 m_pCachedBitmap = pSrc->Clone();
253 delete pSrc;
254 } else {
255 m_pCachedBitmap = pSrc;
256 }
257 if (pMaskSrc) {
258 m_pCachedMask = pMaskSrc->Clone();
259 delete pMaskSrc;
260 }
261
262 pBitmap = m_pCachedBitmap;
263 pMask = m_pCachedMask;
264 CalcSize();
265 return FALSE;
266 }
DetachBitmap()267 CFX_DIBSource* CPDF_ImageCache::DetachBitmap()
268 {
269 CFX_DIBSource* pDIBSource = m_pCurBitmap;
270 m_pCurBitmap = NULL;
271 return pDIBSource;
272 }
DetachMask()273 CFX_DIBSource* CPDF_ImageCache::DetachMask()
274 {
275 CFX_DIBSource* pDIBSource = m_pCurMask;
276 m_pCurMask = NULL;
277 return pDIBSource;
278 }
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)279 int CPDF_ImageCache::StartGetCachedBitmap(CPDF_Dictionary* pFormResources, CPDF_Dictionary* pPageResources, FX_BOOL bStdCS,
280 FX_DWORD GroupFamily, FX_BOOL bLoadMask, CPDF_RenderStatus* pRenderStatus,
281 FX_INT32 downsampleWidth, FX_INT32 downsampleHeight)
282 {
283 if (m_pCachedBitmap) {
284 m_pCurBitmap = m_pCachedBitmap;
285 m_pCurMask = m_pCachedMask;
286 return 1;
287 }
288 if (!pRenderStatus) {
289 return 0;
290 }
291 m_pRenderStatus = pRenderStatus;
292 m_pCurBitmap = new CPDF_DIBSource;
293 int ret = ((CPDF_DIBSource*)m_pCurBitmap)->StartLoadDIBSource(m_pDocument, m_pStream, TRUE, pFormResources, pPageResources, bStdCS, GroupFamily, bLoadMask);
294 if (ret == 2) {
295 return ret;
296 }
297 if (!ret) {
298 delete m_pCurBitmap;
299 m_pCurBitmap = NULL;
300 return 0;
301 }
302 ContinueGetCachedBitmap();
303 return 0;
304 }
ContinueGetCachedBitmap()305 int CPDF_ImageCache::ContinueGetCachedBitmap()
306 {
307 m_MatteColor = ((CPDF_DIBSource*)m_pCurBitmap)->m_MatteColor;
308 m_pCurMask = ((CPDF_DIBSource*)m_pCurBitmap)->DetachMask();
309 CPDF_RenderContext*pContext = m_pRenderStatus->GetContext();
310 CPDF_PageRenderCache* pPageRenderCache = pContext->m_pPageCache;
311 m_dwTimeCount = pPageRenderCache->GetTimeCount();
312 if (m_pCurBitmap->GetPitch() * m_pCurBitmap->GetHeight() < FPDF_HUGE_IMAGE_SIZE) {
313 m_pCachedBitmap = m_pCurBitmap->Clone();
314 delete m_pCurBitmap;
315 m_pCurBitmap = NULL;
316 } else {
317 m_pCachedBitmap = m_pCurBitmap;
318 }
319 if (m_pCurMask) {
320 m_pCachedMask = m_pCurMask->Clone();
321 delete m_pCurMask;
322 m_pCurMask = NULL;
323 }
324 m_pCurBitmap = m_pCachedBitmap;
325 m_pCurMask = m_pCachedMask;
326 CalcSize();
327 return 0;
328 }
Continue(IFX_Pause * pPause)329 int CPDF_ImageCache::Continue(IFX_Pause* pPause)
330 {
331 int ret = ((CPDF_DIBSource*)m_pCurBitmap)->ContinueLoadDIBSource(pPause);
332 if (ret == 2) {
333 return ret;
334 }
335 if (!ret) {
336 delete m_pCurBitmap;
337 m_pCurBitmap = NULL;
338 return 0;
339 }
340 ContinueGetCachedBitmap();
341 return 0;
342 }
CalcSize()343 void CPDF_ImageCache::CalcSize()
344 {
345 m_dwCacheSize = FPDF_ImageCache_EstimateImageSize(m_pCachedBitmap) + FPDF_ImageCache_EstimateImageSize(m_pCachedMask);
346 }
ClearRenderFont()347 void CPDF_Document::ClearRenderFont()
348 {
349 if (m_pDocRender) {
350 CFX_FontCache* pCache = m_pDocRender->GetFontCache();
351 if (pCache) {
352 pCache->FreeCache(FALSE);
353 }
354 }
355 }
356