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"
12struct CACHEINFO {
13    FX_DWORD time;
14    CPDF_Stream* pStream;
15};
16extern "C" {
17    static int compare(const void* data1, const void* data2)
18    {
19        return ((CACHEINFO*)data1)->time - ((CACHEINFO*)data2)->time;
20    }
21};
22void CPDF_Page::ClearRenderCache()
23{
24    if (m_pPageRender) {
25        m_pPageRender->ClearAll();
26    }
27}
28void 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}
40void 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}
73void 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}
84FX_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}
96FX_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}
107void 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}
125FX_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}
144FX_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}
159void 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}
173CPDF_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}
186CPDF_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}
197void 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}
208void 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}
217void CPDF_ImageCache::ClearImageData()
218{
219    if (m_pCachedBitmap && m_pCachedBitmap->GetBuffer() == NULL) {
220        ((CPDF_DIBSource*)m_pCachedBitmap)->ClearImageData();
221    }
222}
223static 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}
227FX_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}
284CFX_DIBSource* CPDF_ImageCache::DetachBitmap()
285{
286    CFX_DIBSource* pDIBSource = m_pCurBitmap;
287    m_pCurBitmap = NULL;
288    return pDIBSource;
289}
290CFX_DIBSource* CPDF_ImageCache::DetachMask()
291{
292    CFX_DIBSource* pDIBSource = m_pCurMask;
293    m_pCurMask = NULL;
294    return pDIBSource;
295}
296int	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}
322int 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}
363int	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}
377void CPDF_ImageCache::CalcSize()
378{
379    m_dwCacheSize = FPDF_ImageCache_EstimateImageSize(m_pCachedBitmap) + FPDF_ImageCache_EstimateImageSize(m_pCachedMask);
380}
381void 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