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