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