1 // Copyright 2016 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 "core/fpdfapi/render/cpdf_renderstatus.h"
8
9 #include <algorithm>
10 #include <cmath>
11 #include <limits>
12 #include <memory>
13 #include <numeric>
14 #include <set>
15 #include <utility>
16 #include <vector>
17
18 #include "build/build_config.h"
19 #include "constants/transparency.h"
20 #include "core/fpdfapi/font/cpdf_font.h"
21 #include "core/fpdfapi/font/cpdf_type3char.h"
22 #include "core/fpdfapi/font/cpdf_type3font.h"
23 #include "core/fpdfapi/page/cpdf_docpagedata.h"
24 #include "core/fpdfapi/page/cpdf_form.h"
25 #include "core/fpdfapi/page/cpdf_formobject.h"
26 #include "core/fpdfapi/page/cpdf_function.h"
27 #include "core/fpdfapi/page/cpdf_graphicstates.h"
28 #include "core/fpdfapi/page/cpdf_image.h"
29 #include "core/fpdfapi/page/cpdf_imageobject.h"
30 #include "core/fpdfapi/page/cpdf_occontext.h"
31 #include "core/fpdfapi/page/cpdf_page.h"
32 #include "core/fpdfapi/page/cpdf_pageobject.h"
33 #include "core/fpdfapi/page/cpdf_pathobject.h"
34 #include "core/fpdfapi/page/cpdf_shadingobject.h"
35 #include "core/fpdfapi/page/cpdf_shadingpattern.h"
36 #include "core/fpdfapi/page/cpdf_textobject.h"
37 #include "core/fpdfapi/page/cpdf_tilingpattern.h"
38 #include "core/fpdfapi/page/cpdf_transferfunc.h"
39 #include "core/fpdfapi/parser/cpdf_array.h"
40 #include "core/fpdfapi/parser/cpdf_document.h"
41 #include "core/fpdfapi/parser/cpdf_stream.h"
42 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
43 #include "core/fpdfapi/render/cpdf_charposlist.h"
44 #include "core/fpdfapi/render/cpdf_docrenderdata.h"
45 #include "core/fpdfapi/render/cpdf_imagerenderer.h"
46 #include "core/fpdfapi/render/cpdf_pagerendercache.h"
47 #include "core/fpdfapi/render/cpdf_rendercontext.h"
48 #include "core/fpdfapi/render/cpdf_renderoptions.h"
49 #include "core/fpdfapi/render/cpdf_rendershading.h"
50 #include "core/fpdfapi/render/cpdf_scaledrenderbuffer.h"
51 #include "core/fpdfapi/render/cpdf_textrenderer.h"
52 #include "core/fpdfapi/render/cpdf_type3cache.h"
53 #include "core/fxcrt/autorestorer.h"
54 #include "core/fxcrt/fx_safe_types.h"
55 #include "core/fxcrt/fx_system.h"
56 #include "core/fxge/cfx_defaultrenderdevice.h"
57 #include "core/fxge/cfx_glyphbitmap.h"
58 #include "core/fxge/cfx_pathdata.h"
59 #include "core/fxge/dib/cfx_dibitmap.h"
60 #include "core/fxge/fx_font.h"
61 #include "core/fxge/renderdevicedriver_iface.h"
62 #include "core/fxge/text_char_pos.h"
63 #include "core/fxge/text_glyph_pos.h"
64 #include "third_party/base/compiler_specific.h"
65 #include "third_party/base/logging.h"
66 #include "third_party/base/numerics/safe_math.h"
67 #include "third_party/base/ptr_util.h"
68 #include "third_party/base/stl_util.h"
69
70 #ifdef _SKIA_SUPPORT_
71 #include "core/fxge/skia/fx_skia_device.h"
72 #endif
73
74 namespace {
75
76 constexpr int kRenderMaxRecursionDepth = 64;
77 int g_CurrentRecursionDepth = 0;
78
DrawPatternBitmap(CPDF_Document * pDoc,CPDF_PageRenderCache * pCache,CPDF_TilingPattern * pPattern,CPDF_Form * pPatternForm,const CFX_Matrix & mtObject2Device,int width,int height,const CPDF_RenderOptions::Options & draw_options)79 RetainPtr<CFX_DIBitmap> DrawPatternBitmap(
80 CPDF_Document* pDoc,
81 CPDF_PageRenderCache* pCache,
82 CPDF_TilingPattern* pPattern,
83 CPDF_Form* pPatternForm,
84 const CFX_Matrix& mtObject2Device,
85 int width,
86 int height,
87 const CPDF_RenderOptions::Options& draw_options) {
88 auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
89 if (!pBitmap->Create(width, height,
90 pPattern->colored() ? FXDIB_Argb : FXDIB_8bppMask)) {
91 return nullptr;
92 }
93 CFX_DefaultRenderDevice bitmap_device;
94 bitmap_device.Attach(pBitmap, false, nullptr, false);
95 pBitmap->Clear(0);
96 CFX_FloatRect cell_bbox =
97 pPattern->pattern_to_form().TransformRect(pPattern->bbox());
98 cell_bbox = mtObject2Device.TransformRect(cell_bbox);
99 CFX_FloatRect bitmap_rect(0.0f, 0.0f, width, height);
100 CFX_Matrix mtAdjust;
101 mtAdjust.MatchRect(bitmap_rect, cell_bbox);
102
103 CFX_Matrix mtPattern2Bitmap = mtObject2Device * mtAdjust;
104 CPDF_RenderOptions options;
105 if (!pPattern->colored())
106 options.SetColorMode(CPDF_RenderOptions::kAlpha);
107
108 options.GetOptions() = draw_options;
109 options.GetOptions().bForceHalftone = true;
110
111 CPDF_RenderContext context(pDoc, nullptr, pCache);
112 context.AppendLayer(pPatternForm, &mtPattern2Bitmap);
113 context.Render(&bitmap_device, &options, nullptr);
114 #if defined _SKIA_SUPPORT_PATHS_
115 bitmap_device.Flush(true);
116 pBitmap->UnPreMultiply();
117 #endif
118 return pBitmap;
119 }
120
IsAvailableMatrix(const CFX_Matrix & matrix)121 bool IsAvailableMatrix(const CFX_Matrix& matrix) {
122 if (matrix.a == 0 || matrix.d == 0)
123 return matrix.b != 0 && matrix.c != 0;
124
125 if (matrix.b == 0 || matrix.c == 0)
126 return matrix.a != 0 && matrix.d != 0;
127
128 return true;
129 }
130
MissingFillColor(const CPDF_ColorState * pColorState)131 bool MissingFillColor(const CPDF_ColorState* pColorState) {
132 return !pColorState->HasRef() || pColorState->GetFillColor()->IsNull();
133 }
134
MissingStrokeColor(const CPDF_ColorState * pColorState)135 bool MissingStrokeColor(const CPDF_ColorState* pColorState) {
136 return !pColorState->HasRef() || pColorState->GetStrokeColor()->IsNull();
137 }
138
Type3CharMissingFillColor(const CPDF_Type3Char * pChar,const CPDF_ColorState * pColorState)139 bool Type3CharMissingFillColor(const CPDF_Type3Char* pChar,
140 const CPDF_ColorState* pColorState) {
141 return pChar && (!pChar->colored() || MissingFillColor(pColorState));
142 }
143
Type3CharMissingStrokeColor(const CPDF_Type3Char * pChar,const CPDF_ColorState * pColorState)144 bool Type3CharMissingStrokeColor(const CPDF_Type3Char* pChar,
145 const CPDF_ColorState* pColorState) {
146 return pChar && (!pChar->colored() || MissingStrokeColor(pColorState));
147 }
148
149 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
150 class ScopedSkiaDeviceFlush {
151 public:
ScopedSkiaDeviceFlush(CFX_RenderDevice * pDevice)152 explicit ScopedSkiaDeviceFlush(CFX_RenderDevice* pDevice)
153 : m_pDevice(pDevice) {}
154
155 ScopedSkiaDeviceFlush(const ScopedSkiaDeviceFlush&) = delete;
156 ScopedSkiaDeviceFlush& operator=(const ScopedSkiaDeviceFlush&) = delete;
157
~ScopedSkiaDeviceFlush()158 ~ScopedSkiaDeviceFlush() { m_pDevice->Flush(/*release=*/false); }
159
160 private:
161 CFX_RenderDevice* const m_pDevice;
162 };
163 #endif
164
165 } // namespace
166
CPDF_RenderStatus(CPDF_RenderContext * pContext,CFX_RenderDevice * pDevice)167 CPDF_RenderStatus::CPDF_RenderStatus(CPDF_RenderContext* pContext,
168 CFX_RenderDevice* pDevice)
169 : m_pContext(pContext), m_pDevice(pDevice) {}
170
~CPDF_RenderStatus()171 CPDF_RenderStatus::~CPDF_RenderStatus() {}
172
Initialize(const CPDF_RenderStatus * pParentStatus,const CPDF_GraphicStates * pInitialStates)173 void CPDF_RenderStatus::Initialize(const CPDF_RenderStatus* pParentStatus,
174 const CPDF_GraphicStates* pInitialStates) {
175 m_bPrint = m_pDevice->GetDeviceType() != DeviceType::kDisplay;
176 m_pPageResource.Reset(m_pContext->GetPageResources());
177 if (pInitialStates && !m_pType3Char) {
178 m_InitialStates.CopyStates(*pInitialStates);
179 if (pParentStatus) {
180 if (!m_InitialStates.m_ColorState.HasFillColor()) {
181 m_InitialStates.m_ColorState.SetFillColorRef(
182 pParentStatus->m_InitialStates.m_ColorState.GetFillColorRef());
183 *m_InitialStates.m_ColorState.GetMutableFillColor() =
184 *pParentStatus->m_InitialStates.m_ColorState.GetFillColor();
185 }
186 if (!m_InitialStates.m_ColorState.HasStrokeColor()) {
187 m_InitialStates.m_ColorState.SetStrokeColorRef(
188 pParentStatus->m_InitialStates.m_ColorState.GetFillColorRef());
189 *m_InitialStates.m_ColorState.GetMutableStrokeColor() =
190 *pParentStatus->m_InitialStates.m_ColorState.GetStrokeColor();
191 }
192 }
193 } else {
194 m_InitialStates.DefaultStates();
195 }
196 }
197
RenderObjectList(const CPDF_PageObjectHolder * pObjectHolder,const CFX_Matrix & mtObj2Device)198 void CPDF_RenderStatus::RenderObjectList(
199 const CPDF_PageObjectHolder* pObjectHolder,
200 const CFX_Matrix& mtObj2Device) {
201 #if defined _SKIA_SUPPORT_
202 DebugVerifyDeviceIsPreMultiplied();
203 #endif
204 CFX_FloatRect clip_rect = mtObj2Device.GetInverse().TransformRect(
205 CFX_FloatRect(m_pDevice->GetClipBox()));
206 for (const auto& pCurObj : *pObjectHolder) {
207 if (pCurObj.get() == m_pStopObj) {
208 m_bStopped = true;
209 return;
210 }
211 if (!pCurObj)
212 continue;
213
214 if (pCurObj->GetRect().left > clip_rect.right ||
215 pCurObj->GetRect().right < clip_rect.left ||
216 pCurObj->GetRect().bottom > clip_rect.top ||
217 pCurObj->GetRect().top < clip_rect.bottom) {
218 continue;
219 }
220 RenderSingleObject(pCurObj.get(), mtObj2Device);
221 if (m_bStopped)
222 return;
223 }
224 #if defined _SKIA_SUPPORT_
225 DebugVerifyDeviceIsPreMultiplied();
226 #endif
227 }
228
RenderSingleObject(CPDF_PageObject * pObj,const CFX_Matrix & mtObj2Device)229 void CPDF_RenderStatus::RenderSingleObject(CPDF_PageObject* pObj,
230 const CFX_Matrix& mtObj2Device) {
231 #if defined _SKIA_SUPPORT_
232 DebugVerifyDeviceIsPreMultiplied();
233 #endif
234 AutoRestorer<int> restorer(&g_CurrentRecursionDepth);
235 if (++g_CurrentRecursionDepth > kRenderMaxRecursionDepth) {
236 return;
237 }
238 m_pCurObj = pObj;
239 if (m_Options.GetOCContext() &&
240 !m_Options.GetOCContext()->CheckObjectVisible(pObj)) {
241 return;
242 }
243 ProcessClipPath(pObj->m_ClipPath, mtObj2Device);
244 if (ProcessTransparency(pObj, mtObj2Device)) {
245 return;
246 }
247 ProcessObjectNoClip(pObj, mtObj2Device);
248 #if defined _SKIA_SUPPORT_
249 DebugVerifyDeviceIsPreMultiplied();
250 #endif
251 }
252
ContinueSingleObject(CPDF_PageObject * pObj,const CFX_Matrix & mtObj2Device,PauseIndicatorIface * pPause)253 bool CPDF_RenderStatus::ContinueSingleObject(CPDF_PageObject* pObj,
254 const CFX_Matrix& mtObj2Device,
255 PauseIndicatorIface* pPause) {
256 if (m_pImageRenderer) {
257 if (m_pImageRenderer->Continue(pPause))
258 return true;
259
260 if (!m_pImageRenderer->GetResult())
261 DrawObjWithBackground(pObj, mtObj2Device);
262 m_pImageRenderer.reset();
263 return false;
264 }
265
266 m_pCurObj = pObj;
267 if (m_Options.GetOCContext() &&
268 !m_Options.GetOCContext()->CheckObjectVisible(pObj)) {
269 return false;
270 }
271
272 ProcessClipPath(pObj->m_ClipPath, mtObj2Device);
273 if (ProcessTransparency(pObj, mtObj2Device))
274 return false;
275
276 if (!pObj->IsImage()) {
277 ProcessObjectNoClip(pObj, mtObj2Device);
278 return false;
279 }
280
281 m_pImageRenderer = pdfium::MakeUnique<CPDF_ImageRenderer>();
282 if (!m_pImageRenderer->Start(this, pObj->AsImage(), mtObj2Device, false,
283 BlendMode::kNormal)) {
284 if (!m_pImageRenderer->GetResult())
285 DrawObjWithBackground(pObj, mtObj2Device);
286 m_pImageRenderer.reset();
287 return false;
288 }
289 return ContinueSingleObject(pObj, mtObj2Device, pPause);
290 }
291
GetObjectClippedRect(const CPDF_PageObject * pObj,const CFX_Matrix & mtObj2Device) const292 FX_RECT CPDF_RenderStatus::GetObjectClippedRect(
293 const CPDF_PageObject* pObj,
294 const CFX_Matrix& mtObj2Device) const {
295 FX_RECT rect = pObj->GetTransformedBBox(mtObj2Device);
296 rect.Intersect(m_pDevice->GetClipBox());
297 return rect;
298 }
299
ProcessObjectNoClip(CPDF_PageObject * pObj,const CFX_Matrix & mtObj2Device)300 void CPDF_RenderStatus::ProcessObjectNoClip(CPDF_PageObject* pObj,
301 const CFX_Matrix& mtObj2Device) {
302 #if defined _SKIA_SUPPORT_
303 DebugVerifyDeviceIsPreMultiplied();
304 #endif
305 bool bRet = false;
306 switch (pObj->GetType()) {
307 case CPDF_PageObject::TEXT:
308 bRet = ProcessText(pObj->AsText(), mtObj2Device, nullptr);
309 break;
310 case CPDF_PageObject::PATH:
311 bRet = ProcessPath(pObj->AsPath(), mtObj2Device);
312 break;
313 case CPDF_PageObject::IMAGE:
314 bRet = ProcessImage(pObj->AsImage(), mtObj2Device);
315 break;
316 case CPDF_PageObject::SHADING:
317 ProcessShading(pObj->AsShading(), mtObj2Device);
318 return;
319 case CPDF_PageObject::FORM:
320 bRet = ProcessForm(pObj->AsForm(), mtObj2Device);
321 break;
322 }
323 if (!bRet)
324 DrawObjWithBackground(pObj, mtObj2Device);
325 #if defined _SKIA_SUPPORT_
326 DebugVerifyDeviceIsPreMultiplied();
327 #endif
328 }
329
DrawObjWithBlend(CPDF_PageObject * pObj,const CFX_Matrix & mtObj2Device)330 bool CPDF_RenderStatus::DrawObjWithBlend(CPDF_PageObject* pObj,
331 const CFX_Matrix& mtObj2Device) {
332 switch (pObj->GetType()) {
333 case CPDF_PageObject::PATH:
334 return ProcessPath(pObj->AsPath(), mtObj2Device);
335 case CPDF_PageObject::IMAGE:
336 return ProcessImage(pObj->AsImage(), mtObj2Device);
337 case CPDF_PageObject::FORM:
338 return ProcessForm(pObj->AsForm(), mtObj2Device);
339 default:
340 return false;
341 }
342 }
343
DrawObjWithBackground(CPDF_PageObject * pObj,const CFX_Matrix & mtObj2Device)344 void CPDF_RenderStatus::DrawObjWithBackground(CPDF_PageObject* pObj,
345 const CFX_Matrix& mtObj2Device) {
346 FX_RECT rect = GetObjectClippedRect(pObj, mtObj2Device);
347 if (rect.IsEmpty())
348 return;
349
350 int res = 300;
351 if (pObj->IsImage() && m_pDevice->GetDeviceType() == DeviceType::kPrinter)
352 res = 0;
353
354 CPDF_ScaledRenderBuffer buffer;
355 if (!buffer.Initialize(m_pContext.Get(), m_pDevice, rect, pObj, &m_Options,
356 res)) {
357 return;
358 }
359 CFX_Matrix matrix = mtObj2Device * buffer.GetMatrix();
360 const CPDF_Dictionary* pFormResource = nullptr;
361 const CPDF_FormObject* pFormObj = pObj->AsForm();
362 if (pFormObj)
363 pFormResource = pFormObj->form()->GetDict()->GetDictFor("Resources");
364 CPDF_RenderStatus status(m_pContext.Get(), buffer.GetDevice());
365 status.SetOptions(m_Options);
366 status.SetDeviceMatrix(buffer.GetMatrix());
367 status.SetTransparency(m_Transparency);
368 status.SetDropObjects(m_bDropObjects);
369 status.SetFormResource(pFormResource);
370 status.Initialize(nullptr, nullptr);
371 status.RenderSingleObject(pObj, matrix);
372 buffer.OutputToDevice();
373 }
374
ProcessForm(const CPDF_FormObject * pFormObj,const CFX_Matrix & mtObj2Device)375 bool CPDF_RenderStatus::ProcessForm(const CPDF_FormObject* pFormObj,
376 const CFX_Matrix& mtObj2Device) {
377 #if defined _SKIA_SUPPORT_
378 DebugVerifyDeviceIsPreMultiplied();
379 #endif
380 const CPDF_Dictionary* pOC = pFormObj->form()->GetDict()->GetDictFor("OC");
381 if (pOC && m_Options.GetOCContext() &&
382 !m_Options.GetOCContext()->CheckOCGVisible(pOC)) {
383 return true;
384 }
385 CFX_Matrix matrix = pFormObj->form_matrix() * mtObj2Device;
386 const CPDF_Dictionary* pResources =
387 pFormObj->form()->GetDict()->GetDictFor("Resources");
388 CPDF_RenderStatus status(m_pContext.Get(), m_pDevice);
389 status.SetOptions(m_Options);
390 status.SetStopObject(m_pStopObj.Get());
391 status.SetTransparency(m_Transparency);
392 status.SetDropObjects(m_bDropObjects);
393 status.SetFormResource(pResources);
394 status.Initialize(this, pFormObj);
395 status.m_curBlend = m_curBlend;
396 {
397 CFX_RenderDevice::StateRestorer restorer(m_pDevice);
398 status.RenderObjectList(pFormObj->form(), matrix);
399 m_bStopped = status.m_bStopped;
400 }
401 #if defined _SKIA_SUPPORT_
402 DebugVerifyDeviceIsPreMultiplied();
403 #endif
404 return true;
405 }
406
ProcessPath(CPDF_PathObject * pPathObj,const CFX_Matrix & mtObj2Device)407 bool CPDF_RenderStatus::ProcessPath(CPDF_PathObject* pPathObj,
408 const CFX_Matrix& mtObj2Device) {
409 int FillType = pPathObj->filltype();
410 bool bStroke = pPathObj->stroke();
411 ProcessPathPattern(pPathObj, mtObj2Device, &FillType, &bStroke);
412 if (FillType == 0 && !bStroke)
413 return true;
414
415 uint32_t fill_argb = FillType ? GetFillArgb(pPathObj) : 0;
416 uint32_t stroke_argb = bStroke ? GetStrokeArgb(pPathObj) : 0;
417 CFX_Matrix path_matrix = pPathObj->matrix() * mtObj2Device;
418 if (!IsAvailableMatrix(path_matrix))
419 return true;
420
421 if (FillType && m_Options.GetOptions().bRectAA)
422 FillType |= FXFILL_RECT_AA;
423 if (m_Options.GetOptions().bFillFullcover)
424 FillType |= FXFILL_FULLCOVER;
425 if (m_Options.GetOptions().bNoPathSmooth)
426 FillType |= FXFILL_NOPATHSMOOTH;
427 if (bStroke)
428 FillType |= FX_FILL_STROKE;
429
430 const CPDF_PageObject* pPageObj =
431 static_cast<const CPDF_PageObject*>(pPathObj);
432 if (pPageObj->m_GeneralState.GetStrokeAdjust())
433 FillType |= FX_STROKE_ADJUST;
434 if (m_pType3Char)
435 FillType |= FX_FILL_TEXT_MODE;
436
437 CFX_GraphState graphState = pPathObj->m_GraphState;
438 if (m_Options.GetOptions().bThinLine)
439 graphState.SetLineWidth(0);
440 return m_pDevice->DrawPathWithBlend(
441 pPathObj->path().GetObject(), &path_matrix, graphState.GetObject(),
442 fill_argb, stroke_argb, FillType, m_curBlend);
443 }
444
GetTransferFunc(const CPDF_Object * pObj) const445 RetainPtr<CPDF_TransferFunc> CPDF_RenderStatus::GetTransferFunc(
446 const CPDF_Object* pObj) const {
447 ASSERT(pObj);
448 auto* pDocCache = CPDF_DocRenderData::FromDocument(m_pContext->GetDocument());
449 return pDocCache ? pDocCache->GetTransferFunc(pObj) : nullptr;
450 }
451
GetFillArgbInternal(CPDF_PageObject * pObj,bool bType3) const452 FX_ARGB CPDF_RenderStatus::GetFillArgbInternal(CPDF_PageObject* pObj,
453 bool bType3) const {
454 const CPDF_ColorState* pColorState = &pObj->m_ColorState;
455 if (!bType3 && Type3CharMissingFillColor(m_pType3Char.Get(), pColorState))
456 return m_T3FillColor;
457
458 if (MissingFillColor(pColorState))
459 pColorState = &m_InitialStates.m_ColorState;
460
461 FX_COLORREF colorref = pColorState->GetFillColorRef();
462 if (colorref == 0xFFFFFFFF)
463 return 0;
464
465 int32_t alpha =
466 static_cast<int32_t>((pObj->m_GeneralState.GetFillAlpha() * 255));
467 if (pObj->m_GeneralState.GetTR()) {
468 if (!pObj->m_GeneralState.GetTransferFunc()) {
469 pObj->m_GeneralState.SetTransferFunc(
470 GetTransferFunc(pObj->m_GeneralState.GetTR()));
471 }
472 if (pObj->m_GeneralState.GetTransferFunc()) {
473 colorref =
474 pObj->m_GeneralState.GetTransferFunc()->TranslateColor(colorref);
475 }
476 }
477 return m_Options.TranslateColor(AlphaAndColorRefToArgb(alpha, colorref));
478 }
479
GetStrokeArgb(CPDF_PageObject * pObj) const480 FX_ARGB CPDF_RenderStatus::GetStrokeArgb(CPDF_PageObject* pObj) const {
481 const CPDF_ColorState* pColorState = &pObj->m_ColorState;
482 if (Type3CharMissingStrokeColor(m_pType3Char.Get(), pColorState))
483 return m_T3FillColor;
484
485 if (MissingStrokeColor(pColorState))
486 pColorState = &m_InitialStates.m_ColorState;
487
488 FX_COLORREF colorref = pColorState->GetStrokeColorRef();
489 if (colorref == 0xFFFFFFFF)
490 return 0;
491
492 int32_t alpha = static_cast<int32_t>(pObj->m_GeneralState.GetStrokeAlpha() *
493 255); // not rounded.
494 if (pObj->m_GeneralState.GetTR()) {
495 if (!pObj->m_GeneralState.GetTransferFunc()) {
496 pObj->m_GeneralState.SetTransferFunc(
497 GetTransferFunc(pObj->m_GeneralState.GetTR()));
498 }
499 if (pObj->m_GeneralState.GetTransferFunc()) {
500 colorref =
501 pObj->m_GeneralState.GetTransferFunc()->TranslateColor(colorref);
502 }
503 }
504 return m_Options.TranslateColor(AlphaAndColorRefToArgb(alpha, colorref));
505 }
506
ProcessClipPath(const CPDF_ClipPath & ClipPath,const CFX_Matrix & mtObj2Device)507 void CPDF_RenderStatus::ProcessClipPath(const CPDF_ClipPath& ClipPath,
508 const CFX_Matrix& mtObj2Device) {
509 if (!ClipPath.HasRef()) {
510 if (m_LastClipPath.HasRef()) {
511 m_pDevice->RestoreState(true);
512 m_LastClipPath.SetNull();
513 }
514 return;
515 }
516 if (m_LastClipPath == ClipPath)
517 return;
518
519 m_LastClipPath = ClipPath;
520 m_pDevice->RestoreState(true);
521 for (size_t i = 0; i < ClipPath.GetPathCount(); ++i) {
522 const CFX_PathData* pPathData = ClipPath.GetPath(i).GetObject();
523 if (!pPathData)
524 continue;
525
526 if (pPathData->GetPoints().empty()) {
527 CFX_PathData EmptyPath;
528 EmptyPath.AppendRect(-1, -1, 0, 0);
529 m_pDevice->SetClip_PathFill(&EmptyPath, nullptr, FXFILL_WINDING);
530 } else {
531 m_pDevice->SetClip_PathFill(pPathData, &mtObj2Device,
532 ClipPath.GetClipType(i));
533 }
534 }
535
536 if (ClipPath.GetTextCount() == 0)
537 return;
538
539 if (m_pDevice->GetDeviceType() == DeviceType::kDisplay &&
540 !(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP)) {
541 return;
542 }
543
544 std::unique_ptr<CFX_PathData> pTextClippingPath;
545 for (size_t i = 0; i < ClipPath.GetTextCount(); ++i) {
546 CPDF_TextObject* pText = ClipPath.GetText(i);
547 if (pText) {
548 if (!pTextClippingPath)
549 pTextClippingPath = pdfium::MakeUnique<CFX_PathData>();
550 ProcessText(pText, mtObj2Device, pTextClippingPath.get());
551 continue;
552 }
553
554 if (!pTextClippingPath)
555 continue;
556
557 int fill_mode = FXFILL_WINDING;
558 if (m_Options.GetOptions().bNoTextSmooth)
559 fill_mode |= FXFILL_NOPATHSMOOTH;
560 m_pDevice->SetClip_PathFill(pTextClippingPath.get(), nullptr, fill_mode);
561 pTextClippingPath.reset();
562 }
563 }
564
ClipPattern(const CPDF_PageObject * pPageObj,const CFX_Matrix & mtObj2Device,bool bStroke)565 bool CPDF_RenderStatus::ClipPattern(const CPDF_PageObject* pPageObj,
566 const CFX_Matrix& mtObj2Device,
567 bool bStroke) {
568 if (pPageObj->IsPath())
569 return SelectClipPath(pPageObj->AsPath(), mtObj2Device, bStroke);
570 if (pPageObj->IsImage()) {
571 m_pDevice->SetClip_Rect(pPageObj->GetTransformedBBox(mtObj2Device));
572 return true;
573 }
574 return false;
575 }
576
SelectClipPath(const CPDF_PathObject * pPathObj,const CFX_Matrix & mtObj2Device,bool bStroke)577 bool CPDF_RenderStatus::SelectClipPath(const CPDF_PathObject* pPathObj,
578 const CFX_Matrix& mtObj2Device,
579 bool bStroke) {
580 CFX_Matrix path_matrix = pPathObj->matrix() * mtObj2Device;
581 if (bStroke) {
582 CFX_GraphState graphState = pPathObj->m_GraphState;
583 if (m_Options.GetOptions().bThinLine)
584 graphState.SetLineWidth(0);
585 return m_pDevice->SetClip_PathStroke(pPathObj->path().GetObject(),
586 &path_matrix, graphState.GetObject());
587 }
588 int fill_mode = pPathObj->filltype();
589 if (m_Options.GetOptions().bNoPathSmooth) {
590 fill_mode |= FXFILL_NOPATHSMOOTH;
591 }
592 return m_pDevice->SetClip_PathFill(pPathObj->path().GetObject(), &path_matrix,
593 fill_mode);
594 }
595
ProcessTransparency(CPDF_PageObject * pPageObj,const CFX_Matrix & mtObj2Device)596 bool CPDF_RenderStatus::ProcessTransparency(CPDF_PageObject* pPageObj,
597 const CFX_Matrix& mtObj2Device) {
598 #if defined _SKIA_SUPPORT_
599 DebugVerifyDeviceIsPreMultiplied();
600 #endif
601 BlendMode blend_type = pPageObj->m_GeneralState.GetBlendType();
602 CPDF_Dictionary* pSMaskDict =
603 ToDictionary(pPageObj->m_GeneralState.GetSoftMask());
604 if (pSMaskDict) {
605 if (pPageObj->IsImage() &&
606 pPageObj->AsImage()->GetImage()->GetDict()->KeyExist("SMask")) {
607 pSMaskDict = nullptr;
608 }
609 }
610 const CPDF_Dictionary* pFormResource = nullptr;
611 float group_alpha = 1.0f;
612 CPDF_Transparency transparency = m_Transparency;
613 bool bGroupTransparent = false;
614 const CPDF_FormObject* pFormObj = pPageObj->AsForm();
615 if (pFormObj) {
616 group_alpha = pFormObj->m_GeneralState.GetFillAlpha();
617 transparency = pFormObj->form()->GetTransparency();
618 bGroupTransparent = transparency.IsIsolated();
619 pFormResource = pFormObj->form()->GetDict()->GetDictFor("Resources");
620 }
621 bool bTextClip =
622 (pPageObj->m_ClipPath.HasRef() &&
623 pPageObj->m_ClipPath.GetTextCount() > 0 &&
624 m_pDevice->GetDeviceType() == DeviceType::kDisplay &&
625 !(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP));
626 if (m_Options.GetOptions().bOverprint && pPageObj->IsImage() &&
627 pPageObj->m_GeneralState.GetFillOP() &&
628 pPageObj->m_GeneralState.GetStrokeOP()) {
629 CPDF_Document* pDocument = nullptr;
630 CPDF_Page* pPage = nullptr;
631 if (m_pContext->GetPageCache()) {
632 pPage = m_pContext->GetPageCache()->GetPage();
633 pDocument = pPage->GetDocument();
634 } else {
635 pDocument = pPageObj->AsImage()->GetImage()->GetDocument();
636 }
637 const CPDF_Dictionary* pPageResources =
638 pPage ? pPage->m_pPageResources.Get() : nullptr;
639 auto* pImageStream = pPageObj->AsImage()->GetImage()->GetStream();
640 const CPDF_Object* pCSObj =
641 pImageStream->GetDict()->GetDirectObjectFor("ColorSpace");
642 RetainPtr<CPDF_ColorSpace> pColorSpace =
643 CPDF_DocPageData::FromDocument(pDocument)->GetColorSpace(
644 pCSObj, pPageResources);
645 if (pColorSpace) {
646 int format = pColorSpace->GetFamily();
647 if (format == PDFCS_DEVICECMYK || format == PDFCS_SEPARATION ||
648 format == PDFCS_DEVICEN) {
649 blend_type = BlendMode::kDarken;
650 }
651 }
652 }
653 if (!pSMaskDict && group_alpha == 1.0f && blend_type == BlendMode::kNormal &&
654 !bTextClip && !bGroupTransparent) {
655 return false;
656 }
657 if (m_bPrint) {
658 bool bRet = false;
659 int rendCaps = m_pDevice->GetRenderCaps();
660 if (!(transparency.IsIsolated() || pSMaskDict || bTextClip) &&
661 (rendCaps & FXRC_BLEND_MODE)) {
662 BlendMode oldBlend = m_curBlend;
663 m_curBlend = blend_type;
664 bRet = DrawObjWithBlend(pPageObj, mtObj2Device);
665 m_curBlend = oldBlend;
666 }
667 if (!bRet) {
668 DrawObjWithBackground(pPageObj, mtObj2Device);
669 }
670 return true;
671 }
672 FX_RECT rect = pPageObj->GetTransformedBBox(mtObj2Device);
673 rect.Intersect(m_pDevice->GetClipBox());
674 if (rect.IsEmpty())
675 return true;
676
677 int width = rect.Width();
678 int height = rect.Height();
679 CFX_DefaultRenderDevice bitmap_device;
680 RetainPtr<CFX_DIBitmap> backdrop;
681 if (!transparency.IsIsolated() &&
682 (m_pDevice->GetRenderCaps() & FXRC_GET_BITS)) {
683 backdrop = pdfium::MakeRetain<CFX_DIBitmap>();
684 if (!m_pDevice->CreateCompatibleBitmap(backdrop, width, height))
685 return true;
686 m_pDevice->GetDIBits(backdrop, rect.left, rect.top);
687 }
688 if (!bitmap_device.Create(width, height, FXDIB_Argb, backdrop))
689 return true;
690
691 RetainPtr<CFX_DIBitmap> bitmap = bitmap_device.GetBitmap();
692 bitmap->Clear(0);
693
694 CFX_Matrix new_matrix = mtObj2Device;
695 new_matrix.Translate(-rect.left, -rect.top);
696
697 RetainPtr<CFX_DIBitmap> pTextMask;
698 if (bTextClip) {
699 pTextMask = pdfium::MakeRetain<CFX_DIBitmap>();
700 if (!pTextMask->Create(width, height, FXDIB_8bppMask))
701 return true;
702
703 pTextMask->Clear(0);
704 CFX_DefaultRenderDevice text_device;
705 text_device.Attach(pTextMask, false, nullptr, false);
706 for (size_t i = 0; i < pPageObj->m_ClipPath.GetTextCount(); ++i) {
707 CPDF_TextObject* textobj = pPageObj->m_ClipPath.GetText(i);
708 if (!textobj)
709 break;
710
711 // TODO(thestig): Should we check the return value here?
712 CPDF_TextRenderer::DrawTextPath(
713 &text_device, textobj->GetCharCodes(), textobj->GetCharPositions(),
714 textobj->m_TextState.GetFont().Get(),
715 textobj->m_TextState.GetFontSize(), textobj->GetTextMatrix(),
716 &new_matrix, textobj->m_GraphState.GetObject(), 0xffffffff, 0,
717 nullptr, 0);
718 }
719 }
720 CPDF_RenderStatus bitmap_render(m_pContext.Get(), &bitmap_device);
721 bitmap_render.SetOptions(m_Options);
722 bitmap_render.SetStopObject(m_pStopObj.Get());
723 bitmap_render.SetStdCS(true);
724 bitmap_render.SetDropObjects(m_bDropObjects);
725 bitmap_render.SetFormResource(pFormResource);
726 bitmap_render.Initialize(nullptr, nullptr);
727 bitmap_render.ProcessObjectNoClip(pPageObj, new_matrix);
728 #if defined _SKIA_SUPPORT_PATHS_
729 bitmap_device.Flush(true);
730 bitmap->UnPreMultiply();
731 #endif
732 m_bStopped = bitmap_render.m_bStopped;
733 if (pSMaskDict) {
734 CFX_Matrix smask_matrix =
735 *pPageObj->m_GeneralState.GetSMaskMatrix() * mtObj2Device;
736 RetainPtr<CFX_DIBBase> pSMaskSource =
737 LoadSMask(pSMaskDict, &rect, &smask_matrix);
738 if (pSMaskSource)
739 bitmap->MultiplyAlpha(pSMaskSource);
740 }
741 if (pTextMask) {
742 bitmap->MultiplyAlpha(pTextMask);
743 pTextMask.Reset();
744 }
745 int32_t blitAlpha = 255;
746 if (group_alpha != 1.0f && transparency.IsGroup()) {
747 blitAlpha = (int32_t)(group_alpha * 255);
748 #ifndef _SKIA_SUPPORT_
749 bitmap->MultiplyAlpha(blitAlpha);
750 blitAlpha = 255;
751 #endif
752 }
753 transparency = m_Transparency;
754 if (pPageObj->IsForm()) {
755 transparency.SetGroup();
756 }
757 CompositeDIBitmap(bitmap, rect.left, rect.top, 0, blitAlpha, blend_type,
758 transparency);
759 #if defined _SKIA_SUPPORT_
760 DebugVerifyDeviceIsPreMultiplied();
761 #endif
762 return true;
763 }
764
GetBackdrop(const CPDF_PageObject * pObj,const FX_RECT & rect,bool bBackAlphaRequired,int * left,int * top)765 RetainPtr<CFX_DIBitmap> CPDF_RenderStatus::GetBackdrop(
766 const CPDF_PageObject* pObj,
767 const FX_RECT& rect,
768 bool bBackAlphaRequired,
769 int* left,
770 int* top) {
771 FX_RECT bbox = rect;
772 bbox.Intersect(m_pDevice->GetClipBox());
773 *left = bbox.left;
774 *top = bbox.top;
775 int width = bbox.Width();
776 int height = bbox.Height();
777 auto pBackdrop = pdfium::MakeRetain<CFX_DIBitmap>();
778 if (bBackAlphaRequired && !m_bDropObjects)
779 pBackdrop->Create(width, height, FXDIB_Argb);
780 else
781 m_pDevice->CreateCompatibleBitmap(pBackdrop, width, height);
782
783 if (!pBackdrop->GetBuffer())
784 return nullptr;
785
786 bool bNeedDraw;
787 if (pBackdrop->HasAlpha())
788 bNeedDraw = !(m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT);
789 else
790 bNeedDraw = !(m_pDevice->GetRenderCaps() & FXRC_GET_BITS);
791
792 if (!bNeedDraw) {
793 m_pDevice->GetDIBits(pBackdrop, *left, *top);
794 return pBackdrop;
795 }
796 CFX_Matrix FinalMatrix = m_DeviceMatrix;
797 FinalMatrix.Translate(-*left, -*top);
798 pBackdrop->Clear(pBackdrop->HasAlpha() ? 0 : 0xffffffff);
799
800 CFX_DefaultRenderDevice device;
801 device.Attach(pBackdrop, false, nullptr, false);
802 m_pContext->Render(&device, pObj, &m_Options, &FinalMatrix);
803 return pBackdrop;
804 }
805
CloneObjStates(const CPDF_GraphicStates * pSrcStates,bool bStroke)806 std::unique_ptr<CPDF_GraphicStates> CPDF_RenderStatus::CloneObjStates(
807 const CPDF_GraphicStates* pSrcStates,
808 bool bStroke) {
809 if (!pSrcStates)
810 return nullptr;
811
812 auto pStates = pdfium::MakeUnique<CPDF_GraphicStates>();
813 pStates->CopyStates(*pSrcStates);
814 const CPDF_Color* pObjColor = bStroke
815 ? pSrcStates->m_ColorState.GetStrokeColor()
816 : pSrcStates->m_ColorState.GetFillColor();
817 if (!pObjColor->IsNull()) {
818 pStates->m_ColorState.SetFillColorRef(
819 bStroke ? pSrcStates->m_ColorState.GetStrokeColorRef()
820 : pSrcStates->m_ColorState.GetFillColorRef());
821 pStates->m_ColorState.SetStrokeColorRef(
822 pStates->m_ColorState.GetFillColorRef());
823 }
824 return pStates;
825 }
826
827 #if defined _SKIA_SUPPORT_
DebugVerifyDeviceIsPreMultiplied() const828 void CPDF_RenderStatus::DebugVerifyDeviceIsPreMultiplied() const {
829 m_pDevice->DebugVerifyBitmapIsPreMultiplied();
830 }
831 #endif
832
ProcessText(CPDF_TextObject * textobj,const CFX_Matrix & mtObj2Device,CFX_PathData * pClippingPath)833 bool CPDF_RenderStatus::ProcessText(CPDF_TextObject* textobj,
834 const CFX_Matrix& mtObj2Device,
835 CFX_PathData* pClippingPath) {
836 if (textobj->GetCharCodes().empty())
837 return true;
838
839 const TextRenderingMode text_render_mode = textobj->m_TextState.GetTextMode();
840 if (text_render_mode == TextRenderingMode::MODE_INVISIBLE)
841 return true;
842
843 RetainPtr<CPDF_Font> pFont = textobj->m_TextState.GetFont();
844 if (pFont->IsType3Font())
845 return ProcessType3Text(textobj, mtObj2Device);
846
847 bool bFill = false;
848 bool bStroke = false;
849 bool bClip = false;
850 if (pClippingPath) {
851 bClip = true;
852 } else {
853 switch (text_render_mode) {
854 case TextRenderingMode::MODE_FILL:
855 case TextRenderingMode::MODE_FILL_CLIP:
856 bFill = true;
857 break;
858 case TextRenderingMode::MODE_STROKE:
859 case TextRenderingMode::MODE_STROKE_CLIP:
860 if (pFont->HasFace())
861 bStroke = true;
862 else
863 bFill = true;
864 break;
865 case TextRenderingMode::MODE_FILL_STROKE:
866 case TextRenderingMode::MODE_FILL_STROKE_CLIP:
867 bFill = true;
868 if (pFont->HasFace())
869 bStroke = true;
870 break;
871 case TextRenderingMode::MODE_INVISIBLE:
872 // Already handled above, but the compiler is not smart enough to
873 // realize it.
874 NOTREACHED();
875 return true;
876 case TextRenderingMode::MODE_CLIP:
877 return true;
878 case TextRenderingMode::MODE_UNKNOWN:
879 NOTREACHED();
880 return false;
881 }
882 }
883 FX_ARGB stroke_argb = 0;
884 FX_ARGB fill_argb = 0;
885 bool bPattern = false;
886 if (bStroke) {
887 if (textobj->m_ColorState.GetStrokeColor()->IsPattern()) {
888 bPattern = true;
889 } else {
890 stroke_argb = GetStrokeArgb(textobj);
891 }
892 }
893 if (bFill) {
894 if (textobj->m_ColorState.GetFillColor()->IsPattern()) {
895 bPattern = true;
896 } else {
897 fill_argb = GetFillArgb(textobj);
898 }
899 }
900 CFX_Matrix text_matrix = textobj->GetTextMatrix();
901 if (!IsAvailableMatrix(text_matrix))
902 return true;
903
904 float font_size = textobj->m_TextState.GetFontSize();
905 if (bPattern) {
906 DrawTextPathWithPattern(textobj, mtObj2Device, pFont.Get(), font_size,
907 &text_matrix, bFill, bStroke);
908 return true;
909 }
910 if (bClip || bStroke) {
911 const CFX_Matrix* pDeviceMatrix = &mtObj2Device;
912 CFX_Matrix device_matrix;
913 if (bStroke) {
914 const float* pCTM = textobj->m_TextState.GetCTM();
915 if (pCTM[0] != 1.0f || pCTM[3] != 1.0f) {
916 CFX_Matrix ctm(pCTM[0], pCTM[1], pCTM[2], pCTM[3], 0, 0);
917 text_matrix *= ctm.GetInverse();
918 device_matrix = ctm * mtObj2Device;
919 pDeviceMatrix = &device_matrix;
920 }
921 }
922 int flag = 0;
923 if (bStroke && bFill) {
924 flag |= FX_FILL_STROKE;
925 flag |= FX_STROKE_TEXT_MODE;
926 }
927 if (textobj->m_GeneralState.GetStrokeAdjust())
928 flag |= FX_STROKE_ADJUST;
929 if (m_Options.GetOptions().bNoTextSmooth)
930 flag |= FXFILL_NOPATHSMOOTH;
931 return CPDF_TextRenderer::DrawTextPath(
932 m_pDevice, textobj->GetCharCodes(), textobj->GetCharPositions(),
933 pFont.Get(), font_size, text_matrix, pDeviceMatrix,
934 textobj->m_GraphState.GetObject(), fill_argb, stroke_argb,
935 pClippingPath, flag);
936 }
937 text_matrix.Concat(mtObj2Device);
938 return CPDF_TextRenderer::DrawNormalText(
939 m_pDevice, textobj->GetCharCodes(), textobj->GetCharPositions(),
940 pFont.Get(), font_size, text_matrix, fill_argb, m_Options);
941 }
942
943 // TODO(npm): Font fallback for type 3 fonts? (Completely separate code!!)
ProcessType3Text(CPDF_TextObject * textobj,const CFX_Matrix & mtObj2Device)944 bool CPDF_RenderStatus::ProcessType3Text(CPDF_TextObject* textobj,
945 const CFX_Matrix& mtObj2Device) {
946 CPDF_Type3Font* pType3Font = textobj->m_TextState.GetFont()->AsType3Font();
947 if (pdfium::ContainsValue(m_Type3FontCache, pType3Font))
948 return true;
949
950 DeviceType device_type = m_pDevice->GetDeviceType();
951 FX_ARGB fill_argb = GetFillArgbForType3(textobj);
952 int fill_alpha = FXARGB_A(fill_argb);
953 if (device_type != DeviceType::kDisplay && fill_alpha < 255)
954 return false;
955
956 CFX_Matrix text_matrix = textobj->GetTextMatrix();
957 CFX_Matrix char_matrix = pType3Font->GetFontMatrix();
958 float font_size = textobj->m_TextState.GetFontSize();
959 char_matrix.Scale(font_size, font_size);
960
961 // Must come before |glyphs|, because |glyphs| points into |refTypeCache|.
962 std::set<RetainPtr<CPDF_Type3Cache>> refTypeCache;
963 std::vector<TextGlyphPos> glyphs;
964 if (device_type == DeviceType::kDisplay)
965 glyphs.resize(textobj->GetCharCodes().size());
966
967 for (size_t iChar = 0; iChar < textobj->GetCharCodes().size(); ++iChar) {
968 uint32_t charcode = textobj->GetCharCodes()[iChar];
969 if (charcode == static_cast<uint32_t>(-1))
970 continue;
971
972 CPDF_Type3Char* pType3Char = pType3Font->LoadChar(charcode);
973 if (!pType3Char)
974 continue;
975
976 CFX_Matrix matrix = char_matrix;
977 matrix.e += iChar > 0 ? textobj->GetCharPositions()[iChar - 1] : 0;
978 matrix.Concat(text_matrix);
979 matrix.Concat(mtObj2Device);
980 if (!pType3Char->LoadBitmapFromSoleImageOfForm()) {
981 if (!glyphs.empty()) {
982 for (size_t i = 0; i < iChar; ++i) {
983 const TextGlyphPos& glyph = glyphs[i];
984 if (!glyph.m_pGlyph)
985 continue;
986
987 Optional<CFX_Point> point = glyph.GetOrigin({0, 0});
988 if (!point.has_value())
989 continue;
990
991 m_pDevice->SetBitMask(glyph.m_pGlyph->GetBitmap(), point->x, point->y,
992 fill_argb);
993 }
994 glyphs.clear();
995 }
996
997 std::unique_ptr<CPDF_GraphicStates> pStates =
998 CloneObjStates(textobj, false);
999 CPDF_RenderOptions options = m_Options;
1000 options.GetOptions().bForceHalftone = true;
1001 options.GetOptions().bRectAA = true;
1002
1003 const auto* pForm = static_cast<const CPDF_Form*>(pType3Char->form());
1004 const CPDF_Dictionary* pFormResource =
1005 pForm->GetDict()->GetDictFor("Resources");
1006
1007 if (fill_alpha == 255) {
1008 CPDF_RenderStatus status(m_pContext.Get(), m_pDevice);
1009 status.SetOptions(options);
1010 status.SetTransparency(pForm->GetTransparency());
1011 status.SetType3Char(pType3Char);
1012 status.SetFillColor(fill_argb);
1013 status.SetDropObjects(m_bDropObjects);
1014 status.SetFormResource(pFormResource);
1015 status.Initialize(this, pStates.get());
1016 status.m_Type3FontCache = m_Type3FontCache;
1017 status.m_Type3FontCache.push_back(pType3Font);
1018
1019 CFX_RenderDevice::StateRestorer restorer(m_pDevice);
1020 status.RenderObjectList(pForm, matrix);
1021 } else {
1022 FX_RECT rect =
1023 matrix.TransformRect(pForm->CalcBoundingBox()).GetOuterRect();
1024 if (!rect.Valid())
1025 continue;
1026
1027 CFX_DefaultRenderDevice bitmap_device;
1028 if (!bitmap_device.Create(rect.Width(), rect.Height(), FXDIB_Argb,
1029 nullptr)) {
1030 return true;
1031 }
1032 bitmap_device.GetBitmap()->Clear(0);
1033 CPDF_RenderStatus status(m_pContext.Get(), &bitmap_device);
1034 status.SetOptions(options);
1035 status.SetTransparency(pForm->GetTransparency());
1036 status.SetType3Char(pType3Char);
1037 status.SetFillColor(fill_argb);
1038 status.SetDropObjects(m_bDropObjects);
1039 status.SetFormResource(pFormResource);
1040 status.Initialize(this, pStates.get());
1041 status.m_Type3FontCache = m_Type3FontCache;
1042 status.m_Type3FontCache.push_back(pType3Font);
1043 matrix.Translate(-rect.left, -rect.top);
1044 status.RenderObjectList(pForm, matrix);
1045 m_pDevice->SetDIBits(bitmap_device.GetBitmap(), rect.left, rect.top);
1046 }
1047 } else if (pType3Char->GetBitmap()) {
1048 if (device_type == DeviceType::kDisplay) {
1049 CPDF_Document* pDoc = pType3Font->GetDocument();
1050 RetainPtr<CPDF_Type3Cache> pCache =
1051 CPDF_DocRenderData::FromDocument(pDoc)->GetCachedType3(pType3Font);
1052
1053 const CFX_GlyphBitmap* pBitmap = pCache->LoadGlyph(charcode, &matrix);
1054 if (!pBitmap)
1055 continue;
1056
1057 refTypeCache.insert(std::move(pCache));
1058
1059 CFX_Point origin(FXSYS_roundf(matrix.e), FXSYS_roundf(matrix.f));
1060 if (glyphs.empty()) {
1061 FX_SAFE_INT32 left = origin.x;
1062 left += pBitmap->left();
1063 if (!left.IsValid())
1064 continue;
1065
1066 FX_SAFE_INT32 top = origin.y;
1067 top -= pBitmap->top();
1068 if (!top.IsValid())
1069 continue;
1070
1071 m_pDevice->SetBitMask(pBitmap->GetBitmap(), left.ValueOrDie(),
1072 top.ValueOrDie(), fill_argb);
1073 } else {
1074 glyphs[iChar].m_pGlyph = pBitmap;
1075 glyphs[iChar].m_Origin = origin;
1076 }
1077 } else {
1078 CFX_Matrix image_matrix = pType3Char->matrix() * matrix;
1079 CPDF_ImageRenderer renderer;
1080 if (renderer.Start(this, pType3Char->GetBitmap(), fill_argb, 255,
1081 image_matrix, FXDIB_ResampleOptions(), false,
1082 BlendMode::kNormal)) {
1083 renderer.Continue(nullptr);
1084 }
1085 if (!renderer.GetResult())
1086 return false;
1087 }
1088 }
1089 }
1090
1091 if (glyphs.empty())
1092 return true;
1093
1094 FX_RECT rect = GetGlyphsBBox(glyphs, 0);
1095 auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
1096 if (!pBitmap->Create(rect.Width(), rect.Height(), FXDIB_8bppMask))
1097 return true;
1098
1099 pBitmap->Clear(0);
1100 for (const TextGlyphPos& glyph : glyphs) {
1101 if (!glyph.m_pGlyph)
1102 continue;
1103
1104 Optional<CFX_Point> point = glyph.GetOrigin({rect.left, rect.top});
1105 if (!point.has_value())
1106 continue;
1107
1108 pBitmap->CompositeMask(
1109 point->x, point->y, glyph.m_pGlyph->GetBitmap()->GetWidth(),
1110 glyph.m_pGlyph->GetBitmap()->GetHeight(), glyph.m_pGlyph->GetBitmap(),
1111 fill_argb, 0, 0, BlendMode::kNormal, nullptr, false);
1112 }
1113 m_pDevice->SetBitMask(pBitmap, rect.left, rect.top, fill_argb);
1114 return true;
1115 }
1116
DrawTextPathWithPattern(const CPDF_TextObject * textobj,const CFX_Matrix & mtObj2Device,CPDF_Font * pFont,float font_size,const CFX_Matrix * pTextMatrix,bool bFill,bool bStroke)1117 void CPDF_RenderStatus::DrawTextPathWithPattern(const CPDF_TextObject* textobj,
1118 const CFX_Matrix& mtObj2Device,
1119 CPDF_Font* pFont,
1120 float font_size,
1121 const CFX_Matrix* pTextMatrix,
1122 bool bFill,
1123 bool bStroke) {
1124 if (!bStroke) {
1125 std::vector<std::unique_ptr<CPDF_TextObject>> pCopy;
1126 pCopy.push_back(std::unique_ptr<CPDF_TextObject>(textobj->Clone()));
1127
1128 CPDF_PathObject path;
1129 path.set_filltype(FXFILL_WINDING);
1130 path.m_ClipPath.CopyClipPath(m_LastClipPath);
1131 path.m_ClipPath.AppendTexts(&pCopy);
1132 path.m_ColorState = textobj->m_ColorState;
1133 path.m_GeneralState = textobj->m_GeneralState;
1134 path.path().AppendFloatRect(textobj->GetRect());
1135 path.SetRect(textobj->GetRect());
1136
1137 AutoRestorer<UnownedPtr<const CPDF_PageObject>> restorer2(&m_pCurObj);
1138 RenderSingleObject(&path, mtObj2Device);
1139 return;
1140 }
1141 const CPDF_CharPosList CharPosList(
1142 textobj->GetCharCodes(), textobj->GetCharPositions(), pFont, font_size);
1143 for (const TextCharPos& charpos : CharPosList.Get()) {
1144 auto* font = charpos.m_FallbackFontPosition == -1
1145 ? pFont->GetFont()
1146 : pFont->GetFontFallback(charpos.m_FallbackFontPosition);
1147 const CFX_PathData* pPath =
1148 font->LoadGlyphPath(charpos.m_GlyphIndex, charpos.m_FontCharWidth);
1149 if (!pPath)
1150 continue;
1151
1152 CPDF_PathObject path;
1153 path.m_GraphState = textobj->m_GraphState;
1154 path.m_ColorState = textobj->m_ColorState;
1155
1156 CFX_Matrix matrix;
1157 if (charpos.m_bGlyphAdjust) {
1158 matrix = CFX_Matrix(charpos.m_AdjustMatrix[0], charpos.m_AdjustMatrix[1],
1159 charpos.m_AdjustMatrix[2], charpos.m_AdjustMatrix[3],
1160 0, 0);
1161 }
1162 matrix.Concat(CFX_Matrix(font_size, 0, 0, font_size, charpos.m_Origin.x,
1163 charpos.m_Origin.y));
1164 path.set_stroke(bStroke);
1165 path.set_filltype(bFill ? FXFILL_WINDING : 0);
1166 path.path().Append(pPath, &matrix);
1167 path.set_matrix(*pTextMatrix);
1168 path.CalcBoundingBox();
1169 ProcessPath(&path, mtObj2Device);
1170 }
1171 }
1172
DrawShadingPattern(CPDF_ShadingPattern * pattern,const CPDF_PageObject * pPageObj,const CFX_Matrix & mtObj2Device,bool bStroke)1173 void CPDF_RenderStatus::DrawShadingPattern(CPDF_ShadingPattern* pattern,
1174 const CPDF_PageObject* pPageObj,
1175 const CFX_Matrix& mtObj2Device,
1176 bool bStroke) {
1177 if (!pattern->Load())
1178 return;
1179
1180 CFX_RenderDevice::StateRestorer restorer(m_pDevice);
1181 if (!ClipPattern(pPageObj, mtObj2Device, bStroke))
1182 return;
1183
1184 FX_RECT rect = GetObjectClippedRect(pPageObj, mtObj2Device);
1185 if (rect.IsEmpty())
1186 return;
1187
1188 CFX_Matrix matrix = pattern->pattern_to_form() * mtObj2Device;
1189 int alpha =
1190 FXSYS_roundf(255 * (bStroke ? pPageObj->m_GeneralState.GetStrokeAlpha()
1191 : pPageObj->m_GeneralState.GetFillAlpha()));
1192 CPDF_RenderShading::Draw(m_pDevice, m_pContext.Get(), m_pCurObj.Get(),
1193 pattern, matrix, rect, alpha, m_Options);
1194 }
1195
ProcessShading(const CPDF_ShadingObject * pShadingObj,const CFX_Matrix & mtObj2Device)1196 void CPDF_RenderStatus::ProcessShading(const CPDF_ShadingObject* pShadingObj,
1197 const CFX_Matrix& mtObj2Device) {
1198 FX_RECT rect = pShadingObj->GetTransformedBBox(mtObj2Device);
1199 FX_RECT clip_box = m_pDevice->GetClipBox();
1200 rect.Intersect(clip_box);
1201 if (rect.IsEmpty())
1202 return;
1203
1204 CFX_Matrix matrix = pShadingObj->matrix() * mtObj2Device;
1205 CPDF_RenderShading::Draw(
1206 m_pDevice, m_pContext.Get(), m_pCurObj.Get(), pShadingObj->pattern(),
1207 matrix, rect,
1208 FXSYS_roundf(255 * pShadingObj->m_GeneralState.GetFillAlpha()),
1209 m_Options);
1210 }
1211
DrawTilingPattern(CPDF_TilingPattern * pPattern,CPDF_PageObject * pPageObj,const CFX_Matrix & mtObj2Device,bool bStroke)1212 void CPDF_RenderStatus::DrawTilingPattern(CPDF_TilingPattern* pPattern,
1213 CPDF_PageObject* pPageObj,
1214 const CFX_Matrix& mtObj2Device,
1215 bool bStroke) {
1216 const std::unique_ptr<CPDF_Form> pPatternForm = pPattern->Load(pPageObj);
1217 if (!pPatternForm)
1218 return;
1219
1220 CFX_RenderDevice::StateRestorer restorer(m_pDevice);
1221 #if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
1222 ScopedSkiaDeviceFlush scoped_skia_device_flush(m_pDevice);
1223 #endif
1224 if (!ClipPattern(pPageObj, mtObj2Device, bStroke))
1225 return;
1226
1227 FX_RECT clip_box = m_pDevice->GetClipBox();
1228 if (clip_box.IsEmpty())
1229 return;
1230
1231 const CFX_Matrix mtPattern2Device =
1232 pPattern->pattern_to_form() * mtObj2Device;
1233
1234 CFX_FloatRect cell_bbox = mtPattern2Device.TransformRect(pPattern->bbox());
1235
1236 float ceil_height = std::ceil(cell_bbox.Height());
1237 float ceil_width = std::ceil(cell_bbox.Width());
1238
1239 // Validate the float will fit into the int when the conversion is done.
1240 if (!pdfium::base::IsValueInRangeForNumericType<int>(ceil_height) ||
1241 !pdfium::base::IsValueInRangeForNumericType<int>(ceil_width)) {
1242 return;
1243 }
1244
1245 int width = static_cast<int>(ceil_width);
1246 int height = static_cast<int>(ceil_height);
1247 if (width <= 0)
1248 width = 1;
1249 if (height <= 0)
1250 height = 1;
1251
1252 CFX_FloatRect clip_box_p =
1253 mtPattern2Device.GetInverse().TransformRect(CFX_FloatRect(clip_box));
1254 int min_col = static_cast<int>(
1255 ceil((clip_box_p.left - pPattern->bbox().right) / pPattern->x_step()));
1256 int max_col = static_cast<int>(
1257 floor((clip_box_p.right - pPattern->bbox().left) / pPattern->x_step()));
1258 int min_row = static_cast<int>(
1259 ceil((clip_box_p.bottom - pPattern->bbox().top) / pPattern->y_step()));
1260 int max_row = static_cast<int>(
1261 floor((clip_box_p.top - pPattern->bbox().bottom) / pPattern->y_step()));
1262
1263 // Make sure we can fit the needed width * height into an int.
1264 if (height > std::numeric_limits<int>::max() / width)
1265 return;
1266
1267 if (width > clip_box.Width() || height > clip_box.Height() ||
1268 width * height > clip_box.Width() * clip_box.Height()) {
1269 std::unique_ptr<CPDF_GraphicStates> pStates;
1270 if (!pPattern->colored())
1271 pStates = CloneObjStates(pPageObj, bStroke);
1272
1273 const CPDF_Dictionary* pFormDict = pPatternForm->GetDict();
1274 const CPDF_Dictionary* pFormResource =
1275 pFormDict ? pFormDict->GetDictFor("Resources") : nullptr;
1276 for (int col = min_col; col <= max_col; col++) {
1277 for (int row = min_row; row <= max_row; row++) {
1278 CFX_PointF original = mtPattern2Device.Transform(
1279 CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step()));
1280 CFX_Matrix matrix = mtObj2Device;
1281 matrix.Translate(original.x - mtPattern2Device.e,
1282 original.y - mtPattern2Device.f);
1283 CFX_RenderDevice::StateRestorer restorer2(m_pDevice);
1284 CPDF_RenderStatus status(m_pContext.Get(), m_pDevice);
1285 status.SetOptions(m_Options);
1286 status.SetTransparency(pPatternForm->GetTransparency());
1287 status.SetFormResource(pFormResource);
1288 status.SetDropObjects(m_bDropObjects);
1289 status.Initialize(this, pStates.get());
1290 status.RenderObjectList(pPatternForm.get(), matrix);
1291 }
1292 }
1293 return;
1294 }
1295
1296 bool bAligned =
1297 pPattern->bbox().left == 0 && pPattern->bbox().bottom == 0 &&
1298 pPattern->bbox().right == pPattern->x_step() &&
1299 pPattern->bbox().top == pPattern->y_step() &&
1300 (mtPattern2Device.IsScaled() || mtPattern2Device.Is90Rotated());
1301 if (bAligned) {
1302 int orig_x = FXSYS_roundf(mtPattern2Device.e);
1303 int orig_y = FXSYS_roundf(mtPattern2Device.f);
1304 min_col = (clip_box.left - orig_x) / width;
1305 if (clip_box.left < orig_x)
1306 min_col--;
1307
1308 max_col = (clip_box.right - orig_x) / width;
1309 if (clip_box.right <= orig_x)
1310 max_col--;
1311
1312 min_row = (clip_box.top - orig_y) / height;
1313 if (clip_box.top < orig_y)
1314 min_row--;
1315
1316 max_row = (clip_box.bottom - orig_y) / height;
1317 if (clip_box.bottom <= orig_y)
1318 max_row--;
1319 }
1320 float left_offset = cell_bbox.left - mtPattern2Device.e;
1321 float top_offset = cell_bbox.bottom - mtPattern2Device.f;
1322 RetainPtr<CFX_DIBitmap> pPatternBitmap;
1323 if (width * height < 16) {
1324 RetainPtr<CFX_DIBitmap> pEnlargedBitmap = DrawPatternBitmap(
1325 m_pContext->GetDocument(), m_pContext->GetPageCache(), pPattern,
1326 pPatternForm.get(), mtObj2Device, 8, 8, m_Options.GetOptions());
1327 pPatternBitmap = pEnlargedBitmap->StretchTo(
1328 width, height, FXDIB_ResampleOptions(), nullptr);
1329 } else {
1330 pPatternBitmap =
1331 DrawPatternBitmap(m_pContext->GetDocument(), m_pContext->GetPageCache(),
1332 pPattern, pPatternForm.get(), mtObj2Device, width,
1333 height, m_Options.GetOptions());
1334 }
1335 if (!pPatternBitmap)
1336 return;
1337
1338 if (m_Options.ColorModeIs(CPDF_RenderOptions::kGray))
1339 pPatternBitmap->ConvertColorScale(0, 0xffffff);
1340
1341 FX_ARGB fill_argb = GetFillArgb(pPageObj);
1342 int clip_width = clip_box.right - clip_box.left;
1343 int clip_height = clip_box.bottom - clip_box.top;
1344 auto pScreen = pdfium::MakeRetain<CFX_DIBitmap>();
1345 if (!pScreen->Create(clip_width, clip_height, FXDIB_Argb))
1346 return;
1347
1348 pScreen->Clear(0);
1349 const uint8_t* const src_buf = pPatternBitmap->GetBuffer();
1350 for (int col = min_col; col <= max_col; col++) {
1351 for (int row = min_row; row <= max_row; row++) {
1352 int start_x;
1353 int start_y;
1354 if (bAligned) {
1355 start_x =
1356 FXSYS_roundf(mtPattern2Device.e) + col * width - clip_box.left;
1357 start_y =
1358 FXSYS_roundf(mtPattern2Device.f) + row * height - clip_box.top;
1359 } else {
1360 CFX_PointF original = mtPattern2Device.Transform(
1361 CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step()));
1362
1363 pdfium::base::CheckedNumeric<int> safeStartX =
1364 FXSYS_roundf(original.x + left_offset);
1365 pdfium::base::CheckedNumeric<int> safeStartY =
1366 FXSYS_roundf(original.y + top_offset);
1367
1368 safeStartX -= clip_box.left;
1369 safeStartY -= clip_box.top;
1370 if (!safeStartX.IsValid() || !safeStartY.IsValid())
1371 return;
1372
1373 start_x = safeStartX.ValueOrDie();
1374 start_y = safeStartY.ValueOrDie();
1375 }
1376 if (width == 1 && height == 1) {
1377 if (start_x < 0 || start_x >= clip_box.Width() || start_y < 0 ||
1378 start_y >= clip_box.Height()) {
1379 continue;
1380 }
1381 uint32_t* dest_buf = reinterpret_cast<uint32_t*>(
1382 pScreen->GetBuffer() + pScreen->GetPitch() * start_y + start_x * 4);
1383 if (pPattern->colored()) {
1384 const auto* src_buf32 = reinterpret_cast<const uint32_t*>(src_buf);
1385 *dest_buf = *src_buf32;
1386 } else {
1387 *dest_buf = (*src_buf << 24) | (fill_argb & 0xffffff);
1388 }
1389 } else {
1390 if (pPattern->colored()) {
1391 pScreen->CompositeBitmap(start_x, start_y, width, height,
1392 pPatternBitmap, 0, 0, BlendMode::kNormal,
1393 nullptr, false);
1394 } else {
1395 pScreen->CompositeMask(start_x, start_y, width, height,
1396 pPatternBitmap, fill_argb, 0, 0,
1397 BlendMode::kNormal, nullptr, false);
1398 }
1399 }
1400 }
1401 }
1402 CompositeDIBitmap(pScreen, clip_box.left, clip_box.top, 0, 255,
1403 BlendMode::kNormal, CPDF_Transparency());
1404 }
1405
DrawPathWithPattern(CPDF_PathObject * pPathObj,const CFX_Matrix & mtObj2Device,const CPDF_Color * pColor,bool bStroke)1406 void CPDF_RenderStatus::DrawPathWithPattern(CPDF_PathObject* pPathObj,
1407 const CFX_Matrix& mtObj2Device,
1408 const CPDF_Color* pColor,
1409 bool bStroke) {
1410 CPDF_Pattern* pattern = pColor->GetPattern();
1411 if (!pattern)
1412 return;
1413
1414 if (CPDF_TilingPattern* pTilingPattern = pattern->AsTilingPattern())
1415 DrawTilingPattern(pTilingPattern, pPathObj, mtObj2Device, bStroke);
1416 else if (CPDF_ShadingPattern* pShadingPattern = pattern->AsShadingPattern())
1417 DrawShadingPattern(pShadingPattern, pPathObj, mtObj2Device, bStroke);
1418 }
1419
ProcessPathPattern(CPDF_PathObject * pPathObj,const CFX_Matrix & mtObj2Device,int * filltype,bool * bStroke)1420 void CPDF_RenderStatus::ProcessPathPattern(CPDF_PathObject* pPathObj,
1421 const CFX_Matrix& mtObj2Device,
1422 int* filltype,
1423 bool* bStroke) {
1424 ASSERT(filltype);
1425 ASSERT(bStroke);
1426
1427 if (*filltype) {
1428 const CPDF_Color& FillColor = *pPathObj->m_ColorState.GetFillColor();
1429 if (FillColor.IsPattern()) {
1430 DrawPathWithPattern(pPathObj, mtObj2Device, &FillColor, false);
1431 *filltype = 0;
1432 }
1433 }
1434 if (*bStroke) {
1435 const CPDF_Color& StrokeColor = *pPathObj->m_ColorState.GetStrokeColor();
1436 if (StrokeColor.IsPattern()) {
1437 DrawPathWithPattern(pPathObj, mtObj2Device, &StrokeColor, true);
1438 *bStroke = false;
1439 }
1440 }
1441 }
1442
ProcessImage(CPDF_ImageObject * pImageObj,const CFX_Matrix & mtObj2Device)1443 bool CPDF_RenderStatus::ProcessImage(CPDF_ImageObject* pImageObj,
1444 const CFX_Matrix& mtObj2Device) {
1445 CPDF_ImageRenderer render;
1446 if (render.Start(this, pImageObj, mtObj2Device, m_bStdCS, m_curBlend))
1447 render.Continue(nullptr);
1448 return render.GetResult();
1449 }
1450
CompositeDIBitmap(const RetainPtr<CFX_DIBitmap> & pDIBitmap,int left,int top,FX_ARGB mask_argb,int bitmap_alpha,BlendMode blend_mode,const CPDF_Transparency & transparency)1451 void CPDF_RenderStatus::CompositeDIBitmap(
1452 const RetainPtr<CFX_DIBitmap>& pDIBitmap,
1453 int left,
1454 int top,
1455 FX_ARGB mask_argb,
1456 int bitmap_alpha,
1457 BlendMode blend_mode,
1458 const CPDF_Transparency& transparency) {
1459 if (!pDIBitmap)
1460 return;
1461
1462 if (blend_mode == BlendMode::kNormal) {
1463 if (!pDIBitmap->IsAlphaMask()) {
1464 if (bitmap_alpha < 255) {
1465 #ifdef _SKIA_SUPPORT_
1466 std::unique_ptr<CFX_ImageRenderer> dummy;
1467 CFX_Matrix m = CFX_RenderDevice::GetFlipMatrix(
1468 pDIBitmap->GetWidth(), pDIBitmap->GetHeight(), left, top);
1469 m_pDevice->StartDIBits(pDIBitmap, bitmap_alpha, 0, m,
1470 FXDIB_ResampleOptions(), &dummy);
1471 return;
1472 #else
1473 pDIBitmap->MultiplyAlpha(bitmap_alpha);
1474 #endif
1475 }
1476 #ifdef _SKIA_SUPPORT_
1477 CFX_SkiaDeviceDriver::PreMultiply(pDIBitmap);
1478 #endif
1479 if (m_pDevice->SetDIBits(pDIBitmap, left, top)) {
1480 return;
1481 }
1482 } else {
1483 uint32_t fill_argb = m_Options.TranslateColor(mask_argb);
1484 if (bitmap_alpha < 255) {
1485 uint8_t* fill_argb8 = reinterpret_cast<uint8_t*>(&fill_argb);
1486 fill_argb8[3] *= bitmap_alpha / 255;
1487 }
1488 if (m_pDevice->SetBitMask(pDIBitmap, left, top, fill_argb)) {
1489 return;
1490 }
1491 }
1492 }
1493 bool bIsolated = transparency.IsIsolated();
1494 bool bBackAlphaRequired =
1495 blend_mode != BlendMode::kNormal && bIsolated && !m_bDropObjects;
1496 bool bGetBackGround =
1497 ((m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT)) ||
1498 (!(m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT) &&
1499 (m_pDevice->GetRenderCaps() & FXRC_GET_BITS) && !bBackAlphaRequired);
1500 if (bGetBackGround) {
1501 if (bIsolated || !transparency.IsGroup()) {
1502 if (!pDIBitmap->IsAlphaMask())
1503 m_pDevice->SetDIBitsWithBlend(pDIBitmap, left, top, blend_mode);
1504 return;
1505 }
1506
1507 FX_RECT rect(left, top, left + pDIBitmap->GetWidth(),
1508 top + pDIBitmap->GetHeight());
1509 rect.Intersect(m_pDevice->GetClipBox());
1510 RetainPtr<CFX_DIBitmap> pClone;
1511 if (m_pDevice->GetBackDrop() && m_pDevice->GetBitmap()) {
1512 pClone = m_pDevice->GetBackDrop()->Clone(&rect);
1513 if (!pClone)
1514 return;
1515
1516 RetainPtr<CFX_DIBitmap> pForeBitmap = m_pDevice->GetBitmap();
1517 pClone->CompositeBitmap(0, 0, pClone->GetWidth(), pClone->GetHeight(),
1518 pForeBitmap, rect.left, rect.top,
1519 BlendMode::kNormal, nullptr, false);
1520 left = std::min(left, 0);
1521 top = std::min(top, 0);
1522 if (pDIBitmap->IsAlphaMask()) {
1523 pClone->CompositeMask(0, 0, pClone->GetWidth(), pClone->GetHeight(),
1524 pDIBitmap, mask_argb, left, top, blend_mode,
1525 nullptr, false);
1526 } else {
1527 pClone->CompositeBitmap(0, 0, pClone->GetWidth(), pClone->GetHeight(),
1528 pDIBitmap, left, top, blend_mode, nullptr,
1529 false);
1530 }
1531 } else {
1532 pClone = pDIBitmap;
1533 }
1534 if (m_pDevice->GetBackDrop()) {
1535 m_pDevice->SetDIBits(pClone, rect.left, rect.top);
1536 } else {
1537 if (!pDIBitmap->IsAlphaMask()) {
1538 m_pDevice->SetDIBitsWithBlend(pDIBitmap, rect.left, rect.top,
1539 blend_mode);
1540 }
1541 }
1542 return;
1543 }
1544 int back_left;
1545 int back_top;
1546 FX_RECT rect(left, top, left + pDIBitmap->GetWidth(),
1547 top + pDIBitmap->GetHeight());
1548 RetainPtr<CFX_DIBitmap> pBackdrop = GetBackdrop(
1549 m_pCurObj.Get(), rect, blend_mode != BlendMode::kNormal && bIsolated,
1550 &back_left, &back_top);
1551 if (!pBackdrop)
1552 return;
1553
1554 if (pDIBitmap->IsAlphaMask()) {
1555 pBackdrop->CompositeMask(left - back_left, top - back_top,
1556 pDIBitmap->GetWidth(), pDIBitmap->GetHeight(),
1557 pDIBitmap, mask_argb, 0, 0, blend_mode, nullptr,
1558 false);
1559 } else {
1560 pBackdrop->CompositeBitmap(left - back_left, top - back_top,
1561 pDIBitmap->GetWidth(), pDIBitmap->GetHeight(),
1562 pDIBitmap, 0, 0, blend_mode, nullptr, false);
1563 }
1564
1565 auto pBackdrop1 = pdfium::MakeRetain<CFX_DIBitmap>();
1566 pBackdrop1->Create(pBackdrop->GetWidth(), pBackdrop->GetHeight(),
1567 FXDIB_Rgb32);
1568 pBackdrop1->Clear((uint32_t)-1);
1569 pBackdrop1->CompositeBitmap(0, 0, pBackdrop->GetWidth(),
1570 pBackdrop->GetHeight(), pBackdrop, 0, 0,
1571 BlendMode::kNormal, nullptr, false);
1572 pBackdrop = std::move(pBackdrop1);
1573 m_pDevice->SetDIBits(pBackdrop, back_left, back_top);
1574 }
1575
LoadSMask(CPDF_Dictionary * pSMaskDict,FX_RECT * pClipRect,const CFX_Matrix * pMatrix)1576 RetainPtr<CFX_DIBitmap> CPDF_RenderStatus::LoadSMask(
1577 CPDF_Dictionary* pSMaskDict,
1578 FX_RECT* pClipRect,
1579 const CFX_Matrix* pMatrix) {
1580 if (!pSMaskDict)
1581 return nullptr;
1582
1583 CPDF_Stream* pGroup = pSMaskDict->GetStreamFor(pdfium::transparency::kG);
1584 if (!pGroup)
1585 return nullptr;
1586
1587 std::unique_ptr<CPDF_Function> pFunc;
1588 const CPDF_Object* pFuncObj =
1589 pSMaskDict->GetDirectObjectFor(pdfium::transparency::kTR);
1590 if (pFuncObj && (pFuncObj->IsDictionary() || pFuncObj->IsStream()))
1591 pFunc = CPDF_Function::Load(pFuncObj);
1592
1593 CFX_Matrix matrix = *pMatrix;
1594 matrix.Translate(-pClipRect->left, -pClipRect->top);
1595
1596 CPDF_Form form(m_pContext->GetDocument(), m_pContext->GetPageResources(),
1597 pGroup);
1598 form.ParseContent();
1599
1600 CFX_DefaultRenderDevice bitmap_device;
1601 bool bLuminosity =
1602 pSMaskDict->GetStringFor(pdfium::transparency::kSoftMaskSubType) !=
1603 pdfium::transparency::kAlpha;
1604 int width = pClipRect->right - pClipRect->left;
1605 int height = pClipRect->bottom - pClipRect->top;
1606 FXDIB_Format format;
1607 #if defined(OS_MACOSX) || defined _SKIA_SUPPORT_ || defined _SKIA_SUPPORT_PATHS_
1608 format = bLuminosity ? FXDIB_Rgb32 : FXDIB_8bppMask;
1609 #else
1610 format = bLuminosity ? FXDIB_Rgb : FXDIB_8bppMask;
1611 #endif
1612 if (!bitmap_device.Create(width, height, format, nullptr))
1613 return nullptr;
1614
1615 RetainPtr<CFX_DIBitmap> bitmap = bitmap_device.GetBitmap();
1616 int nCSFamily = 0;
1617 if (bLuminosity) {
1618 FX_ARGB back_color =
1619 GetBackColor(pSMaskDict, pGroup->GetDict(), &nCSFamily);
1620 bitmap->Clear(back_color);
1621 } else {
1622 bitmap->Clear(0);
1623 }
1624
1625 const CPDF_Dictionary* pFormResource =
1626 form.GetDict()->GetDictFor("Resources");
1627 CPDF_RenderOptions options;
1628 options.SetColorMode(bLuminosity ? CPDF_RenderOptions::kNormal
1629 : CPDF_RenderOptions::kAlpha);
1630 CPDF_RenderStatus status(m_pContext.Get(), &bitmap_device);
1631 status.SetOptions(options);
1632 status.SetGroupFamily(nCSFamily);
1633 status.SetLoadMask(bLuminosity);
1634 status.SetStdCS(true);
1635 status.SetFormResource(pFormResource);
1636 status.SetDropObjects(m_bDropObjects);
1637 status.Initialize(nullptr, nullptr);
1638 status.RenderObjectList(&form, matrix);
1639
1640 auto pMask = pdfium::MakeRetain<CFX_DIBitmap>();
1641 if (!pMask->Create(width, height, FXDIB_8bppMask))
1642 return nullptr;
1643
1644 uint8_t* dest_buf = pMask->GetBuffer();
1645 int dest_pitch = pMask->GetPitch();
1646 uint8_t* src_buf = bitmap->GetBuffer();
1647 int src_pitch = bitmap->GetPitch();
1648 std::vector<uint8_t> transfers(256);
1649 if (pFunc) {
1650 std::vector<float> results(pFunc->CountOutputs());
1651 for (size_t i = 0; i < transfers.size(); ++i) {
1652 float input = i / 255.0f;
1653 int nresult;
1654 pFunc->Call(&input, 1, results.data(), &nresult);
1655 transfers[i] = FXSYS_roundf(results[0] * 255);
1656 }
1657 } else {
1658 // Fill |transfers| with 0, 1, ... N.
1659 std::iota(transfers.begin(), transfers.end(), 0);
1660 }
1661 if (bLuminosity) {
1662 int Bpp = bitmap->GetBPP() / 8;
1663 for (int row = 0; row < height; row++) {
1664 uint8_t* dest_pos = dest_buf + row * dest_pitch;
1665 uint8_t* src_pos = src_buf + row * src_pitch;
1666 for (int col = 0; col < width; col++) {
1667 *dest_pos++ = transfers[FXRGB2GRAY(src_pos[2], src_pos[1], *src_pos)];
1668 src_pos += Bpp;
1669 }
1670 }
1671 } else if (pFunc) {
1672 int size = dest_pitch * height;
1673 for (int i = 0; i < size; i++) {
1674 dest_buf[i] = transfers[src_buf[i]];
1675 }
1676 } else {
1677 memcpy(dest_buf, src_buf, dest_pitch * height);
1678 }
1679 return pMask;
1680 }
1681
GetBackColor(const CPDF_Dictionary * pSMaskDict,const CPDF_Dictionary * pGroupDict,int * pCSFamily)1682 FX_ARGB CPDF_RenderStatus::GetBackColor(const CPDF_Dictionary* pSMaskDict,
1683 const CPDF_Dictionary* pGroupDict,
1684 int* pCSFamily) {
1685 static constexpr FX_ARGB kDefaultColor = ArgbEncode(255, 0, 0, 0);
1686 const CPDF_Array* pBC = pSMaskDict->GetArrayFor(pdfium::transparency::kBC);
1687 if (!pBC)
1688 return kDefaultColor;
1689
1690 const CPDF_Object* pCSObj = nullptr;
1691 const CPDF_Dictionary* pGroup =
1692 pGroupDict ? pGroupDict->GetDictFor("Group") : nullptr;
1693 if (pGroup)
1694 pCSObj = pGroup->GetDirectObjectFor(pdfium::transparency::kCS);
1695 RetainPtr<CPDF_ColorSpace> pCS =
1696 CPDF_DocPageData::FromDocument(m_pContext->GetDocument())
1697 ->GetColorSpace(pCSObj, nullptr);
1698 if (!pCS)
1699 return kDefaultColor;
1700
1701 int family = pCS->GetFamily();
1702 if (family == PDFCS_LAB || pCS->IsSpecial() ||
1703 (family == PDFCS_ICCBASED && !pCS->IsNormal())) {
1704 return kDefaultColor;
1705 }
1706
1707 // Store Color Space Family to use in CPDF_RenderStatus::Initialize().
1708 *pCSFamily = family;
1709
1710 uint32_t comps = std::max(8u, pCS->CountComponents());
1711 size_t count = std::min<size_t>(8, pBC->size());
1712 std::vector<float> floats = ReadArrayElementsToVector(pBC, count);
1713 floats.resize(comps);
1714
1715 float R;
1716 float G;
1717 float B;
1718 pCS->GetRGB(floats.data(), &R, &G, &B);
1719 return ArgbEncode(255, static_cast<int>(R * 255), static_cast<int>(G * 255),
1720 static_cast<int>(B * 255));
1721 }
1722