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