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