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_rendertiling.cpp
Go to the documentation of this file.
1// Copyright 2020 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/render/cpdf_rendertiling.h"
8
9#include <limits>
10#include <memory>
11#include <utility>
12
13#include "core/fpdfapi/page/cpdf_form.h"
14#include "core/fpdfapi/page/cpdf_pageimagecache.h"
15#include "core/fpdfapi/page/cpdf_tilingpattern.h"
16#include "core/fpdfapi/parser/cpdf_document.h"
17#include "core/fpdfapi/render/cpdf_rendercontext.h"
18#include "core/fpdfapi/render/cpdf_renderoptions.h"
19#include "core/fpdfapi/render/cpdf_renderstatus.h"
20#include "core/fxcrt/fx_safe_types.h"
21#include "core/fxcrt/span_util.h"
22#include "core/fxge/cfx_defaultrenderdevice.h"
23#include "core/fxge/dib/cfx_dibitmap.h"
24
25namespace {
26
27RetainPtr<CFX_DIBitmap> DrawPatternBitmap(
28 CPDF_Document* pDoc,
29 CPDF_PageImageCache* pCache,
30 CPDF_TilingPattern* pPattern,
31 CPDF_Form* pPatternForm,
32 const CFX_Matrix& mtObject2Device,
33 int width,
34 int height,
35 const CPDF_RenderOptions::Options& draw_options) {
36 auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
37 // TODO(crbug.com/42271020): Consider adding support for
38 // `FXDIB_Format::kBgraPremul`
39 if (!pBitmap->Create(width, height,
42 return nullptr;
43 }
44 CFX_DefaultRenderDevice bitmap_device;
46 pBitmap, /*pBackdropBitmap=*/nullptr, /*bGroupKnockout=*/true);
47 CFX_FloatRect cell_bbox =
48 pPattern->pattern_to_form().TransformRect(pPattern->bbox());
49 cell_bbox = mtObject2Device.TransformRect(cell_bbox);
50 CFX_FloatRect bitmap_rect(0.0f, 0.0f, width, height);
51 CFX_Matrix mtAdjust;
52 mtAdjust.MatchRect(bitmap_rect, cell_bbox);
53
54 CFX_Matrix mtPattern2Bitmap = mtObject2Device * mtAdjust;
55 CPDF_RenderOptions options;
56 if (!pPattern->colored())
58
59 options.GetOptions() = draw_options;
60 options.GetOptions().bForceHalftone = true;
61
62 CPDF_RenderContext context(pDoc, nullptr, pCache);
63 context.AppendLayer(pPatternForm, mtPattern2Bitmap);
64 context.Render(&bitmap_device, nullptr, &options, nullptr);
65
66 return pBitmap;
67}
68
69} // namespace
70
71// static
73 CPDF_RenderStatus* pRenderStatus,
74 CPDF_PageObject* pPageObj,
75 CPDF_TilingPattern* pPattern,
76 CPDF_Form* pPatternForm,
77 const CFX_Matrix& mtObj2Device,
78 const FX_RECT& clip_box,
79 bool bStroke) {
80 const CFX_Matrix mtPattern2Device =
81 pPattern->pattern_to_form() * mtObj2Device;
82
83 CFX_FloatRect cell_bbox = mtPattern2Device.TransformRect(pPattern->bbox());
84
85 float ceil_height = std::ceil(cell_bbox.Height());
86 float ceil_width = std::ceil(cell_bbox.Width());
87
88 // Validate the float will fit into the int when the conversion is done.
89 if (!pdfium::IsValueInRangeForNumericType<int>(ceil_height) ||
90 !pdfium::IsValueInRangeForNumericType<int>(ceil_width)) {
91 return nullptr;
92 }
93
94 int width = static_cast<int>(ceil_width);
95 int height = static_cast<int>(ceil_height);
96 if (width <= 0)
97 width = 1;
98 if (height <= 0)
99 height = 1;
100
101 CFX_FloatRect clip_box_p =
102 mtPattern2Device.GetInverse().TransformRect(CFX_FloatRect(clip_box));
103 int min_col = static_cast<int>(
104 ceil((clip_box_p.left - pPattern->bbox().right) / pPattern->x_step()));
105 int max_col = static_cast<int>(
106 floor((clip_box_p.right - pPattern->bbox().left) / pPattern->x_step()));
107 int min_row = static_cast<int>(
108 ceil((clip_box_p.bottom - pPattern->bbox().top) / pPattern->y_step()));
109 int max_row = static_cast<int>(
110 floor((clip_box_p.top - pPattern->bbox().bottom) / pPattern->y_step()));
111
112 // Make sure we can fit the needed width * height into an int.
113 if (height > std::numeric_limits<int>::max() / width)
114 return nullptr;
115
116 CFX_RenderDevice* pDevice = pRenderStatus->GetRenderDevice();
117 CPDF_RenderContext* pContext = pRenderStatus->GetContext();
118 const CPDF_RenderOptions& options = pRenderStatus->GetRenderOptions();
119 if (width > clip_box.Width() || height > clip_box.Height() ||
120 width * height > clip_box.Width() * clip_box.Height()) {
121 std::unique_ptr<CPDF_GraphicStates> pStates;
122 if (!pPattern->colored()) {
123 pStates = CPDF_RenderStatus::CloneObjStates(&pPageObj->graphic_states(),
124 bStroke);
125 } else if (pPageObj->AsPath()) {
126 pStates = std::make_unique<CPDF_GraphicStates>();
127 pStates->SetDefaultStates();
128 pStates->mutable_general_state().SetFillAlpha(pPageObj->general_state().GetFillAlpha());
129 }
130
131 RetainPtr<const CPDF_Dictionary> pFormResource =
132 pPatternForm->GetDict()->GetDictFor("Resources");
133 for (int col = min_col; col <= max_col; col++) {
134 for (int row = min_row; row <= max_row; row++) {
135 CFX_PointF original = mtPattern2Device.Transform(
136 CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step()));
137 CFX_Matrix matrix = mtObj2Device;
138 matrix.Translate(original.x - mtPattern2Device.e,
139 original.y - mtPattern2Device.f);
140 CFX_RenderDevice::StateRestorer restorer2(pDevice);
141 CPDF_RenderStatus status(pContext, pDevice);
142 status.SetOptions(options);
143 status.SetTransparency(pPatternForm->GetTransparency());
144 status.SetFormResource(pFormResource);
145 status.SetDropObjects(pRenderStatus->GetDropObjects());
146 status.Initialize(pRenderStatus, pStates.get());
147 status.RenderObjectList(pPatternForm, matrix);
148 }
149 }
150 return nullptr;
151 }
152
153 bool bAligned =
154 pPattern->bbox().left == 0 && pPattern->bbox().bottom == 0 &&
155 pPattern->bbox().right == pPattern->x_step() &&
156 pPattern->bbox().top == pPattern->y_step() &&
157 (mtPattern2Device.IsScaled() || mtPattern2Device.Is90Rotated());
158 if (bAligned) {
159 int orig_x = FXSYS_roundf(mtPattern2Device.e);
160 int orig_y = FXSYS_roundf(mtPattern2Device.f);
161 min_col = (clip_box.left - orig_x) / width;
162 if (clip_box.left < orig_x)
163 min_col--;
164
165 max_col = (clip_box.right - orig_x) / width;
166 if (clip_box.right <= orig_x)
167 max_col--;
168
169 min_row = (clip_box.top - orig_y) / height;
170 if (clip_box.top < orig_y)
171 min_row--;
172
173 max_row = (clip_box.bottom - orig_y) / height;
174 if (clip_box.bottom <= orig_y)
175 max_row--;
176 }
177 float left_offset = cell_bbox.left - mtPattern2Device.e;
178 float top_offset = cell_bbox.bottom - mtPattern2Device.f;
179 RetainPtr<CFX_DIBitmap> pPatternBitmap;
180 if (width * height < 16) {
181 RetainPtr<CFX_DIBitmap> pEnlargedBitmap = DrawPatternBitmap(
182 pContext->GetDocument(), pContext->GetPageCache(), pPattern,
183 pPatternForm, mtObj2Device, 8, 8, options.GetOptions());
184 pPatternBitmap = pEnlargedBitmap->StretchTo(
185 width, height, FXDIB_ResampleOptions(), nullptr);
186 } else {
187 pPatternBitmap = DrawPatternBitmap(
188 pContext->GetDocument(), pContext->GetPageCache(), pPattern,
189 pPatternForm, mtObj2Device, width, height, options.GetOptions());
190 }
191 if (!pPatternBitmap)
192 return nullptr;
193
195 pPatternBitmap->ConvertColorScale(0, 0xffffff);
196
197 FX_ARGB fill_argb = pRenderStatus->GetFillArgb(pPageObj);
198 int clip_width = clip_box.right - clip_box.left;
199 int clip_height = clip_box.bottom - clip_box.top;
200 auto pScreen = pdfium::MakeRetain<CFX_DIBitmap>();
201 if (!pScreen->Create(clip_width, clip_height, FXDIB_Format::kBgra)) {
202 return nullptr;
203 }
204
205 pdfium::span<const uint8_t> src_buf = pPatternBitmap->GetBuffer();
206 for (int col = min_col; col <= max_col; col++) {
207 for (int row = min_row; row <= max_row; row++) {
208 int start_x;
209 int start_y;
210 if (bAligned) {
211 start_x =
212 FXSYS_roundf(mtPattern2Device.e) + col * width - clip_box.left;
213 start_y =
214 FXSYS_roundf(mtPattern2Device.f) + row * height - clip_box.top;
215 } else {
216 CFX_PointF original = mtPattern2Device.Transform(
217 CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step()));
218
219 FX_SAFE_INT32 safeStartX = FXSYS_roundf(original.x + left_offset);
220 FX_SAFE_INT32 safeStartY = FXSYS_roundf(original.y + top_offset);
221
222 safeStartX -= clip_box.left;
223 safeStartY -= clip_box.top;
224 if (!safeStartX.IsValid() || !safeStartY.IsValid())
225 return nullptr;
226
227 start_x = safeStartX.ValueOrDie();
228 start_y = safeStartY.ValueOrDie();
229 }
230 if (width == 1 && height == 1) {
231 if (start_x < 0 || start_x >= clip_box.Width() || start_y < 0 ||
232 start_y >= clip_box.Height()) {
233 continue;
234 }
235 uint32_t* dest_buf = fxcrt::reinterpret_span<uint32_t>(
236 pScreen->GetWritableScanline(start_y))
237 .subspan(start_x)
238 .data();
239 if (pPattern->colored()) {
240 const uint32_t* src_buf32 =
241 fxcrt::reinterpret_span<const uint32_t>(src_buf).data();
242 *dest_buf = *src_buf32;
243 } else {
244 *dest_buf = (*(src_buf.data()) << 24) | (fill_argb & 0xffffff);
245 }
246 } else {
247 if (pPattern->colored()) {
248 pScreen->CompositeBitmap(start_x, start_y, width, height,
249 pPatternBitmap, 0, 0, BlendMode::kNormal,
250 nullptr, false);
251 } else {
252 pScreen->CompositeMask(start_x, start_y, width, height,
253 pPatternBitmap, fill_argb, 0, 0,
254 BlendMode::kNormal, nullptr, false);
255 }
256 }
257 }
258 }
259 return pScreen;
260}
bool AttachWithBackdropAndGroupKnockout(RetainPtr< CFX_DIBitmap > pBitmap, RetainPtr< CFX_DIBitmap > pBackdropBitmap, bool bGroupKnockout)
constexpr CFX_FloatRect(float l, float b, float r, float t)
float Width() const
CFX_FloatRect(const FX_RECT &rect)
CFX_FloatRect & operator=(const CFX_FloatRect &that)=default
float Height() const
bool IsScaled() const
CFX_FloatRect TransformRect(const CFX_FloatRect &rect) const
void MatchRect(const CFX_FloatRect &dest, const CFX_FloatRect &src)
CFX_Matrix operator*(const CFX_Matrix &right) const
CFX_Matrix GetInverse() const
bool Is90Rotated() const
StateRestorer(CFX_RenderDevice *pDevice)
std::map< ByteString, RetainPtr< CPDF_Object >, std::less<> > DictMap
const CPDF_Transparency & GetTransparency() const
const CPDF_GraphicStates & graphic_states() const
const CPDF_GeneralState & general_state() const
virtual CPDF_PathObject * AsPath()
const CFX_Matrix & pattern_to_form() const
void AppendLayer(CPDF_PageObjectHolder *pObjectHolder, const CFX_Matrix &mtObject2Device)
void Render(CFX_RenderDevice *pDevice, const CPDF_PageObject *pStopObj, const CPDF_RenderOptions *pOptions, const CFX_Matrix *pLastMatrix)
bool ColorModeIs(Type mode) const
void SetColorMode(Type mode)
void SetDropObjects(bool bDropObjects)
const CPDF_RenderOptions & GetRenderOptions() const
CPDF_RenderStatus(CPDF_RenderContext *pContext, CFX_RenderDevice *pDevice)
CPDF_RenderContext * GetContext() const
void SetOptions(const CPDF_RenderOptions &options)
void SetTransparency(const CPDF_Transparency &transparency)
void RenderObjectList(const CPDF_PageObjectHolder *pObjectHolder, const CFX_Matrix &mtObj2Device)
CFX_RenderDevice * GetRenderDevice() const
void Initialize(const CPDF_RenderStatus *pParentStatus, const CPDF_GraphicStates *pInitialStates)
void SetFormResource(RetainPtr< const CPDF_Dictionary > pRes)
FX_ARGB GetFillArgb(CPDF_PageObject *pObj) const
bool GetDropObjects() const
static RetainPtr< CFX_DIBitmap > Draw(CPDF_RenderStatus *pRenderStatus, CPDF_PageObject *pPageObj, CPDF_TilingPattern *pPattern, CPDF_Form *pPatternForm, const CFX_Matrix &mtObj2Device, const FX_RECT &clip_box, bool bStroke)
const CFX_FloatRect & bbox() const
CFX_PTemplate< float > CFX_PointF
uint32_t FX_ARGB
Definition fx_dib.h:36
BlendMode
Definition fx_dib.h:119
@ kNormal
Definition fx_dib.h:120
FXDIB_Format
Definition fx_dib.h:21
pdfium::CheckedNumeric< int32_t > FX_SAFE_INT32
int FXSYS_roundf(float f)
Options & operator=(const Options &rhs)
int Height() const
int32_t bottom
int32_t right
int Width() const
int32_t top
int32_t left