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