Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cpdf_pageimagecache.cpp
Go to the documentation of this file.
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 <stddef.h>
10#include <stdint.h>
11
12#include <algorithm>
13#include <utility>
14#include <vector>
15
16#include "core/fpdfapi/page/cpdf_dib.h"
17#include "core/fpdfapi/page/cpdf_image.h"
18#include "core/fpdfapi/page/cpdf_page.h"
19#include "core/fpdfapi/parser/cpdf_dictionary.h"
20#include "core/fpdfapi/parser/cpdf_document.h"
21#include "core/fpdfapi/parser/cpdf_stream.h"
22#include "core/fxcrt/retain_ptr.h"
23#include "core/fxcrt/stl_util.h"
24#include "core/fxge/dib/cfx_dibbase.h"
25#include "core/fxge/dib/cfx_dibitmap.h"
26#include "third_party/base/check.h"
27
28#if defined(PDF_USE_SKIA)
29#include "core/fxcrt/data_vector.h"
30#include "core/fxge/cfx_defaultrenderdevice.h"
31#include "third_party/skia/include/core/SkImage.h" // nogncheck
32#include "third_party/skia/include/core/SkRefCnt.h" // nogncheck
33#endif
34
35namespace {
36
37struct CacheInfo {
38 CacheInfo(uint32_t t, RetainPtr<const CPDF_Stream> stream)
39 : time(t), pStream(std::move(stream)) {}
40
41 uint32_t time;
42 RetainPtr<const CPDF_Stream> pStream;
43
44 bool operator<(const CacheInfo& other) const { return time < other.time; }
45};
46
47#if defined(PDF_USE_SKIA)
48// Wrapper around a `CFX_DIBBase` that memoizes `RealizeSkImage()`. This is only
49// safe if the underlying `CFX_DIBBase` is not mutable.
50class CachedImage final : public CFX_DIBBase {
51 public:
52 explicit CachedImage(RetainPtr<CFX_DIBBase> image)
53 : image_(std::move(image)) {
54 m_Format = image_->GetFormat();
55 m_Width = image_->GetWidth();
56 m_Height = image_->GetHeight();
57 m_Pitch = image_->GetPitch();
58
59 if (image_->HasPalette()) {
60 pdfium::span<const uint32_t> palette = image_->GetPaletteSpan();
61 m_palette = DataVector<uint32_t>(palette.begin(), palette.end());
62 }
63 }
64
65 pdfium::span<const uint8_t> GetScanline(int line) const override {
66 // TODO(crbug.com/pdfium/2050): Still needed for `Realize()` call in
67 // `CPDF_ImageRenderer`.
68 return image_->GetScanline(line);
69 }
70
71 bool SkipToScanline(int line, PauseIndicatorIface* pause) const override {
72 return image_->SkipToScanline(line, pause);
73 }
74
75 size_t GetEstimatedImageMemoryBurden() const override {
76 // A better estimate would account for realizing the `SkImage`.
77 return image_->GetEstimatedImageMemoryBurden();
78 }
79
80#if BUILDFLAG(IS_WIN) || defined(PDF_USE_SKIA)
81 RetainPtr<const CFX_DIBitmap> RealizeIfNeeded() const override {
82 return image_->RealizeIfNeeded();
83 }
84#endif
85
86 sk_sp<SkImage> RealizeSkImage() const override {
87 if (!cached_skia_image_) {
88 cached_skia_image_ = image_->RealizeSkImage();
89 }
90 return cached_skia_image_;
91 }
92
93 private:
94 RetainPtr<CFX_DIBBase> image_;
95 mutable sk_sp<SkImage> cached_skia_image_;
96};
97#endif // defined(PDF_USE_SKIA)
98
99// Makes a `CachedImage` backed by `image` if Skia is the default renderer,
100// otherwise return the image itself. `realize_hint` indicates whether it would
101// be beneficial to realize `image` before caching.
102RetainPtr<CFX_DIBBase> MakeCachedImage(RetainPtr<CFX_DIBBase> image,
103 bool realize_hint) {
104#if defined(PDF_USE_SKIA)
105 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
106 // Ignore `realize_hint`, as `RealizeSkImage()` doesn't benefit from it.
107 return pdfium::MakeRetain<CachedImage>(std::move(image));
108 }
109#endif // defined(PDF_USE_SKIA)
110 return realize_hint ? image->Realize() : image;
111}
112
113} // namespace
114
116
118
119void CPDF_PageImageCache::CacheOptimization(int32_t dwLimitCacheSize) {
120 if (m_nCacheSize <= (uint32_t)dwLimitCacheSize)
121 return;
122
123 uint32_t nCount = fxcrt::CollectionSize<uint32_t>(m_ImageCache);
124 std::vector<CacheInfo> cache_info;
125 cache_info.reserve(nCount);
126 for (const auto& it : m_ImageCache) {
127 cache_info.emplace_back(it.second->GetTimeCount(),
128 it.second->GetImage()->GetStream());
129 }
130 std::sort(cache_info.begin(), cache_info.end());
131
132 // Check if time value is about to roll over and reset all entries.
133 // The comparison is legal because uint32_t is an unsigned type.
134 uint32_t nTimeCount = m_nTimeCount;
135 if (nTimeCount + 1 < nTimeCount) {
136 for (uint32_t i = 0; i < nCount; i++)
137 m_ImageCache[cache_info[i].pStream]->SetTimeCount(i);
138 m_nTimeCount = nCount;
139 }
140
141 size_t i = 0;
142 while (i + 15 < nCount)
143 ClearImageCacheEntry(cache_info[i++].pStream);
144
145 while (i < nCount && m_nCacheSize > (uint32_t)dwLimitCacheSize)
146 ClearImageCacheEntry(cache_info[i++].pStream);
147}
148
149void CPDF_PageImageCache::ClearImageCacheEntry(const CPDF_Stream* pStream) {
150 auto it = m_ImageCache.find(pStream);
151 if (it == m_ImageCache.end())
152 return;
153
154 m_nCacheSize -= it->second->EstimateSize();
155
156 // Avoid leaving `m_pCurImageCacheEntry` as a dangling pointer when `it` is
157 // about to be deleted.
158 if (m_pCurImageCacheEntry.Get() == it->second.get()) {
159 DCHECK(!m_pCurImageCacheEntry.IsOwned());
160 m_pCurImageCacheEntry.Reset();
161 }
162 m_ImageCache.erase(it);
163}
164
166 RetainPtr<CPDF_Image> pImage,
167 const CPDF_Dictionary* pFormResources,
168 const CPDF_Dictionary* pPageResources,
169 bool bStdCS,
170 CPDF_ColorSpace::Family eFamily,
171 bool bLoadMask,
172 const CFX_Size& max_size_required) {
173 // A cross-document image may have come from the embedder.
174 if (m_pPage->GetDocument() != pImage->GetDocument())
175 return false;
176
177 RetainPtr<const CPDF_Stream> pStream = pImage->GetStream();
178 const auto it = m_ImageCache.find(pStream);
179 m_bCurFindCache = it != m_ImageCache.end();
180 if (m_bCurFindCache) {
181 m_pCurImageCacheEntry = it->second.get();
182 } else {
183 m_pCurImageCacheEntry = std::make_unique<Entry>(std::move(pImage));
184 }
185 CPDF_DIB::LoadState ret = m_pCurImageCacheEntry->StartGetCachedBitmap(
186 this, pFormResources, pPageResources, bStdCS, eFamily, bLoadMask,
187 max_size_required);
188 if (ret == CPDF_DIB::LoadState::kContinue)
189 return true;
190
191 m_nTimeCount++;
192 if (!m_bCurFindCache)
193 m_ImageCache[pStream] = m_pCurImageCacheEntry.Release();
194
195 if (ret == CPDF_DIB::LoadState::kFail)
196 m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
197
198 return false;
199}
200
202 bool ret = m_pCurImageCacheEntry->Continue(pPause, this);
203 if (ret)
204 return true;
205
206 m_nTimeCount++;
207 if (!m_bCurFindCache) {
208 m_ImageCache[m_pCurImageCacheEntry->GetImage()->GetStream()] =
209 m_pCurImageCacheEntry.Release();
210 }
211 m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
212 return false;
213}
214
216 RetainPtr<const CPDF_Stream> pStream = pImage->GetStream();
217 const auto it = m_ImageCache.find(pStream);
218 if (it == m_ImageCache.end())
219 return;
220
221 Entry* pEntry = it->second.get();
222 m_nCacheSize -= pEntry->EstimateSize();
223 pEntry->Reset();
224 m_nCacheSize += pEntry->EstimateSize();
225}
226
228 return m_pCurImageCacheEntry->GetMatteColor();
229}
230
232 return m_pCurImageCacheEntry->DetachBitmap();
233}
234
236 return m_pCurImageCacheEntry->DetachMask();
237}
238
239CPDF_PageImageCache::Entry::Entry(RetainPtr<CPDF_Image> pImage)
240 : m_pImage(std::move(pImage)) {}
241
242CPDF_PageImageCache::Entry::~Entry() = default;
243
244void CPDF_PageImageCache::Entry::Reset() {
245 m_pCachedBitmap.Reset();
246 CalcSize();
247}
248
249RetainPtr<CFX_DIBBase> CPDF_PageImageCache::Entry::DetachBitmap() {
250 return std::move(m_pCurBitmap);
251}
252
253RetainPtr<CFX_DIBBase> CPDF_PageImageCache::Entry::DetachMask() {
254 return std::move(m_pCurMask);
255}
256
257CPDF_DIB::LoadState CPDF_PageImageCache::Entry::StartGetCachedBitmap(
258 CPDF_PageImageCache* pPageImageCache,
259 const CPDF_Dictionary* pFormResources,
260 const CPDF_Dictionary* pPageResources,
261 bool bStdCS,
262 CPDF_ColorSpace::Family eFamily,
263 bool bLoadMask,
264 const CFX_Size& max_size_required) {
265 if (m_pCachedBitmap && IsCacheValid(max_size_required)) {
266 m_pCurBitmap = m_pCachedBitmap;
267 m_pCurMask = m_pCachedMask;
268 return CPDF_DIB::LoadState::kSuccess;
269 }
270
271 m_pCurBitmap = m_pImage->CreateNewDIB();
272 CPDF_DIB::LoadState ret = m_pCurBitmap.AsRaw<CPDF_DIB>()->StartLoadDIBBase(
273 true, pFormResources, pPageResources, bStdCS, eFamily, bLoadMask,
274 max_size_required);
275 m_bCachedSetMaxSizeRequired =
276 (max_size_required.width != 0 && max_size_required.height != 0);
277 if (ret == CPDF_DIB::LoadState::kContinue)
278 return CPDF_DIB::LoadState::kContinue;
279
280 if (ret == CPDF_DIB::LoadState::kSuccess)
281 ContinueGetCachedBitmap(pPageImageCache);
282 else
283 m_pCurBitmap.Reset();
284 return CPDF_DIB::LoadState::kFail;
285}
286
287bool CPDF_PageImageCache::Entry::Continue(
288 PauseIndicatorIface* pPause,
289 CPDF_PageImageCache* pPageImageCache) {
290 CPDF_DIB::LoadState ret =
291 m_pCurBitmap.AsRaw<CPDF_DIB>()->ContinueLoadDIBBase(pPause);
292 if (ret == CPDF_DIB::LoadState::kContinue)
293 return true;
294
295 if (ret == CPDF_DIB::LoadState::kSuccess)
296 ContinueGetCachedBitmap(pPageImageCache);
297 else
298 m_pCurBitmap.Reset();
299 return false;
300}
301
302void CPDF_PageImageCache::Entry::ContinueGetCachedBitmap(
303 CPDF_PageImageCache* pPageImageCache) {
304 m_MatteColor = m_pCurBitmap.AsRaw<CPDF_DIB>()->GetMatteColor();
305 m_pCurMask = m_pCurBitmap.AsRaw<CPDF_DIB>()->DetachMask();
306 m_dwTimeCount = pPageImageCache->GetTimeCount();
307 if (m_pCurBitmap->GetPitch() * m_pCurBitmap->GetHeight() < kHugeImageSize) {
308 m_pCachedBitmap = MakeCachedImage(m_pCurBitmap, /*realize_hint=*/true);
309 m_pCurBitmap.Reset();
310 } else {
311 m_pCachedBitmap = MakeCachedImage(m_pCurBitmap, /*realize_hint=*/false);
312 }
313 if (m_pCurMask) {
314 m_pCachedMask = MakeCachedImage(m_pCurMask, /*realize_hint=*/true);
315 m_pCurMask.Reset();
316 }
317 m_pCurBitmap = m_pCachedBitmap;
318 m_pCurMask = m_pCachedMask;
319 CalcSize();
320}
321
322void CPDF_PageImageCache::Entry::CalcSize() {
323 m_dwCacheSize = 0;
324 if (m_pCachedBitmap)
325 m_dwCacheSize += m_pCachedBitmap->GetEstimatedImageMemoryBurden();
326 if (m_pCachedMask)
327 m_dwCacheSize += m_pCachedMask->GetEstimatedImageMemoryBurden();
328}
329
330bool CPDF_PageImageCache::Entry::IsCacheValid(
331 const CFX_Size& max_size_required) const {
332 if (!m_bCachedSetMaxSizeRequired) {
333 return true;
334 }
335 if (max_size_required.width == 0 && max_size_required.height == 0) {
336 return false;
337 }
338
339 return (m_pCachedBitmap->GetWidth() >= max_size_required.width) &&
340 (m_pCachedBitmap->GetHeight() >= max_size_required.height);
341}
void ResetBitmapForImage(RetainPtr< CPDF_Image > pImage)
RetainPtr< CFX_DIBBase > DetachCurBitmap()
bool 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)
bool Continue(PauseIndicatorIface *pPause)
uint32_t GetCurMatteColor() const
void CacheOptimization(int32_t dwLimitCacheSize)
CPDF_PageImageCache(CPDF_Page *pPage)
RetainPtr< CFX_DIBBase > DetachCurMask()
uint32_t GetTimeCount() const