Home | History | Annotate | Download | only in fpdf_render
      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 
     14 struct CACHEINFO {
     15   FX_DWORD time;
     16   CPDF_Stream* pStream;
     17 };
     18 
     19 extern "C" {
     20 static int compare(const void* data1, const void* data2) {
     21   return ((CACHEINFO*)data1)->time - ((CACHEINFO*)data2)->time;
     22 }
     23 }  // extern "C"
     24 
     25 CPDF_PageRenderCache::~CPDF_PageRenderCache() {
     26   for (const auto& it : m_ImageCache)
     27     delete it.second;
     28 }
     29 void 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 }
     60 void 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 }
     69 FX_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 }
     77 void 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 }
    106 FX_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 }
    137 FX_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 }
    148 void 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 }
    164 CPDF_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) {}
    176 CPDF_ImageCacheEntry::~CPDF_ImageCacheEntry() {
    177   delete m_pCachedBitmap;
    178   delete m_pCachedMask;
    179 }
    180 void 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 }
    188 void CPDF_PageRenderCache::ClearImageData() {
    189   for (const auto& it : m_ImageCache)
    190     it.second->ClearImageData();
    191 }
    192 void CPDF_ImageCacheEntry::ClearImageData() {
    193   if (m_pCachedBitmap && !m_pCachedBitmap->GetBuffer()) {
    194     ((CPDF_DIBSource*)m_pCachedBitmap)->ClearImageData();
    195   }
    196 }
    197 static 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 }
    203 FX_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 }
    251 CFX_DIBSource* CPDF_ImageCacheEntry::DetachBitmap() {
    252   CFX_DIBSource* pDIBSource = m_pCurBitmap;
    253   m_pCurBitmap = NULL;
    254   return pDIBSource;
    255 }
    256 CFX_DIBSource* CPDF_ImageCacheEntry::DetachMask() {
    257   CFX_DIBSource* pDIBSource = m_pCurMask;
    258   m_pCurMask = NULL;
    259   return pDIBSource;
    260 }
    261 int 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 }
    294 void 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 }
    317 int 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 }
    330 void CPDF_ImageCacheEntry::CalcSize() {
    331   m_dwCacheSize = FPDF_ImageCache_EstimateImageSize(m_pCachedBitmap) +
    332                   FPDF_ImageCache_EstimateImageSize(m_pCachedMask);
    333 }
    334 void 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