• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The PDFium Authors
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 "core/fpdfapi/page/cpdf_pageimagecache.h"
8 
9 #include <algorithm>
10 #include <utility>
11 #include <vector>
12 
13 #include "core/fpdfapi/page/cpdf_dib.h"
14 #include "core/fpdfapi/page/cpdf_image.h"
15 #include "core/fpdfapi/page/cpdf_page.h"
16 #include "core/fpdfapi/parser/cpdf_dictionary.h"
17 #include "core/fpdfapi/parser/cpdf_document.h"
18 #include "core/fpdfapi/parser/cpdf_stream.h"
19 #include "core/fxcrt/stl_util.h"
20 #include "core/fxge/dib/cfx_dibitmap.h"
21 
22 namespace {
23 
24 struct CacheInfo {
CacheInfo__anon456074780111::CacheInfo25   CacheInfo(uint32_t t, RetainPtr<const CPDF_Stream> stream)
26       : time(t), pStream(std::move(stream)) {}
27 
28   uint32_t time;
29   RetainPtr<const CPDF_Stream> pStream;
30 
operator <__anon456074780111::CacheInfo31   bool operator<(const CacheInfo& other) const { return time < other.time; }
32 };
33 
34 }  // namespace
35 
CPDF_PageImageCache(CPDF_Page * pPage)36 CPDF_PageImageCache::CPDF_PageImageCache(CPDF_Page* pPage) : m_pPage(pPage) {}
37 
38 CPDF_PageImageCache::~CPDF_PageImageCache() = default;
39 
CacheOptimization(int32_t dwLimitCacheSize)40 void CPDF_PageImageCache::CacheOptimization(int32_t dwLimitCacheSize) {
41   if (m_nCacheSize <= (uint32_t)dwLimitCacheSize)
42     return;
43 
44   uint32_t nCount = fxcrt::CollectionSize<uint32_t>(m_ImageCache);
45   std::vector<CacheInfo> cache_info;
46   cache_info.reserve(nCount);
47   for (const auto& it : m_ImageCache) {
48     cache_info.emplace_back(it.second->GetTimeCount(),
49                             it.second->GetImage()->GetStream());
50   }
51   std::sort(cache_info.begin(), cache_info.end());
52 
53   // Check if time value is about to roll over and reset all entries.
54   // The comparison is legal because uint32_t is an unsigned type.
55   uint32_t nTimeCount = m_nTimeCount;
56   if (nTimeCount + 1 < nTimeCount) {
57     for (uint32_t i = 0; i < nCount; i++)
58       m_ImageCache[cache_info[i].pStream]->SetTimeCount(i);
59     m_nTimeCount = nCount;
60   }
61 
62   size_t i = 0;
63   while (i + 15 < nCount)
64     ClearImageCacheEntry(cache_info[i++].pStream);
65 
66   while (i < nCount && m_nCacheSize > (uint32_t)dwLimitCacheSize)
67     ClearImageCacheEntry(cache_info[i++].pStream);
68 }
69 
ClearImageCacheEntry(const CPDF_Stream * pStream)70 void CPDF_PageImageCache::ClearImageCacheEntry(const CPDF_Stream* pStream) {
71   auto it = m_ImageCache.find(pStream);
72   if (it == m_ImageCache.end())
73     return;
74 
75   m_nCacheSize -= it->second->EstimateSize();
76 
77   // Avoid leaving `m_pCurImageCacheEntry` as a dangling pointer when `it` is
78   // about to be deleted.
79   if (m_pCurImageCacheEntry.Get() == it->second.get()) {
80     DCHECK(!m_pCurImageCacheEntry.IsOwned());
81     m_pCurImageCacheEntry.Reset();
82   }
83   m_ImageCache.erase(it);
84 }
85 
StartGetCachedBitmap(RetainPtr<CPDF_Image> pImage,const CPDF_Dictionary * pFormResources,const CPDF_Dictionary * pPageResources,bool bStdCS,CPDF_ColorSpace::Family eFamily,bool bLoadMask,const CFX_Size & max_size_required)86 bool CPDF_PageImageCache::StartGetCachedBitmap(
87     RetainPtr<CPDF_Image> pImage,
88     const CPDF_Dictionary* pFormResources,
89     const CPDF_Dictionary* pPageResources,
90     bool bStdCS,
91     CPDF_ColorSpace::Family eFamily,
92     bool bLoadMask,
93     const CFX_Size& max_size_required) {
94   // A cross-document image may have come from the embedder.
95   if (m_pPage->GetDocument() != pImage->GetDocument())
96     return false;
97 
98   RetainPtr<const CPDF_Stream> pStream = pImage->GetStream();
99   const auto it = m_ImageCache.find(pStream);
100   m_bCurFindCache = it != m_ImageCache.end();
101   if (m_bCurFindCache) {
102     m_pCurImageCacheEntry = it->second.get();
103   } else {
104     m_pCurImageCacheEntry = std::make_unique<Entry>(std::move(pImage));
105   }
106   CPDF_DIB::LoadState ret = m_pCurImageCacheEntry->StartGetCachedBitmap(
107       this, pFormResources, pPageResources, bStdCS, eFamily, bLoadMask,
108       max_size_required);
109   if (ret == CPDF_DIB::LoadState::kContinue)
110     return true;
111 
112   m_nTimeCount++;
113   if (!m_bCurFindCache)
114     m_ImageCache[pStream] = m_pCurImageCacheEntry.Release();
115 
116   if (ret == CPDF_DIB::LoadState::kFail)
117     m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
118 
119   return false;
120 }
121 
Continue(PauseIndicatorIface * pPause)122 bool CPDF_PageImageCache::Continue(PauseIndicatorIface* pPause) {
123   bool ret = m_pCurImageCacheEntry->Continue(pPause, this);
124   if (ret)
125     return true;
126 
127   m_nTimeCount++;
128   if (!m_bCurFindCache) {
129     m_ImageCache[m_pCurImageCacheEntry->GetImage()->GetStream()] =
130         m_pCurImageCacheEntry.Release();
131   }
132   m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
133   return false;
134 }
135 
ResetBitmapForImage(RetainPtr<CPDF_Image> pImage)136 void CPDF_PageImageCache::ResetBitmapForImage(RetainPtr<CPDF_Image> pImage) {
137   RetainPtr<const CPDF_Stream> pStream = pImage->GetStream();
138   const auto it = m_ImageCache.find(pStream);
139   if (it == m_ImageCache.end())
140     return;
141 
142   Entry* pEntry = it->second.get();
143   m_nCacheSize -= pEntry->EstimateSize();
144   pEntry->Reset();
145   m_nCacheSize += pEntry->EstimateSize();
146 }
147 
GetCurMatteColor() const148 uint32_t CPDF_PageImageCache::GetCurMatteColor() const {
149   return m_pCurImageCacheEntry->GetMatteColor();
150 }
151 
DetachCurBitmap()152 RetainPtr<CFX_DIBBase> CPDF_PageImageCache::DetachCurBitmap() {
153   return m_pCurImageCacheEntry->DetachBitmap();
154 }
155 
DetachCurMask()156 RetainPtr<CFX_DIBBase> CPDF_PageImageCache::DetachCurMask() {
157   return m_pCurImageCacheEntry->DetachMask();
158 }
159 
Entry(RetainPtr<CPDF_Image> pImage)160 CPDF_PageImageCache::Entry::Entry(RetainPtr<CPDF_Image> pImage)
161     : m_pImage(std::move(pImage)) {}
162 
163 CPDF_PageImageCache::Entry::~Entry() = default;
164 
Reset()165 void CPDF_PageImageCache::Entry::Reset() {
166   m_pCachedBitmap.Reset();
167   CalcSize();
168 }
169 
DetachBitmap()170 RetainPtr<CFX_DIBBase> CPDF_PageImageCache::Entry::DetachBitmap() {
171   return std::move(m_pCurBitmap);
172 }
173 
DetachMask()174 RetainPtr<CFX_DIBBase> CPDF_PageImageCache::Entry::DetachMask() {
175   return std::move(m_pCurMask);
176 }
177 
StartGetCachedBitmap(CPDF_PageImageCache * pPageImageCache,const CPDF_Dictionary * pFormResources,const CPDF_Dictionary * pPageResources,bool bStdCS,CPDF_ColorSpace::Family eFamily,bool bLoadMask,const CFX_Size & max_size_required)178 CPDF_DIB::LoadState CPDF_PageImageCache::Entry::StartGetCachedBitmap(
179     CPDF_PageImageCache* pPageImageCache,
180     const CPDF_Dictionary* pFormResources,
181     const CPDF_Dictionary* pPageResources,
182     bool bStdCS,
183     CPDF_ColorSpace::Family eFamily,
184     bool bLoadMask,
185     const CFX_Size& max_size_required) {
186   if (m_pCachedBitmap && IsCacheValid(max_size_required)) {
187     m_pCurBitmap = m_pCachedBitmap;
188     m_pCurMask = m_pCachedMask;
189     return CPDF_DIB::LoadState::kSuccess;
190   }
191 
192   m_pCurBitmap = m_pImage->CreateNewDIB();
193   CPDF_DIB::LoadState ret = m_pCurBitmap.AsRaw<CPDF_DIB>()->StartLoadDIBBase(
194       true, pFormResources, pPageResources, bStdCS, eFamily, bLoadMask,
195       max_size_required);
196   m_bCachedSetMaxSizeRequired =
197       (max_size_required.width != 0 && max_size_required.height != 0);
198   if (ret == CPDF_DIB::LoadState::kContinue)
199     return CPDF_DIB::LoadState::kContinue;
200 
201   if (ret == CPDF_DIB::LoadState::kSuccess)
202     ContinueGetCachedBitmap(pPageImageCache);
203   else
204     m_pCurBitmap.Reset();
205   return CPDF_DIB::LoadState::kFail;
206 }
207 
Continue(PauseIndicatorIface * pPause,CPDF_PageImageCache * pPageImageCache)208 bool CPDF_PageImageCache::Entry::Continue(
209     PauseIndicatorIface* pPause,
210     CPDF_PageImageCache* pPageImageCache) {
211   CPDF_DIB::LoadState ret =
212       m_pCurBitmap.AsRaw<CPDF_DIB>()->ContinueLoadDIBBase(pPause);
213   if (ret == CPDF_DIB::LoadState::kContinue)
214     return true;
215 
216   if (ret == CPDF_DIB::LoadState::kSuccess)
217     ContinueGetCachedBitmap(pPageImageCache);
218   else
219     m_pCurBitmap.Reset();
220   return false;
221 }
222 
ContinueGetCachedBitmap(CPDF_PageImageCache * pPageImageCache)223 void CPDF_PageImageCache::Entry::ContinueGetCachedBitmap(
224     CPDF_PageImageCache* pPageImageCache) {
225   m_MatteColor = m_pCurBitmap.AsRaw<CPDF_DIB>()->GetMatteColor();
226   m_pCurMask = m_pCurBitmap.AsRaw<CPDF_DIB>()->DetachMask();
227   m_dwTimeCount = pPageImageCache->GetTimeCount();
228   if (m_pCurBitmap->GetPitch() * m_pCurBitmap->GetHeight() < kHugeImageSize) {
229     m_pCachedBitmap = m_pCurBitmap->Realize();
230     m_pCurBitmap.Reset();
231   } else {
232     m_pCachedBitmap = m_pCurBitmap;
233   }
234   if (m_pCurMask) {
235     m_pCachedMask = m_pCurMask->Realize();
236     m_pCurMask.Reset();
237   }
238   m_pCurBitmap = m_pCachedBitmap;
239   m_pCurMask = m_pCachedMask;
240   CalcSize();
241 }
242 
CalcSize()243 void CPDF_PageImageCache::Entry::CalcSize() {
244   m_dwCacheSize = 0;
245   if (m_pCachedBitmap)
246     m_dwCacheSize += m_pCachedBitmap->GetEstimatedImageMemoryBurden();
247   if (m_pCachedMask)
248     m_dwCacheSize += m_pCachedMask->GetEstimatedImageMemoryBurden();
249 }
250 
IsCacheValid(const CFX_Size & max_size_required) const251 bool CPDF_PageImageCache::Entry::IsCacheValid(
252     const CFX_Size& max_size_required) const {
253   if (!m_bCachedSetMaxSizeRequired) {
254     return true;
255   }
256   if (max_size_required.width == 0 && max_size_required.height == 0) {
257     return false;
258   }
259 
260   return (m_pCachedBitmap->GetWidth() >= max_size_required.width) &&
261          (m_pCachedBitmap->GetHeight() >= max_size_required.height);
262 }
263